Minchin.cahttps://blog.minchin.ca/2024-03-18T22:10:00-06:00Genealogy — March 2024 Update2024-03-18T20:54:00-06:002024-03-18T20:54:00-06:00Wm. Minchintag:blog.minchin.ca,2024-03-18:/2024/03/geneology.htmlIt’s been 6 years, but my <a href="https://genealogy.minchin.ca/">genealogy website</a> has
finally been updated!<html><head></head><body><p>It’s been 6 years, but my <a href="https://genealogy.minchin.ca/">genealogy website</a> has
finally been updated!</p>
<p>I’m not sure exactly why I stopped updating the site; I think it was mostly
that the generator (<a href="https://gigatrees.com/">Gigatrees</a>) that I was using had
updated and broke my process (again…). At the time, I figured I had spent so
much time wrangling <em>Gigatrees</em> to work the way I wanted, that I could have
built my own site generator in that time! I did start
<a href="https://github.com/MinchinWeb/exodus">that</a>, but it’s the dreaded re-write,
and died on the vine before it got usable. For now, I’ve pulled out the old
(working) version of <em>Gigatrees</em> out of my archives, and it at least generates
the site for now.</p>
<p>Another downside of the current process is that I run the <em>Gigatrees</em> output
through <em>Pelican</em> to get (final) generated site to match the style of the rest
of my site, but that step alone takes 45 minutes!<sup id="fnref:20240318-1"><a class="footnote-ref" href="#fn:20240318-1">1</a></sup></p>
<p>The immediate push for picking this up again is that we are planning a Minchin
reunion<sup id="fnref:20240318-2"><a class="footnote-ref" href="#fn:20240318-2">2</a></sup> for this summer, and I’d like to have the family history
in shape to be presented and updated at the reunion.</p>
<p>For now, the immediate things I’ve been working has been adding details from
the 1901 and 1911 census. It’s proving a good way to confirm family
relationships and residences.</p>
<p>There are lots of things that I’m not sure are working, and so I should
probably look into making the most recent version of <em>Gigatrees</em> work, or get
serious about my own generator….</p>
<p>One positive it is proves how well a static website (like this) works. After
all, even after all this time left along, the site was still working!</p>
<p>To an update, sooner than 2030!</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:20240318-1">
<p>In fairness, I’m running <em>Pelican</em> on over 10,000 source files,
and the output site is ~1 <span class="caps">GB</span> in size, so it’s not like I expect it to be
instantaneous. But this is perhaps one of the times when “Python is slow”
actually applies…. Maybe while I waiting for one of these runs, I’ll take to
profiling and speeding up <em>Pelican</em>. <a class="footnote-backref" href="#fnref:20240318-1" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:20240318-2">
<p>For the decendants of <a href="https://genealogy.minchin.ca/profiles/i17">Alex
Minchin</a> (1890-1963). <a class="footnote-backref" href="#fnref:20240318-2" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
</ol>
</div></body></html>u2024030418462024-03-04T18:46:00-07:002024-03-04T18:46:00-07:00tag:blog.minchin.ca,2024-03-04:/2024/03/u202403041846.html<html><head></head><body><blockquote>
<p>Socks possess the mysterious power, like cats, of vanishing; unlike cats, they don’t get hungry and come back.</p>
</blockquote>
<p>— Gwern Branwen, on <a href="https://gwern.net/socks">Socks</a></p></body></html>Robot Cars, Meet Snowfall2024-02-22T21:17:00-07:002024-02-22T21:17:00-07:00Wm. Minchintag:blog.minchin.ca,2024-02-22:/2024/02/robot-cars-in-winter.html<blockquote>
<p>I would love to know what the actual blocker to immediate global rollout [of
robot cars] is.</p>
<p>— <a href="https://interconnected.org/home/2024/02/07/cars">Matt Webb</a></p>
</blockquote>
<p>In a word, <strong>snow</strong>.</p>
<p>I can’t help but feel it telling that all of the self-driving car trials take
place in sunny places like <span class="caps">LA</span> and Phoenix. Snow, as I see it, creates two large
obstacles for self-driving cars: the snow can physically obscure the road, and
the snow (and ice) will differentially change the road surface friction.</p>
<html><head></head><body><blockquote>
<p>I would love to know what the actual blocker to immediate global rollout [of
robot cars] is.</p>
<p>— <a href="https://interconnected.org/home/2024/02/07/cars">Matt Webb</a></p>
</blockquote>
<p>In a word, <strong>snow</strong>.</p>
<p>I can’t help but feel it telling that all of the self-driving car trials take
place in sunny places like <span class="caps">LA</span> and Phoenix. Snow, as I see it, creates two large
obstacles for self-driving cars: the snow can physically obscure the road, and
the snow (and ice) will differentially change the road surface friction.</p>
<h2>An Obscured Road Surface</h2>
<p>Even if you’ve never had the pleasure to drive in snowy conditions, it seem is
easy to understand that as snow falls, it will cover the road surface and the
markings on it. If you self-driving car is using a camera to “stay between the
lines”, they all of sudden need a different lane marker. Adding to this, human
drivers don’t always drive in the official “lanes” when the snow falls; in the
extreme, drivers on rural roads will generate only three tire tracks,
effectively using the center one when driving both ways down the road, and then
pulling over to the side (out of the tire tracks altogether) to pass oncoming traffic.</p>
<p>One of my most stressful personal drives I’ve ever done was when the snow
completely covered the road surface and the surrounding fields were
snow-covered and at the same height as the road. Without any previous tracks to
follow, I drove two hours in the dark (with headlights only) keep a 3 inch bump
in the snow off to my right that I knew was the taller grass at the
pavement edge. There was no place to turn off, no shoulder, and I was worried
that if I stopped in the driving lane in the middle of the continuing stop
storm, someone would come up from behind and crash into me before they saw me.
All to say, not an enjoyable drive, and a very hard situration to expect a
self-driving car to perform well in.</p>
<h2>An Icy Road Surface</h2>
<p>Perhaps less obvious if you’ve never driven in winter conditions is how (snow
and) ice change the response of the road surface itself. In perhaps the
simplest to understand situations, a layer of ice on the road (like after
freezing rain) will make the road itself slick, and so more distance is needed
to accelerate, break, and change direction. But more typical is that the
changing patterns of sunlight (and shadow), applied road salt, blowing snow,
and the passing of vehicles will create a patchwork of more and less icy
patches. A few examples:</p>
<ul>
<li>bridges tend to ice over before the rest of the roadway as temperatures drop,
as they lack the ground under them as a heat store.</li>
<li>as vehicles wait at the stop line of traffic light for the light to turn
green, the heat from the engines will melt the ice on the road under them. As
a result, the road is much slicker right before the stopline than elsewhere.
Thus, you can’t count on stopping at the stop line, and instead slow down
much before the intersection, and only do the last bit of breaking at the
stop line.</li>
<li>as vehicles drive down the road, they clear out snow under the tirepath, but
build up the snow in ridges beyond these ruts. Thus, when the vehicle moves
sideways, such as to change lanes, the car will meet more resistance outside
the normal running ruts. In extreme cases, in front wheel drive vehicles and
heavy, slushy snow, if you accelerate as you hit this ridge, the wheel in the
ridge will be mostly held by the extra snow while the “free” wheel in the
driving lane continues to accelerate, and the car will spin!</li>
<li>in extreme cold siturations (i.e. -20°C or colder), as the snow blows
across the highway, it will be crushed under the wheels of the passing
vehicles, causing it to momentarily thaw and then re-freeze. Thus, wind-blown
sections of the highway can busy much icier than sheltered sections. And
patches of roadside trees and brush will result in repeated changes between
windblown and sheltered sections of road, as you drive down a road.</li>
</ul>
<p>What most of these siturations have in common is the best way to deal with them
is to anticipate them, rather than waiting for feedback from the vehicle in the
middle of the situation.</p>
<h2>Conclusions</h2>
<p>Much as it would be cool to see self-driving/robot cars, I think it’s easy to
under-estimate the complexities of winter driving. I would love to “let the car
drive” and let it worry about winter conditions, but I’m not holding my breath.</p></body></html>u2024020410492024-02-04T10:49:00-07:002024-02-04T10:49:00-07:00tag:blog.minchin.ca,2024-02-04:/2024/02/u202402041049.html<html><head></head><body><p>Walking to church, listening to church bells 🔔, add a special delight to this Subday morning!</p></body></html>u2023122612482023-12-26T12:48:00-08:002023-12-26T12:48:00-08:00tag:blog.minchin.ca,2023-12-26:/2023/12/u202312261248.html<html><head></head><body><p>Merry Christmas, from California! <a href='https%3A//blog.minchin.ca/images/2023/PXL_20231226_194840979.jpg"'>https%3A//blog.minchin.ca/images/2023/PXL_20231226_194840979.jpg</a></p></body></html>The Mortal Engines Series, a Book Review2023-12-20T08:43:00-08:002023-12-20T08:43:00-08:00Wm. Minchintag:blog.minchin.ca,2023-12-20:/2023/12/mortal-engines.htmlI recently completed the four books of the Mortal Engines series (<em>Mortal
Engines</em>, <em>Predator’s Gold</em>, <em>Infernal Devices</em>, and <em>A Darkling Plain</em>). I was
drawn in by the premise — that whole cities have gone mobile, something like
giant tanks, and have taken to eating each other — but it was the depth of the
love stories that drew me to finish it.<html><head></head><body><p>I recently completed the four books of the Mortal Engines series (<em>Mortal
Engines</em>, <em>Predator’s Gold</em>, <em>Infernal Devices</em>, and <em>A Darkling Plain</em>). I was
drawn in by the premise — that whole cities have gone mobile, something like
giant tanks, and have taken to eating each other — but it was the depth of the
love stories that drew me to finish it.</p>
<p>Yes, I said “love stories”, but these were so far from the typical fluff that
often connotates. Yes, these are billed as “Young Adult” novels, but the
emotional depth of them is far beyond what that billing might suggest too.</p>
<p>The first novel, <em>Mortal Engines</em>, is probably closest to the “Young Adult”
billing: most of the characters that pull the story along (Tom, Hestor,
Katherine, and Bevis) are ~15 years old and have various shifting love
connections between them. But what ultimately drives the story is the love of a
father (Thaddeus Valentine, nominally, the “bad guy” of the story) for his
daughter, Katherine; his singular desire is to give her a comfromtable life.
Too late, he realises he has become the “bad guy” as he has compromised himself
in his quest and surrounded himself with evil, and is neither strong enough or
brave enough to face that evil straight on. In the final tally, it costs him everything.</p>
<p>The second novel, <em>Predator’s Gold</em>, is perhaps my least favourite of the set,
but due to the other books being as fantastic as they are, rather than any
particular flaw here. The “love story” revolves around a (very “Young Adult”)
potential love triangle (Hestor and Freya, with Tom between them, all ~17 years
old), and the extremes that a flawed Hestor will go to keep the man she loves
as hers alone.</p>
<p>The third and fourth novels, <em>Infernal Devices</em> and <em>A Darkling Plain</em>, are
best read as a pair. The overall story breaks well enough between the two
books, but <em>Infernal Devices</em> ends on a sequence of notes that make Hestor look
flawed beyond redemption, something that she doesn’t (entirely) deserve. The
follow-up, <em>A Darkling Plain</em>, doesn’t completely redeem her, but does reveal
her motivations and left me wondering if I would be her if I had faced the
struggles growing up that she had. The love story here is deep and rich,
exploring the love of a mother and father for their child, enough to drive them
to search the ends of the earth for her, against all odds; it explores the
rebelliousness of youth contrasted the desire for the security and comforts of
home; and it explores how love can fail with time and what it takes to
revitalize it. And although the story focuses on Tom, Hestor, and their
daughter Wren, the arcs of many of the surrounding characters (Fishcake, Anna,
Shrike, Uncle, Cole, and more) go through satisfying explorations the meaning
and implications of our relationships to those around us as well. Indeed, in
the very last chapter, Shike goes from a one-dimensional paper cutout killer to
perhaps the deepest character of the series, exploring what it means to love
someone after they are gone but you are not, and how to go on; it provides a
remarkable and deeply satisfying capstone to the series.</p>
<p>How much of the power of these “love stories” comes from a reflection of my own
life, of my own “love story”? Perhaps much of it. I cannot say how much power
the story would have on one without the experience of life.</p>
<p>The stories are also “tragedies”, perhaps not in the classical Greek sense, but
in the sense that that very, very few of the characters we follow in the story
get the happy ending they want. Not in an overly cruel way, but in the ways
that real life so often can let you down. The balance here, on the whole, was
very finely done; it’s the sort of thing I wish I could one day do.</p>
<p>So the stories are worth a read, and well past your own “young adult” years.</p>
<p>There is the prequel <em>Fever Crumb</em> series that I look forward to reading; I
hope it can live up to these stories.</p>
<p>Happy Reading!</p></body></html>Joseph Conrad on Sailors2023-12-03T09:59:00-07:002023-12-03T09:59:00-07:00Wm. Minchintag:blog.minchin.ca,2023-12-03:/2023/12/heart-of-darkness.htmlI recently re-read <em>The Heart of Darkness</em>, by Joseph Conrad (the last time was
in high school, for English class). I was pleasantly surprised by how short it
was (~90 pages) and delighted by the prose: so much of the “modern” texts I
read are either soulless corporate speak, that say very little, particularly
about the things that actually matter, or flowery advertising speak, that
serves more to misdirect and hide, or bland and flat, by writers who are
exposed to very little soulful writing (or is this a description of my fears of
own writing??). It’s hard to tease apart how much is a result of being written
in over a century ago in 1899, how much is the writer displaying his craft, and
how much of the structure is borrowed from Conrad’s native Polish.<html><head></head><body><p>I recently re-read <em>The Heart of Darkness</em>, by Joseph Conrad (the last time was
in high school, for English class). I was pleasantly surprised by how short it
was (~90 pages) and delighted by the prose: so much of the “modern” texts I
read are either soulless corporate speak, that say very little, particularly
about the things that actually matter, or flowery advertising speak, that
serves more to misdirect and hide, or bland and flat, by writers who are
exposed to very little soulful writing (or is this a description of my fears of
own writing??). It’s hard to tease apart how much is a result of being written
in over a century ago in 1899, how much is the writer displaying his craft, and
how much of the structure is borrowed from Conrad’s native Polish.</p>
<p>One example that I particularly liked is the description of sailors, which is
full of emotion and revels in the contradictions it exposes:</p>
<blockquote>
<p>[M]ost seamen lead, if one may so express it, a sedentary life. Their minds
are of the stay-at-home order, and their home is always with them — the
ship; and so is their country — the sea. One ship is very much like another,
and the sea is always the same. In the immutability of their surroundings,
the foreign shores, the foreign faces, the changing immensity of life, glide
past, veiled not by a sense of mystery but by a slightly disdainful
ignorance; for there is nothing mysterious to a seaman unless it be the sea
itself, which is the mistress of his existence and as inscrutable as destiny.
For the rest, after his hours of work, a casual stroll or a casual spree on
shore suffices to unfold for him the secret of a whole continent, and
generally he finds the secret not worth knowing.</p>
</blockquote>
<p>Here is a group of people that, to those of us who remain on land, we assumed
to live the exact opposite of a “sedentary life[style]”, who would seem to be
drawn to the sea for the wonder of far away places, and yet when they are
confronted with them, turn back to their own “small village” of their ship.</p>
<p>And the contradictions run even deeper: this is offered in direct contrast to
our narrator, Marlow, who has the wanderlust to drag him first to the mainland
Europe (from London) and then into the barely explored depths of Africa, out of
a desire to know the unseen, unexplored parts of the map.</p>
<p>I hope to find more beautifully written stories like this soon!</p></body></html>They Shall Not Grow Old, As We Grow Old2023-11-13T17:23:00-07:002023-11-13T17:23:00-07:00Wm. Minchintag:blog.minchin.ca,2023-11-13:/2023/11/they-shall-not-grow-old.html<em>They shall grow not old, as we that are left grow old:</em><br>
<em>Age shall not weary them, nor the years condemn.</em><br>
<em>At the going down of the sun and in the morning,</em><br>
<em>We will remember them.</em><html><head></head><body><h2>Act of Remembrance</h2>
<p><em>They shall grow not old, as we that are left grow old:</em><br/>
<em>Age shall not weary them, nor the years condemn.</em><br/>
<em>At the going down of the sun and in the morning,</em><br/>
<em>We will remember them.</em></p>
<p><em>We will remember them.</em></p>
<h2>Remembrance Day, 2023</h2>
<p>This year, Remembrance Day fell on a Saturday. The weather was surprisingly
warm for November, and I wanted to attend a Remembrance Day ceremony with the
little ones in person (the last few years, we’ve watched the National ceremony
from Ottawa at home on the couch).</p>
<p>I was disappointed to discover that the indoor ceremony that I’d previously
attended wasn’t happening (and I guess hasn’t since Covid first became a
thing). Of course, we didn’t discover this until we were physically standing
there, wondering where everyone else was, at 10:30am. Per online, it looked
like there was a ceremony to start shortly at the Provincial Legislature, so we
quickly headed down. On the lawn we found the military set up for the gun (or
cannon!) salute, but no ceremony. We did eventually find (in time!) a ceremony
on the upper plaza. I gather, however, that the Legislature’s “official”
ceremony had been the day before. It was good to be there, and I hope the
little ones gained something from it too.</p>
<p>I wonder a lot how the little ones, growing up now, will relate to Remembrance
Day. When I was a kid in school, the World War <span class="caps">II</span> veterns were old but still
alive; they could come talk to us and tell of something of their experiences.
Today, the youngest of them must be approaching 100, and they will all soon be
gone. There have been wars to produce veterns since — Korea, Bosnia,
Afghanistan, and others — but not in the numbers of the World Wars to lend to
the commonality of the experience, both among young men and among the
community. Perhaps this (familiarity with war venterns) will be another thing
lost as the era of a “common popular culture” dissolves.</p>
<p>To me, Remembrance Day is a connection to the past. It is a reminder that
“freedom isn’t free”; that sometimes the peacable need to bring others in line
at the point of a sword. It is a reminder not to take our freedoms for granted.
It is also a community event, and community ritual; there are so few things we
do as a larger community these days.</p>
<h2>Parts of a Remembrance Day Ceremony</h2>
<p>In case Remembrance Day ceremonies fade even more, it seems perhaps wise to
note the pieces that make up a ceremony.</p>
<p>The traditional date to hold the service is November 11th. If held another date
(if, for example, November 11th is a holiday or on the weekend and you’re doing
this at a workplace or scholl), hold it beforehand but as close to the 11th as reasonable.</p>
<p>Those participating are invited to wear a poppy, attached to their left breast
of their clothing. A poppy is traditionally worm from the beginning of November
until the 11th.</p>
<p>If held municipally, the local cenotaph is the most appropriate location. If
needed, a temporary cenotaph may be erected for the service.</p>
<p>Before the service proper, sentries may be plated at the cenotaph. They should
be in place from before the service begins until after the service is complete.</p>
<p>Larger services will include a parade, bringing military units participating to
the service. I’ve always particularly enjoyed hearing the pipe (i.e. bagpipes)
and drums bands.</p>
<p>There are often some opening remarks, recognition of dignitaries, and signing
of the national anthem.</p>
<p>Finally, the heart of the service itself. In my mind, in a pinch, the service
could be trimmed down to just this, and even then the wreaths could be optional.</p>
<ol>
<li>
<p>Last Post</p>
<p>The <em>Last Post</em> is played on a bugle (or a trumpet), and was traditionally
played at the end of the day. Here, is it a symbol of death.</p>
</li>
<li>
<p>A Moment of Silence</p>
<p>2 minutes long, starting at 11 o’clock. Ideally, starting exactly at the
top of the hour.</p>
</li>
<li>
<p>Lamant</p>
<p>The <em>Lament</em> is played on bagpipes.</p>
</li>
<li>
<p>Rouse</p>
<p>The <em>Rouse</em> is played again on a bugle, and was traditionally played to
wake soldiers at the start of the day.</p>
</li>
<li>
<p>The Act of Remembrance</p>
<p>This is the poem at the top of this article. Some of what I’ve read online
suggests that it should be read by a veteran, where possible. The last line
above (<em>We will remember them</em>) is actually a reply of those present to the
end of the poem.</p>
</li>
<li>
<p>Placing of Wreaths</p>
<p>Wreaths are laid at the foot of the memorial. In larger ceremonies, the
wreath is carried by a member of the miliary, who advances with the
wreath-layer to the memorial, who then lays the wreath.</p>
</li>
</ol>
<p>Following the formal wreaths, it has emerged as a folk tradition to lay one’s
poppy at the memorial too.</p>
<p>And thus the service finishes.</p>
<p><em>We will remember them.</em></p>
<p><em>The picture is one I took of the Dieppe, France Canadian cemetary, dating to
World War <span class="caps">II</span>, during a recent (summer 2022) visit.</em></p></body></html>minchin.jrnl v7.1 “Phoenix” released2023-10-04T11:23:00-06:002023-10-04T11:23:00-06:00Wm. Minchintag:blog.minchin.ca,2023-10-04:/2023/10/minchin-jrnl-710-released.htmlToday, the lastest update of <em>minchin.jrnl</em> was released! This release add
custom exporters and importers!<html><head></head><body><p>Today, the lastest update of <em>minchin.jrnl</em> was released! This release add
custom exporters and importers!</p>
<h2>Upgrading</h2>
<p>You can upgrade it today:</p>
<div class="highlight"><pre><span></span><code>pip install minchin.jrnl --upgrade
</code></pre></div>
<h2>Custom Plugins (Importers and Exporters)</h2>
<p>Under the hood, custom plugins rely to Python’s namespaces. Python code (in the
right format and with the right attributes) placed in the
<code>minchin.jrnl.contrib.importer</code> namespace (for importers) and the
<code>minchin.jrnl.contrib.exporter</code> namespace (for exporters) will automatically be
loaded by <em>minchin.jrnl</em>.</p>
<p>You can read more about how to write plugins in the
<a href="http://minchin.ca/minchin.jrnl/reference/plugins/">documentation</a>. I also
wrote a custom exporter to Obsidian flavoured Markdown (for my immediate use!).
You can review the code on
<a href="https://github.com/MinchinWeb/minchin.jrnl.contrib.exporter.obsidian">GitHub</a>
if you want to see a working example.</p>
<p>Custom plugins can override the built-in functionality. Do see what plugins you
have active, you can run <code>jrnl --diagnostic</code>, and you will get output similiar to:</p>
<div class="highlight"><pre><span></span><code><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="s2">"Phoenix"</span>
<span class="n">Python</span><span class="p">:</span><span class="w"> </span><span class="mf">3.11</span><span class="o">.</span><span class="mi">6</span><span class="w"> </span><span class="p">(</span><span class="n">tags</span><span class="o">/</span><span class="n">v3</span><span class="o">.</span><span class="mf">11.6</span><span class="p">:</span><span class="mi">8</span><span class="n">b6ee5b</span><span class="p">,</span><span class="w"> </span><span class="n">Oct</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">2023</span><span class="p">,</span><span class="w"> </span><span class="mi">14</span><span class="p">:</span><span class="mi">57</span><span class="p">:</span><span class="mi">12</span><span class="p">)</span><span class="w"> </span><span class="p">[</span><span class="n">MSC</span><span class="w"> </span><span class="n">v</span><span class="o">.</span><span class="mi">1935</span><span class="w"> </span><span class="mi">64</span><span class="w"> </span><span class="n">bit</span><span class="w"> </span><span class="p">(</span><span class="n">AMD64</span><span class="p">)]</span>
<span class="n">OS</span><span class="p">:</span><span class="w"> </span><span class="n">Windows</span><span class="w"> </span><span class="mi">10</span>
<span class="n">Active</span><span class="w"> </span><span class="n">Plugins</span><span class="p">:</span>
<span class="w"> </span><span class="n">Importers</span><span class="p">:</span>
<span class="w"> </span><span class="n">jrnl</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">importer</span><span class="o">.</span><span class="n">jrnl</span>
<span class="w"> </span><span class="n">Exporters</span><span class="p">:</span>
<span class="w"> </span><span class="n">boxed</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">fancy</span>
<span class="w"> </span><span class="n">dates</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">dates</span>
<span class="w"> </span><span class="n">default</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">pretty</span>
<span class="w"> </span><span class="n">fancy</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">fancy</span>
<span class="w"> </span><span class="n">json</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">json</span>
<span class="w"> </span><span class="n">markdown</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">markdown</span>
<span class="w"> </span><span class="n">md</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">markdown</span>
<span class="w"> </span><span class="n">pelican</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">pelican</span>
<span class="w"> </span><span class="n">pretty</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">pretty</span>
<span class="w"> </span><span class="n">short</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">short</span>
<span class="w"> </span><span class="n">tags</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">tag</span>
<span class="w"> </span><span class="n">text</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">text</span>
<span class="w"> </span><span class="n">txt</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">text</span>
<span class="w"> </span><span class="n">xml</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">xml</span>
<span class="w"> </span><span class="n">yaml</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">minchin</span><span class="o">.</span><span class="n">jrnl</span><span class="o">.</span><span class="n">plugins</span><span class="o">.</span><span class="n">exporter</span><span class="o">.</span><span class="n">yaml</span>
</code></pre></div>
<h2>Short Term Update Plans</h2>
<p>When I first <a href="https://blog.minchin.ca/2023/09/minchin-jrnl-700-released.html">released v7</a>, I
had three major short term objectives: get the project live, lift the Python
version cap, and get the plugin system working. With today’s release, all of
those three have been accomplished!</p>
<p>Several of my medium term goals have also been accomplished (easy to push
releases, drop <em>poetry</em>, replace the Code of Conduct) or are underway
(documentation in Sphinx, posted on <a href="http://minchin.ca/minchin.jrnl/reference/plugins/">my
site</a>).</p>
<p>However, I realize that my notetaking now mostly happens through Obsidian, and
so I’m not sure how much I’ll be using <em>minchin.jrnl</em> going forward. That said,
I’m about to start a new job, and so may end up using it again to take daily
notes; time will tell.</p>
<h2>Changelog</h2>
<p>Version 7.1.0 “Phoenix” was released October 4th, 2023.</p>
<ul>
<li><strong>feature</strong>: merge external plugin support, as per legacy <a href="https://github.com/jrnl-org/jrnl/pull/1216">Pull Request
#1216</a>. Also merges relevant
parts of legacy <a href="https://github.com/jrnl-org/jrnl/pull/1115">Pull Request
#1115</a>; c.f. legacy <a href="https://github.com/jrnl-org/jrnl/pull/1281">Pull Request
#1281</a>.</li>
<li><strong>feature</strong>: allow top-level <code>__version__</code> without use of an <code>__init__.py</code>
file. c.f. legacy <a href="https://github.com/jrnl-org/jrnl/pull/1296">Pull Request
#1296</a>. This had (previously?)
been required for namespace plugins to load.</li>
<li><strong>bug</strong>: allow exporting files to nested directories.</li>
<li><strong>bug</strong>: Work with updated (v4 or later) <code>tzlocal</code> <span class="caps">API</span>. (DayOne classic
journal particular issue.) c.f. legacy <a href="https://github.com/jrnl-org/jrnl/pull/1528">Pull Request
#1528</a>.</li>
</ul>
<h2>Other Links</h2>
<ul>
<li><a href="https://blog.minchin.ca/label/minchinjrnl/">all release posts</a> for <em>minchin.jrnl</em></li>
<li>code, on GitHub at <a href="https://github.com/MinchinWeb/minchin.jrnl">https://github.com/MinchinWeb/minchin.jrnl</a></li>
<li><a href="http://minchin.ca/minchin.jrnl/changelog/">full changelog</a></li>
</ul></body></html>minchin.jrnl v7 “Phoenix” released2023-09-21T13:22:00-06:002023-09-21T13:22:00-06:00Wm. Minchintag:blog.minchin.ca,2023-09-21:/2023/09/minchin-jrnl-700-released.htmlToday, I do something that I should have done 5 years ago, and something that
I’ve been putting off for the last 2 years<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup>: I’m releasing a personal fork
of <em>jrnl</em><sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup>! I’ve given this release the codename <strong>Phoenix</strong>, after the
mythical bird of rebirth, that springs forth renewed from the ashes of the past.<html><head></head><body><p>Today, I do something that I should have done 5 years ago, and something that
I’ve been putting off for the last 2 years<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup>: I’m releasing a personal fork
of <em>jrnl</em><sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup>! I’ve given this release the codename <strong>Phoenix</strong>, after the
mythical bird of rebirth, that springs forth renewed from the ashes of the past.</p>
<p>You can install it today:</p>
<div class="highlight"><pre><span></span><code>pip install minchin.jrnl
</code></pre></div>
<p>And then to run it from the command line:</p>
<div class="highlight"><pre><span></span><code>minchin.jrnl
</code></pre></div>
<p>(or</p>
<div class="highlight"><pre><span></span><code>jrnl
</code></pre></div>
<p>)</p>
<h2>Features Today</h2>
<p>Today, the codebase is that of <em>jrnl</em> v2.6<sup id="fnref:3"><a class="footnote-ref" href="#fn:3">3</a></sup> in a new namespace. In
particular, that gives us a working <em>yaml exporter</em>; now you can build your
Pelican sites again (or maybe only I was doing that…).</p>
<p>The version number (7) was picked to be larger than the current <em>jrnl-org/jrnl</em>
release (currently at 4.0.1). (Plus I thought it would look cool!)</p>
<p>I’ve moved the configuration to a new location on disk<sup id="fnref:4"><a class="footnote-ref" href="#fn:4">4</a></sup>, as to not stomp on
your existing <em>jrnl</em> (i.e. <em>jrnl-org/jrnl</em> or “legacy”) installation.</p>
<p>Limited documentation, to match the current codebase, has been uploaded to my
personal site at <a href="http://minchin.ca/minchin.jrnl/">minchin.ca/minchin.jrnl</a>.
(Although it remains incomplete and very much a work in progress.)</p>
<p>And finally, I extend an invitation to all those current or former users of
<em>jrnl</em> to move here. I welcome your contributions and support. If there are
features missing, please feel free to let me know.</p>
<h2>Short Term Update Plans</h2>
<p>I’ve pushed this release out with very few changes from the base codebase in a
effort to get it live. But I have some updates that I’m planning to do very
shortly. There updates will maintain the <em>Phoenix</em> codename, even if the major
version number increments.</p>
<p>The biggest of these is to launch my much anticipated plugin system. The code
has been already written (for several years now<sup id="fnref:5"><a class="footnote-ref" href="#fn:5">5</a></sup>, actually), can it just
needs to be double checked that it still works as expected.</p>
<p>The other immediate update is to make sure the code works with Python 3.11 (the
current version of Python), which seems to already be the case.</p>
<h2>Medium to Long Term Project Goals, or My Vision</h2>
<p>These are features I’d like to add, although I realize this will take more than
tonight. Also this section lays out my visions for the project and some
anti-features I want to avoid.</p>
<h3>The Plugin System</h3>
<p>The plugin system I think will be huge movement forward to make <em>minchin.jrnl</em>
more useful. In particular, it allows <em>minchin.jrnl</em> to import and export to
and from new formats, including allowing you to write one-off export formats
(which I intend to use personally right away!). Displying the journal entries
on the commandline is also handled by exporters, so you’d be able to tweak that
output as well. I also intend to extend the plugin system to the storage backends.</p>
<p>My hope is that this will futureproof <em>minchin.jrnl</em>, allowing new formats to
quickly and easily be added, retiring deprecated formats to external plugins,
and being able to quickly test and integrate new formats by seemlessly bring
external plugins “in-house”.</p>
<p>In particular, I’m planning to have separate plugins for “true” yaml + markdown
exports and Pelican-formated markdown, to add an export format for Obsidian<sup id="fnref:6"><a class="footnote-ref" href="#fn:6">6</a></sup>
formatted markdown, and add backend format plugins to support <em>jrnl</em> v1 and
whatever format they’re dreaming up for <em>jrnl</em> v4<sup id="fnref:7"><a class="footnote-ref" href="#fn:7">7</a></sup>.</p>
<p>In short, I hope plugins will allow you to make <em>minchin.jrnl</em> more useful,
without me being the bottleneck.</p>
<h3>Plain Text Forever</h3>
<p>One of the things that drew to the original <em>jrnl</em> implementation was that is
was based on plain text, and using plain text to store journal entries. Plain
text has a number of advantages<sup id="fnref:8"><a class="footnote-ref" href="#fn:8">8</a></sup>, but the near universal backwards and
forewards compatibility in high on that list. Yes, plain text has it’s
limitations<sup id="fnref:9"><a class="footnote-ref" href="#fn:9">9</a></sup>, but I think the advantages far outweight the disadvantages,
particularly when it comes to a journal that you might hope will be readable
years or generations from now. Also, plain text just makes it so much easier to
develop <em>minchin.jrnl</em>.</p>
<p><strong>The included, default option for <em>minchin.jrnl</em> will always be plain text.</strong></p>
<p>If you’re looking to upgrade your plain text, you might consider Markdown<sup id="fnref:10"><a class="footnote-ref" href="#fn:10">10</a></sup>
or ReStructured Text (ReST)<sup id="fnref:11"><a class="footnote-ref" href="#fn:11">11</a></sup>.</p>
<p>If you’re looking for either a database backend or more multimedia
functionality (or both), you’re welcome to write something as a backend plugin
for <em>minchin.jrnl</em>; that ability is a featured reason for providing the (to be
added shortly!) plugin system in the first place!</p>
<h3><span class="caps">MIT</span> Licensed</h3>
<p>The original <em>jrnl</em> was initially released under the <a href="https://github.com/jrnl-org/jrnl/blob/v0.2.5/LICENSE"><span class="caps">MIT</span>
license</a>, and that only
changed with the v2.4 release to GPLv3<sup id="fnref:12"><a class="footnote-ref" href="#fn:12">12</a></sup>. My hope and goal is to remove all
<span class="caps">GPL</span>-licensed code and release future versions of <em>minchin.jrnl</em> under the <span class="caps">MIT</span>
license<sup id="fnref:23"><a class="footnote-ref" href="#fn:23">23</a></sup>.</p>
<p>My opposition to the change<sup id="fnref:13"><a class="footnote-ref" href="#fn:13">13</a></sup> was because I’ve come to feel that Open Source
work is both <em>given and received as a gift</em>, and I feel the <span class="caps">GPL</span> license moves
away from that ethos.</p>
<p>I suspect the least fun part of this partial re-write will be getting the
testing system up and running again, as the original library <em>jrnl</em> v1 had been
using has gone many years without updates.</p>
<p>To this end, I’m requesting that any code contributions to the project be
dual-licensed under both <span class="caps">MIT</span> and GPLv3.</p>
<h3>Documentation in Sphinx</h3>
<p>Documentation will eventually be moved over to Sphinx (from the current
MkDocs), a process I’ve began but not finished. Driving this is the expectation
that I’ll have more technical documentation (than is included currently) as I
layout how to work with the plugin <span class="caps">API</span>, and Sphinx makes it easy to keep code
and documentation side by side in the same (code) file.</p>
<p>Furthermore, I want to document how to use <em>minchin.jrnl</em> as a Python <span class="caps">API</span>
generally; this would allow you to interact with your journal from other Python programs.</p>
<h3>Easy to Push Releases</h3>
<p>Knowing my own schedule, I want to be able to sit down for an evening, make
(several) small improvements, and then push out a new release before I go to
bed. To that end, I want to make the streamlined to push out new releases.
Expect lots of small releases. :)</p>
<h3>Drop <em>poetry</em></h3>
<p><em>poetry</em> is a powerful Python project management tool, but is one I’ve never
really liked<sup id="fnref:14"><a class="footnote-ref" href="#fn:14">14</a></sup>. Particular issues include a lack of first class Windows
support<sup id="fnref:15"><a class="footnote-ref" href="#fn:15">15</a></sup> and very conservative upper bounds for dependencies and supported
Python versions. Plus I have refind a system elsewhere using <em>pip-tools</em><sup id="fnref:16"><a class="footnote-ref" href="#fn:16">16</a></sup>
and <em>setup.py</em> to manage these same issues that I find works very well for me.</p>
<p>This has been accomplished with the current release!</p>
<h3>Windows Support</h3>
<p><em>jnrl</em>, to date, has always had decent Windows support. As I personally work on
Windows, Windows will continue to have first class support.</p>
<p>Where this may show is tools beyond Python will need to be readily available on
Windows before they’re used<sup id="fnref:33"><a class="footnote-ref" href="#fn:33">33</a></sup>, and the Windows Terminal is fairly
limited in what in can do, at least compared with some Linux terminals.</p>
<h3>Replace the Current <em>Code of Conduct</em></h3>
<p>I don’t much care for the current <em>Code of Conduct</em><sup id="fnref:17"><a class="footnote-ref" href="#fn:17">17</a></sup>: it seems to be overly
focused on the horrible things people might do to each other, and I’m not sure
I want that to be the introduction people get to the project. I hope to find a
Code of Conduct that focuses more on the positive things I hope people will do
as they interact with each other and the project.</p>
<p>My replaced/current Code of Conduct is
<a href="https://github.com/MinchinWeb/.github/blob/d01ae8608ad7a74d77304db90cdc6da61b269415/.github/CODE_OF_CONDUCT.md">here</a>
(although this may be updated again in the future).</p>
<h3>Open Stewardship Discussion</h3>
<p>If the time comes when someone else is assuming stewardship for the project, I
intend for those discussions to be help publicly<sup id="fnref:18"><a class="footnote-ref" href="#fn:18">18</a></sup>.</p>
<h2>My History with the Project, and Why the Fork</h2>
<p>This section is different: it is much less about the codebase and more focused
on myself and my relationship to it. I warn you it is likely to be somewhat
long and winding.</p>
<h3>My Introduction to <em>jrnl</em></h3>
<p>Many years ago now, I was new in Python. At that time<sup id="fnref:34"><a class="footnote-ref" href="#fn:34">34</a></sup> when I came across a
problem that I thought programming might solve, I first went looking from a
Python program that might solve it.</p>
<p>In looking for a way to manage the regular text notes I was taking at work, I
found <em>jrnl</em>, which I eventually augmented with DayOne (Classic) for in-field
note entry (on a work-supplied iPad) and Pelican for display.</p>
<p><em>Jrnl</em> was more though: it was the object of my first Pull Request<sup id="fnref:35"><a class="footnote-ref" href="#fn:35">35</a></sup>, my
first contribution to Open Source. My meagre help was appreciated and welcomed
warmly, and so I returned often. I found <em>jrnl</em> to be incredibly useful in
learning about Python best practices and code organization; here was a program
that was more than a toy but simple enough (or logically separated) that I
could attempt to understand it, to <em>gork</em> it, as a whole. I contributed in many
places, but particularly around Windows support, DayOne backend support, and
exports to Markdown (to be fed to Pelican).</p>
<p>In short <em>jrnl</em> became part of the code I used everyday.</p>
<h3><em>jrnl</em> Goes Into Hibernation</h3>
<p>I have heard it said that software rewrites are a great way to kill a project.
The reasons for this are multiple, but in short it (typically) saps the energy
to update the legacy version even as bugs pile up, but the new thing can’t go
into everyday use until it is feature-compatible with the legacy version, and
the re-write always takes way more effort than initial estimates.</p>
<p>For reasons now forgotten<sup id="fnref:36"><a class="footnote-ref" href="#fn:36">36</a></sup>, a “v2” semi-rewrite was undertaken. And then it
stalled. And then the project maintainer got a life<sup id="fnref:19"><a class="footnote-ref" href="#fn:19">19</a></sup> and the re-write
stalled even moreso.</p>
<h3>The (Near) Endless Beta of v2, or the First Time I Should Have Forked</h3>
<p>For me, initially, this wasn’t a big deal: I was often running a development
build locally as I tinkered away with <em>jrnl</em>, so I just kept doing that. Also,
I had just started working on my plugin system (for exporters first, but
expecting it could easily be extended to importers and backends).</p>
<p>As the months of inactivity on the part of the maintainer stretched on, and
pull requests grew staler, at some point I should have forked the project and
“officially” released my development version. But I never did, because it
seemed like a <em>scary new thing</em> to do<sup id="fnref:20"><a class="footnote-ref" href="#fn:20">20</a></sup>.</p>
<h3>Invitation to Become a Maintainer</h3>
<p>And then<sup id="fnref:21"><a class="footnote-ref" href="#fn:21">21</a></sup> one day out of the blue, I got an email from the maintainer
asking if I wanted to be a co-maintainer for <em>jrnl</em>! I was delighted, and
promptly said yes. I was given commit access to the repo on GitHub (but, as far
as I knew, no ability to push releases to PyPI), and then…not much happened.
I reached out to the maintainer to suggest some plans, as it still felt like
“his” project, but I never heard much back. And I was too timid to move forward
without at least something from him. And I was busy with the rest of life too.
After a few months, I realized my first plan wasn’t working and started
thinking about how to try again to move the project forward, more on my own. In
front of me was the push to v2.0, and a question of how to integrate my
in-development plug-in system.</p>
<h3>The Coup</h3>
<p>And then on a one another day, again out of the blue, I got an unexpected email
that I no longer had commit access to the <em>jrnl</em> repo. I searched the project
for updates, including the issue tracker and came up with
<a href="https://github.com/jrnl-org/jrnl/issues/591">#591</a> where a transition to new
maintainers was brought up; I don’t know why I wasn’t pinged on it. At the
time<sup id="fnref:22"><a class="footnote-ref" href="#fn:22">22</a></sup>, I said I was happy to see new life in the project and to see it move
forward. But it was unsettling that I’d missed the early discussions.</p>
<p>It also seemed odd to me that the two maintainer that stepped forward hadn’t
seemed to be involved with the project at all before that point.</p>
<p>For a while, things were ok: a “version 2” was released that was very close to
the v2 branch I was using at home, bugs started getting fixed regularly, and
new releases continued to come out. But my plugins never made it into a release.</p>
<h3>Things Fall Apart (aka I Get Frustrated)</h3>
<p>But things under new management didn’t stay rosy.</p>
<p>One of the things they did was completely rewrite the Git history, and thus
change all the commit hashes. This was a small but continueing annoyance (until
I got a new computer), because everytime I would go to push changes to GitHub,
my git client would complain about the “new” (old) tags it was trying to push,
because it couldn’t find a commit with the matching hash.</p>
<p>But my two big annoyances were a re-write of the <span class="caps">YAML</span> exporter and the
continual roadblocks to getting my plugin system merged in.</p>
<p>My plugin system has the longest history, having been started before the change
in stewardship. Many times (after the change in stewardship), I created a pull
request<sup id="fnref:24"><a class="footnote-ref" href="#fn:24">24</a></sup> and the response would be to make various changes or to split it
into smaller pieces; I would make the changes, and the cycle would continue.
But there was never a plan presented that I felt I could successful complete,
nor was I ever told the plugin system was unaligned with the vision they had
for the project. I lost considerable enthusiasm for trying to get the plugins
merged after rewriting the tests for the third time (as the underlying testing
framework was changed).</p>
<p>The <span class="caps">YAML</span> exporter changes are what ultimately left me feeling frozen out of the
project. Without much fanfare, the <span class="caps">YAML</span> exporter was changed, because
someone<sup id="fnref:25"><a class="footnote-ref" href="#fn:25">25</a></sup> felt that the resulting output wasn’t “true” or “pure” <span class="caps">YAML</span>. This
is true, in a sense, because when I had originally written the exporter, it was
designed to output files for Pelican with an assumed Markdown body and <span class="caps">YAML</span>
front matter for metadata. At the request of the (then) maintainer, I called it
the “<span class="caps">YAML</span> exporter”, partly because there was already a “Markdown exporter”. I
didn’t realize it had been broken until I went to upgrade <em>jrnl</em> and render my
Pelican site (which I use to search and read my notes) and it had just stopped
working<sup id="fnref:26"><a class="footnote-ref" href="#fn:26">26</a></sup>. The change wasn’t noted (in a meaningful way) in the release
notes, and the version numbers didn’t give an indication of when this change
had happened<sup id="fnref:30"><a class="footnote-ref" href="#fn:30">30</a></sup>. I eventually figured out where the change had happened,
explained the history of the exporter (that again, I had written years earlier)
and proposed three solutions, each with a corresponding Pull Request: 1) return
the <span class="caps">YAML</span> exporter to it’s previous output<sup id="fnref:27"><a class="footnote-ref" href="#fn:27">27</a></sup>, 2) duplicate the old exporter
under a new name<sup id="fnref:28"><a class="footnote-ref" href="#fn:28">28</a></sup>, or 3) merge my plugin system, which would allow me to
write my own plugin, and solve the problem myself. I was denied on all three,
and told that I ‘didn’t understand the purpose or function of the <span class="caps">YAML</span>
exporter’<sup id="fnref:31"><a class="footnote-ref" href="#fn:31">31</a></sup> (yes, of the plugin I’d written<sup id="fnref:37"><a class="footnote-ref" href="#fn:37">37</a></sup>). The best I got was that
they would reconsider what rose to the level of a “breaking change” when
dealing with versioning<sup id="fnref:32"><a class="footnote-ref" href="#fn:32">32</a></sup>.</p>
<h3>I Walk Away</h3>
<p>The combined experience left me feeling very frustrated: <em>jrnl</em> was broken (to
me) and all my efforts to fix it were forably rebuffed.</p>
<p>When I tried to express my frustrations at my inability to get a “working”
version of <em>jrnl</em>, I was encouraged to take a mantal health break. While I
appreciate the awareness of mental health, stepping away wouldn’t be helpful in
this particlar case because the nothing would happen to fix my broken tooling
(the cause of my frustrations). It seemed like the “right words”(tm) someone
had picked up at a workshop, but the same workshop figured that the “right
words”(tm) would solve everything without requiring a deeper look or deeper changes.</p>
<p>So I took my leave. I’ve been running an outdated version (v2.6) ever since,
and because of the strictness of the Poetry metadata<sup id="fnref:29"><a class="footnote-ref" href="#fn:29">29</a></sup>, I can’t run it on
anything newer than Python 3.9 (even as I’ve upgraded my “daily” Python to 3.11).</p>
<h3>I Return (Sort Of); The Future and My Fears</h3>
<p>So this marks my return. My “mental health break” is over. As I realize I can
only change myself (and not others), I will do the work to fix the deeper
issues (e.g. broken Pelican exports, lack of “modern” Python support) by
managing my own fork. And so that is the work I’ll do.</p>
<p>Looking forward, if I’m the only one that uses my fork, that would be somewhat
disappointing, but also fine. After all, I write software, first and foremost,
for my own usecase and offer it to others as a gift. On the other hand, if a
large part of the community moves here, I worry about being able to shepherd
that community any better than the one I am leaving.</p>
<p>I worry too that either due to there being conflict at all, or that all of my
writings are publically displayed, others will think less of my work or myself
because of the failings they see there. It is indeed very hard to get through a
disagreement like this without failing in some degree.</p>
<p>But it seems better to act, than to suffer in silence.</p>
<h2>A Thank You</h2>
<p>Thank you to all those who have worked to make <em>jrnl</em> as successful as it has
been to date.</p>
<p>If you’ve gotten this far, thank you for reading this all. I hope you will join
me, and I hope your experiences with <em>minchin.jrnl</em> are positive!</p>
<p><em>The header image was generated locally by Standard Diffusion <span class="caps">XL</span>.</em></p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:1">
<p>October 18, 2021 todo item: “fork jrnl” <a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:2">
<p>main landing page at <a href="https://jrnl.sh/en/stable/">jrnl.sh</a>, code at
<a href="https://github.com/jrnl-org/jrnl">jrnl-orl/jrnl on GitHub</a>, and
<a href="https://pypi.org/project/jrnl/">jrnl</a> on PyPI <a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
<li id="fn:3">
<p><a href="https://github.com/jrnl-org/jrnl/tree/v2.6">https://github.com/jrnl-org/jrnl/tree/v2.6</a> <a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text">↩</a></p>
</li>
<li id="fn:4">
<p>this varies by <span class="caps">OS</span>, so run <code>jrnl --list</code> to see where yours is stored. <a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text">↩</a></p>
</li>
<li id="fn:5">
<p><a href="https://github.com/jrnl-org/jrnl/pull/1115">Pull Request #1115</a> <a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 5 in the text">↩</a></p>
</li>
<li id="fn:6">
<p>I’ve started using <a href="https://obsidian.md/">Obsidian</a> to take notes on my
workstation and on my phone, and find it incredible. The backend format
remains Markdown with basically Yaml front matter, but the format is
slightly different from Pelican, and exported file layout differs. <a class="footnote-backref" href="#fnref:6" title="Jump back to footnote 6 in the text">↩</a></p>
</li>
<li id="fn:7">
<p>The initial draft of this post was written before the v4 release, when
there was talk of changing how the journal files were kept. v4 has since
been released, and I’m unclear if that change ever happened, or what
“breaking change” occurred that bumped the version number from 3 to 4
generally. In any case, if they change their format, with the plugin system
it becomes fairly trivial to add a format-specific importer. <a class="footnote-backref" href="#fnref:7" title="Jump back to footnote 7 in the text">↩</a></p>
</li>
<li id="fn:8">
<p>also: tiny file size, easy to put under version control, no proprietary
format or data lock-in, portability across computing platforms, and
generally are human readable <a class="footnote-backref" href="#fnref:8" title="Jump back to footnote 8 in the text">↩</a></p>
</li>
<li id="fn:9">
<p>includes limitations on embedded text formating, storing pictures,
videos, or sound recordings, and lacking standardized internal metadata <a class="footnote-backref" href="#fnref:9" title="Jump back to footnote 9 in the text">↩</a></p>
</li>
<li id="fn:10">
<p>Markdown has several variants and many extensions. If you’re starting
out, I recommend looking at the <a href="https://spec.commonmark.org/0.30/">CommonMark
specification</a>. Note however that
Markdown was originally designed as a shortcut for creating <span class="caps">HTML</span> documents,
and so has no built in features for managing groups of Markdown documents.
It is also deliberately limited in the formating options available, while
officially supporting raw <span class="caps">HTML</span> as a fallback for anything missing. <a class="footnote-backref" href="#fnref:10" title="Jump back to footnote 10 in the text">↩</a></p>
</li>
<li id="fn:11">
<p>ReST is older than Markdown and has always had a <a href="https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html">full
specification</a>.
It was originally designed for the Python language documentation, and so
was designed from the beginning to deal with the interplay between several
text documents. Sadly, it doesn’t seem to have been adopted much outside of
the Python ecosystem. <a class="footnote-backref" href="#fnref:11" title="Jump back to footnote 11 in the text">↩</a></p>
</li>
<li id="fn:12">
<p><a href="https://github.com/jrnl-org/jrnl/blob/v2.3.1/LICENSE">version 2.3.1
license</a> (<span class="caps">MIT</span>);
<a href="https://github.com/jrnl-org/jrnl/blob/v2.4/LICENSE.md">version 2.4
license</a> (GPLv3),
released April 18, 2020. <a class="footnote-backref" href="#fnref:12" title="Jump back to footnote 12 in the text">↩</a></p>
</li>
<li id="fn:13">
<p>as I detailed <a href="https://github.com/jrnl-org/jrnl/pull/918#issuecomment-619683220">at the
time</a>.
But the <a href="https://github.com/jrnl-org/jrnl/pull/918">issue</a> (#918)
announcing the change was merged within 20 minutes of being opened, so I’m
not sure anything I could have said would have changed their minds. <a class="footnote-backref" href="#fnref:13" title="Jump back to footnote 13 in the text">↩</a></p>
</li>
<li id="fn:14">
<p>this can and should be flushed out into a full blog post. But another day. <a class="footnote-backref" href="#fnref:14" title="Jump back to footnote 14 in the text">↩</a></p>
</li>
<li id="fn:15">
<p>and I work on Windows. And I work with Python because Python has had
good Windows support. <a class="footnote-backref" href="#fnref:15" title="Jump back to footnote 15 in the text">↩</a></p>
</li>
<li id="fn:16">
<p><a href="https://pip-tools.readthedocs.io/en/latest/">https://pip-tools.readthedocs.io/en/latest/</a> <a class="footnote-backref" href="#fnref:16" title="Jump back to footnote 16 in the text">↩</a></p>
</li>
<li id="fn:17">
<p><a href="https://github.com/jrnl-org/jrnl/blob/c7b204022a0201a174244163c6ebfa107e8906d2/CODE_OF_CONDUCT.md">jrnl-org/jrnl’s Code of
Conduct</a>:
the <em>Contributor Code of Conduct</em>. <a class="footnote-backref" href="#fnref:17" title="Jump back to footnote 17 in the text">↩</a></p>
</li>
<li id="fn:18">
<p>I imagine in the issue tracker for the project. <a class="footnote-backref" href="#fnref:18" title="Jump back to footnote 18 in the text">↩</a></p>
</li>
<li id="fn:19">
<p>I think he got a job with or founded a startup, and I suspect he
probably moved continents. <a class="footnote-backref" href="#fnref:19" title="Jump back to footnote 19 in the text">↩</a></p>
</li>
<li id="fn:20">
<p>In the intervening time, I ended up releasing personal forks of several
Pelican plugins. The process is no longer new or scary, but still can be a
fair bit of work. And that experience has given me the confidence to go
forward with this fork. <a class="footnote-backref" href="#fnref:20" title="Jump back to footnote 20 in the text">↩</a></p>
</li>
<li id="fn:21">
<p>February 16, 2018 <a class="footnote-backref" href="#fnref:21" title="Jump back to footnote 21 in the text">↩</a></p>
</li>
<li id="fn:22">
<p>July 5, 2019; <a href="https://github.com/jrnl-org/jrnl/issues/591#issuecomment-508821983">my comment, at the
time</a> <a class="footnote-backref" href="#fnref:22" title="Jump back to footnote 22 in the text">↩</a></p>
</li>
<li id="fn:23">
<p>my (pending) codename for these releases is ⚜ <em>Fluer-de-lis</em>. The
reference is to the Lily, a flower that is a symbol of purity and rebirth. <a class="footnote-backref" href="#fnref:23" title="Jump back to footnote 23 in the text">↩</a></p>
</li>
<li id="fn:24">
<p>see <a href="https://github.com/jrnl-org/jrnl/pull/1216">Pull Request #1216</a> and
<a href="https://github.com/jrnl-org/jrnl/issues/1006">Discussion #1006</a> <a class="footnote-backref" href="#fnref:24" title="Jump back to footnote 24 in the text">↩</a></p>
</li>
<li id="fn:25">
<p><a href="https://github.com/jrnl-org/jrnl/issues/1065">Issue #1065</a> <a class="footnote-backref" href="#fnref:25" title="Jump back to footnote 25 in the text">↩</a></p>
</li>
<li id="fn:26">
<p>in particular, Pelican could no longer find the metadata block and
instead rendered the text of each entry as if it was a code block. <a class="footnote-backref" href="#fnref:26" title="Jump back to footnote 26 in the text">↩</a></p>
</li>
<li id="fn:27">
<p>I’m sure I wrote the code to do this, but can’t find the Pull Request at
the moment. Maybe I figured the suggestion wouldn’t go anyway. <a class="footnote-backref" href="#fnref:27" title="Jump back to footnote 27 in the text">↩</a></p>
</li>
<li id="fn:28">
<p><a href="https://github.com/jrnl-org/jrnl/pull/1337/files">Pull Request #1337</a> <a class="footnote-backref" href="#fnref:28" title="Jump back to footnote 28 in the text">↩</a></p>
</li>
<li id="fn:29">
<p><a href="https://github.com/jrnl-org/jrnl/blob/v2.6/pyproject.toml#L25">https://github.com/jrnl-org/jrnl/blob/v2.6/pyproject.toml#L25</a> <a class="footnote-backref" href="#fnref:29" title="Jump back to footnote 29 in the text">↩</a></p>
</li>
<li id="fn:30">
<p>perhaps because I was looking for a <em>breaking change</em> rather than a <em>bug
fix</em>. <a class="footnote-backref" href="#fnref:30" title="Jump back to footnote 30 in the text">↩</a></p>
</li>
<li id="fn:31">
<p><a href="https://github.com/jrnl-org/jrnl/pull/1337#discussion_r711646511">this
comment</a>
and <a href="https://github.com/jrnl-org/jrnl/pull/1337#discussion_r730316274">this
one</a>, in
particular. I can’t find those exact quoted words, but that was the
sentiment I was left with. <a class="footnote-backref" href="#fnref:31" title="Jump back to footnote 31 in the text">↩</a></p>
</li>
<li id="fn:32">
<p><a href="https://github.com/jrnl-org/jrnl/pull/1337#discussion_r711646511">this comment</a> <a class="footnote-backref" href="#fnref:32" title="Jump back to footnote 32 in the text">↩</a></p>
</li>
<li id="fn:33">
<p>So no <em>make</em>. But <a href="https://www.pyinvoke.org/">Invoke</a>, written in
Python, works well for many of <em>make</em>‘s use cases. <a class="footnote-backref" href="#fnref:33" title="Jump back to footnote 33 in the text">↩</a></p>
</li>
<li id="fn:34">
<p>and still today <a class="footnote-backref" href="#fnref:34" title="Jump back to footnote 34 in the text">↩</a></p>
</li>
<li id="fn:35">
<p><a href="https://github.com/jrnl-org/jrnl/pull/110">Pull Request #110</a>, dated
November 27, 2013 <a class="footnote-backref" href="#fnref:35" title="Jump back to footnote 35 in the text">↩</a></p>
</li>
<li id="fn:36">
<p>but likely recorded in the issue tracker <a class="footnote-backref" href="#fnref:36" title="Jump back to footnote 36 in the text">↩</a></p>
</li>
<li id="fn:37">
<p><a href="https://github.com/jrnl-org/jrnl/pull/258">Pull Request #258</a>, opened
July 30, 2014. <a class="footnote-backref" href="#fnref:37" title="Jump back to footnote 37 in the text">↩</a></p>
</li>
</ol>
</div></body></html>u2023092113162023-09-21T13:16:00-06:002023-09-21T13:16:00-06:00tag:blog.minchin.ca,2023-09-21:/2023/09/u202309211316.html<html><head></head><body><p>Winter is coming... the butter in the kitchen cupboards in no longer soft!</p></body></html>AutoLoader Plugin 1.2.1 for Pelican Released2023-08-09T15:49:00-06:002023-08-09T15:49:00-06:00Wm. Minchintag:blog.minchin.ca,2023-08-09:/2023/08/autoloader-121-released.html
<p>This release of <em>Autoloader</em> is to fix a crashing bug when no plugins in the
<code markdown=1>minchin.​pelican.​readers</code>
namespace are loaded.</p>
<p>I also updated a number of other plugins to blacklist v1.2.0 to avoid these crashes.</p>
<html><head></head><body><p><em>AutoLoader</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>AutoLoader</em> is a “meta plugin” in that it doesn’t directly affect your Pelican
site, but rather works to make your other plugins better.</p>
<p>This release of <em>Autoloader</em> is to fix a crashing bug when no plugins in the
<code markdown="1">minchin.pelican.readers</code>
namespace are loaded.</p>
<p>I also updated a number of other plugins to blacklist v1.2.0 to avoid these crashes.</p>
<p>This actually came about because someone emailed me a bug report. So if you
have issues, please feel free to reach out.</p>
<h2>Upgrading</h2>
<p>The simplest way to upgrade (and install) <em>AutoLoader</em> is to use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.autoloader<span class="w"> </span>--upgrade
</code></pre></div>
<h2>Configuration</h2>
<p>There are no configuration changes needed.</p>
<h2>This Release</h2>
<p>This release is v1.2.1, released August 9, 2023.</p>
<ul>
<li><strong>bug</strong>: don’t break if no plugins exist in the namespace you are trying to
load from.</li>
</ul>
<h2>Other Links</h2>
<ul>
<li><a href="https://blog.minchin.ca/label/autoloader/">all release posts</a> for <em>autoloader</em></li>
<li>code, including full configuration directions, on GitHub at
<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.autoloader">MinchinWeb/minchin.pelican.plugins.autoloader</a></li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.autoloader/blob/master/CHANGELOG.rst">full changelog</a></li>
</ul>
<hr/>
<h2>Other Plugins Updated</h2>
<p>These plugins were updated to blacklist the previous version of <em>Autoloader</em>
(v1.2.0), and to add autoloading support if it wasn’t previously in place.</p>
<ul>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.cname">minchin.pelican.plugins.cname</a>
v1.3.4<ul>
<li>release version numbers 1.3.0 through 1.3.3 actually got “eaten” in
making the release process work as expected, and so don’t represent
“proper” releases.</li>
</ul>
</li>
<li><a href="https://github.com/pelican-plugins/nojekyll">minchin.pelican.plugins.nojekyll</a>
v1.2.0<ul>
<li>this plugin is actually hosted by the
<a href="https://github.com/pelican-plugins">Pelican-Plugin</a> organization, but
remains maintained by me for the time being.</li>
</ul>
</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.optimize_images">minchin.pelican.plugins.optimize_images</a> v1.2.2</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.post_stats">minchin.pelican.plugins.post_stats</a> v1.2.0</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.static_comments">minchin.pelican.plugins.static_comments</a> v2.1.2</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.summary">minchin.pelican.plugins.summary</a> v1.2.1</li>
</ul></body></html>u2023071522422023-07-15T22:42:00-06:002023-07-15T22:42:00-06:00tag:blog.minchin.ca,2023-07-15:/2023/07/u202307152242.html<html><head></head><body><p>Seafoam v2.9.1 released with even better support for
<a href="https://blog.minchin.ca/label/microblogging/">microblogging</a>!
<a href="https://pypi.org/project/seafoam/2.9.1/">pypi.org/project/seafoam/2.9.1</a> <a href="https://blog.minchin.ca/label/seafoam/">#Seafoam</a> <a href="https://blog.minchin.ca/label/releases/">#Releases</a> <a href="https://blog.minchin.ca/label/python/">#Python</a></p></body></html>u2023071412302023-07-14T12:30:00-06:002023-07-14T12:30:00-06:00tag:blog.minchin.ca,2023-07-14:/2023/07/u202307141230.html<html><head></head><body><p>Seafoam v2.9.0 released with support for
<a href="https://blog.minchin.ca/2023/07/microblogging-110.html">microblogging</a>!
<a href="https://pypi.org/project/seafoam/2.9.0/">pypi.org/project/seafoam/2.9.0</a> <a href="https://blog.minchin.ca/label/seafoam/">#Seafoam</a> <a href="https://blog.minchin.ca/label/releases/">#Releases</a> <a href="https://blog.minchin.ca/label/python/">#Python</a></p></body></html>u2023071412272023-07-14T12:27:00-06:002023-07-14T12:27:00-06:00tag:blog.minchin.ca,2023-07-14:/2023/07/u202307141227.html<html><head></head><body><p>I'm <a href="https://blog.minchin.ca/2023/07/microblogging-110.html">microblogging</a>
with Pelican! <a href="https://blog.minchin.ca/label/python/">#Python</a> <a href="https://blog.minchin.ca/label/microblogging/">#Microblogging</a></p></body></html>Microblogging 1.1.0 for Pelican Released2023-07-14T11:52:00-06:002023-07-14T11:52:00-06:00Wm. Minchintag:blog.minchin.ca,2023-07-14:/2023/07/microblogging-110.html<p><em>Microblogging</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>Microblogging</em> is a plugin to allow you to have “micro” (or “µ” or
small) posts, similarly (at least outwardly) to Twitter, Mastodon, or Threads.</p>
<html><head></head><body><p><em>Microblogging</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>Microblogging</em> is a plugin to allow you to have “micro” (or “µ” or
small) posts, similarly (at least outwardly) to Twitter, Mastodon, or Threads.</p>
<p>This is by no means a complete replacement for Twitter (or Mastodon), but if
you want to record your thoughts, especially your short ones, on your own site,
this should allow you to do that. (For longer thoughts, at this point you’ve
already got Pelican set up, so don’t be afraid to create “real” blog posts!)</p>
<p>For best results, you probably want to upgrade your theme to support
µposts, but it should work out of the box with your existing theme
(assuming it supports posts generally).
<em><a href="http://blog.minchin.ca/label/seafoam/">Seafoam</a></em> will have a release
presently with support for µposts.</p>
<p>Pull Requests to extend the functionality of the plugin are by all means welcomed!</p>
<h2>Installing</h2>
<p>The simplest way to install (and upgrade) <em>Microblogging</em> is to use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.readers.microblog<span class="w"> </span>--upgrade
</code></pre></div>
<p>If you’re using <em>seafoam</em> as your theme, you will want to upgrade that to at
least v2.9.0.</p>
<h2>Configuration</h2>
<p>The plugin tries to provide sensible defaults, and so no configuration should
be necessary to get it up and running.</p>
<p>Available configuartion options (place these in your <code>pelicanconf.py</code> file) and
their defaults:</p>
<dl>
<dt>MICROBLOG_FOLDER = “micro”</dt>
<dd>
<p>Folder containing your µposts, relative to your content root folder.</p>
</dd>
<dt>MICROBLOG_MAX_LENGTH = 140</dt>
<dd>
<p>How long should your µposts be limited to. Pelican will emit a warning
if you exceed this.</p>
</dd>
<dt>MICROBLOG_SAVE_AS = ARTICLE_SAVE_AS</dt>
<dd>
<p>What to save the µposts’ output file as. Defaults to using the same
file structure as you are using for articles (aka “regular” posts). c.f.
<code>MICROBLOG_URL</code>.</p>
</dd>
<dt>MICROBLOG_SLUG = “u{date:%Y%m%d%H%M}”</dt>
<dd>
<p>The slug that will be used for µposts. Eg. <code>u202307091701</code>. Note
that Pelican expects slugs to be universally unique.</p>
</dd>
<dt>MICROBLOG_URL = ARTICLE_URL</dt>
<dd>
<p>What <span class="caps">URL</span> to post the µposts to. Defaults to using the same <span class="caps">URL</span>
structure as you are using for articles (aka “regular” posts). c.f.
<code>MICROBLOG_SAVE_AS</code>.</p>
</dd>
</dl>
<h2>Sample µposts</h2>
<p>These are samples of what the source for µposts might look like. Place
them in a subfolder called <code>micro</code> of your main content folder.</p>
<p>Basic µpost:</p>
<div class="highlight"><pre><span></span><code><!-- ./content/micro/202307091701.md -->
date: 2023-07-09 17:01
I'm microblogging with Pelican!
https://blog.minchin.ca/label/microblogging-pelican
</code></pre></div>
<hr/>
<p>Or a post with an (featured) image:</p>
<div class="highlight"><pre><span></span><code><!-- ./content/micro/202307112138.md -->
date: 2023-07-11 21:38
image: images/birger-strahl-olI66vtMgNo-unsplash.jpg
Microblog posts can have "feature" images too! (URL of photo should
automatically be added.)
</code></pre></div>
<p>The image path is relative to your <code>content</code> folder. A <span class="caps">URL</span> of the photo is
added to the end of the post as well.</p>
<hr/>
<p>Or with tags (or hashtags):</p>
<div class="highlight"><pre><span></span><code><!-- ./content/micro/202307131456.md -->
date: 2023-07-13 14:56
tags: Python, Pelican, Microblogging
I'm now Microblogging with Pelican!
</code></pre></div>
<p>This will add links at the end of your post to the tags to the tag page for
your (Pelican) site.</p>
<p>For now, the plugin is not smart enough to pull tags out of the body of your post.</p>
<h1>Updating Your Theme</h1>
<p>Although updating your theme is not <em>strictly</em> necessary, you will probably get
more satifactory results if you do. Some pointers as you go about your update:</p>
<ul>
<li>µposts are considered <code>Articles</code> by Pelican, and will be included
in the <code>articles</code> and <code>dates</code> <span class="dquo">“</span>lists” provided by the templating engine.</li>
<li>each µpost is tagged with <code>article.micro = True</code>, so this can be used
as a test for conditional formating.</li>
<li>µposts have a title (as required for Pelican), but you probably want to
hide/not display the title, as titles are of the form <code>u201307180934</code>.</li>
<li>µposts are short; by default they are encouraged to be no more than 140
displayed characters. If you intend to abide by that, there are several
places (like your archive list) where it may make sense to show the whole
µpost (<code>article.content</code>) rather than the title and a link to the full
post page.</li>
</ul>
<h2>Release History</h2>
<p>This release is v1.1.0, released July 13, 2023.</p>
<ul>
<li><strong>feature</strong>: Support hashtags, if given in frontend post metadata. See <a href="https://github.com/MinchinWeb/minchin.pelican.readers.microblog/issues/7">Issue
#7</a>.</li>
</ul>
<p>Version 1.0.0, released July 11, 2023.</p>
<ul>
<li><strong>feature</strong>: Support feature image for microblog posts.</li>
<li><strong>feature</strong>: Supports basic posting and processing of “micro” blog posts.
Posts are assumed to be written in Markdown (so plain text works as well).
Posts must have a <code>date</code> metadata key.</li>
<li>Initial public release!</li>
</ul>
<h2>Known Issues</h2>
<ul>
<li>hashtags can’t be pulled out of post body</li>
<li>µposts seem to mess up the ordering of the <code>articles</code> list passed to
the templating engine. Use <code>dates</code> instead (which is sorted by date)?</li>
<li>text (metadata and body) processing relies on Pelican’s built-in Markdown reader.</li>
</ul>
<h2>Personal Thoughts (on the Plugin, Development, and Microblogging)</h2>
<p>In terms of development, it is perhaps interesting to note that this project
was “Readme Driven Development”<sup id="fnref:RDD"><a class="footnote-ref" href="#fn:RDD">1</a></sup>, where I first wrote the Readme file, and
then developed the feature set to be able to provide what I’d outlined in the
Readme. I was able to do this initial “development work” on my phone in
<em>vim</em><sup id="fnref:phone-vim"><a class="footnote-ref" href="#fn:phone-vim">2</a></sup> (via <em>Termux</em>), but I was glad to move to the larger screen
of my desktop for the coding portion, in particular because I was updating
another plugin (<em>autoloader</em>), updating my Pelican theme (<em>seafoam</em>), and
taking random dives into the Pelican documentation and codebase all as I
developed this plugin. The update to <em>seafoam</em> will be released as soon as I
have this blog post to link to. :)</p>
<p>It has also been very satisfying to go from <em>idea</em> to <em>working</em> in under a week.</p>
<p>I’m still not certain how this will effect my personal blog. Part of me still
longs for the early days of Facebook when I could privately post random
thoughts on my <em>Wall</em> for my friends to see. That died when Facebook played
with the privacy settings so many times that I wasn’t convinced they intended
to keep anything private (plus, most of my social life had moved elsewhere).
I’ve had a Twitter account for years, but never really used it to post
anything. With the current chaos at Twitter and excitement around Mastodon (and
Threads), I considered setting up a Mastodon account, but it seems the sensible
thing would be to stand up my owner server for my own account; this is way less
work! The missing part of this plugin in its current form is the social aspect,
and that will take more thought on how to implement. For now, I have comments
via email, as with any other post on this site.</p>
<p>So will I be comfortable write more microposts? Will they seem less serious,
and so can be <em>off the cuff</em>? Time will see, I guess.</p>
<p>Let me know how it works for you if you install this, and if you don’t, let me
know what keeps you from using this.</p>
<h2>Other Links</h2>
<ul>
<li><a href="https://blog.minchin.ca/label/microblogging-pelican/">all release posts</a> for <em>microblogging</em> (only
this post so far!)</li>
<li>code, including full configuration directions, on GitHub at
<a href="https://github.com/MinchinWeb/minchin.pelican.readers.microblog">MinchinWeb/minchin.pelican.readers.microblog</a></li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.readers.microblog/blob/master/CHANGELOG.rst">full changelog</a></li>
</ul>
<div class="footnote">
<hr/>
<ol>
<li id="fn:RDD">
<p>as opposed to Test Driven Developement (<span class="caps">TDD</span>) or Behavior Driven
Development (<span class="caps">BDD</span>). Tom Preston-Werner actually provides a <a href="https://tom.preston-werner.com/2010/08/23/readme-driven-development.html">good
overview</a>
of the theory of Readme Driven Development. <a class="footnote-backref" href="#fnref:RDD" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:phone-vim">
<p>I’m still looking for a good “graphical” plain text editor for my
Android phone. Suggestions? <a class="footnote-backref" href="#fnref:phone-vim" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
</ol>
</div></body></html>AutoLoader Plugin 1.2.0 for Pelican Released2023-07-12T19:14:00-06:002023-07-12T19:14:00-06:00Wm. Minchintag:blog.minchin.ca,2023-07-12:/2023/07/autoloader-120.html<p><em>AutoLoader</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>AutoLoader</em> is a “meta plugin” in that it doesn’t directly affect your Pelican
site, but rather works to make your other plugins better. Previously, the
plugin was set to autoload plugins in my <code
markdown=1>minchin.​pelican.​plugins</code>
namespace, and thus will extend that to my <code
markdown=1>minchin.​pelican.​readers</code>
namespace.</p>
<html><head></head><body><p><em>AutoLoader</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>AutoLoader</em> is a “meta plugin” in that it doesn’t directly affect your Pelican
site, but rather works to make your other plugins better. Previously, the
plugin was set to autoload plugins in my <code markdown="1">minchin.pelican.plugins</code>
namespace, and thus will extend that to my <code markdown="1">minchin.pelican.readers</code>
namespace.</p>
<p>This change is done in preparation for my (shortly) pending release of a
microblogging plugin for Pelican.</p>
<h2>Upgrading</h2>
<p>The simplest way to upgrade (and install) <em>AutoLoader</em> is to use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.autoloader<span class="w"> </span>--upgrade
</code></pre></div>
<h2>Configuration</h2>
<p>There are no configuration changes needed.</p>
<h2>This Release</h2>
<p>This release is v1.2.0, released July 11, 2023.</p>
<ul>
<li><strong>feature</strong>: include autoloading from additional “private” namespace of
<code>minchin.pelican.readers</code>.</li>
</ul>
<h2>Known Issues</h2>
<ul>
<li>plugins activated by <em>AutoLoader</em> do not show running <code>pelican-plugins</code>.
(<code>pelican-plugins</code> was a script added by Pelican 4.5 to show namespace
plugins currently active). <em>AutoLoader</em> however should be itself listed
(although likely as <code>pelican.plugins.autoloader</code>).</li>
</ul>
<h2>Other Links</h2>
<ul>
<li><a href="https://blog.minchin.ca/label/autoloader/">all release posts</a> for <em>autoloader</em></li>
<li>code, including full configuration directions, on GitHub at
<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.autoloader">MinchinWeb/minchin.pelican.plugins.autoloader</a></li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.autoloader/blob/master/CHANGELOG.rst">full changelog</a></li>
</ul></body></html>Selecting a Code of Conduct for My Software Projects2023-06-09T22:36:00-06:002023-06-09T22:36:00-06:00Wm. Minchintag:blog.minchin.ca,2023-06-09:/2023/06/code-of-conduct.html<p>Towards the end of <del>2011</del> 2021 (in the depths of Covid…), I started
thinking about
adding a <em>code of conduct</em> to my open source software projects. Github
recommends adding one, somewhat similiar to how they recommend including a
software license.</p>
<p>In trying to pick a <em>code of conduct</em> (for my projects), it seems helpful to
remember the “community”, as such, is often basically me, short (in length) is
generally better, and just about anything can be weaponized by bad faith actors.</p>
<html><head></head><body><p>Towards the end of <del>2011</del> 2021 (in the depths of Covid…), I started
thinking about
adding a <em>code of conduct</em> to my open source software projects. Github
recommends adding one, somewhat similiar to how they recommend including a
software license.</p>
<p>In trying to pick a <em>code of conduct</em> (for my projects), it seems helpful to
remember the “community”, as such, is often basically me, short (in length) is
generally better, and just about anything can be weaponized by bad faith actors.</p>
<h2>The Most Basic: Troll Banning</h2>
<p>I suppose the most basic form of a <em>code of conduct</em> is just have a (written)
policy to ban any trolls, or “Don’t be a jerk!”. It seems almost ridiculous
that you would have to spell this out. But, I suppose as you start dealing with
a wider array of people, it’s helpful to outline what behaviour isn’t
appreciated (e.g. “Doing X makes you a jerk; don’t do it!”).</p>
<h2>The Most Complex: Corporate Codes of Conduct</h2>
<p>When searching Google for examples of <em>codes of conduct</em>, the corporate variety
was by far the most common that came up; one could argue this is the “true”
definition of the term. These tend to be very legalistic (being written by the
legal department), very long (often hundreds of pages), and often very complex.
But I don’t need all that: I’m don’t need to deal with travel reimbursements, I
don’t need to deal with conflicts of interest, etc. I doubt anyone want to
contribute a small fix to my software projects will read something that long,
and I don’t want to spend the next three months (or three years!) writing
something like this either.</p>
<h2>The Most Common: The <em>Contributor Code of Conduct</em></h2>
<p>The <a href="https://www.contributor-covenant.org/version/1/2/0/code-of-conduct/">Contributor Code of
Conduct</a>
(aka the “Contributor Covenant”) is probably the most commonly used
Code of Conduct for software projects and seems to have the largest mindshare;
to some extent, it feels like the injunction to “add a code of conduct” is an
injunction to bind yourself and your project by the Contributor Code of
Conduct, whether you adopt that specific code or not.</p>
<p>It is among the three codes of conduct suggested by GitHub<sup id="fnref:github-cocs"><a class="footnote-ref" href="#fn:github-cocs">1</a></sup>,
although the three of them seem similiar in nature. One of the three actually
says the “primary goal of {COMMUNITY_NAME} is to be inclusive to the largest
number of contributors, with the most varied and diverse backgrounds
possible.”<sup id="fnref:primary-goal"><a class="footnote-ref" href="#fn:primary-goal">2</a></sup>…and I’m not sure that’s a useful goal for any
(functional) group. For example, if you take any marketing, they will suggest
having a “target audience” or an ideal customer to possition your project for.
As a practical matter, if everyone is accepted, what is the common goal or
purpose to hold the group together? As you deal with others, it’s fairly
obvious that some people are more productive contributors, and some are more
excited about the project; both things that I feel should be encouraged.</p>
<p>For me, the goal is to actually have a working piece of software, for me first
of all. I don’t see how these codes of conduct help make that a reality.</p>
<h2>The SQLite Option: The Benedictine Code</h2>
<p>Presumably other (software/open source) codes of conduct have been developed
(though none seem particularly popular), but one of the most interesting (to
me) is the story of SQLite and their Code of Conduct adopted from the
Benedictine Code. SQlite, for various corporate contracts, was asked to link to
their project’s code of conduct, which at the time they lacked. Looking around,
they decided to adopt the Benedictine Code, specifically Chapter 4. This
Chapter is a list of 73 <em>tools for good works</em> and was originally written for
the Benedictine monks ca. <span class="caps">530AD</span>, and has been a foundation of their Order
since. In reading through the list, it felt more like what I myself was looking
for. In particular, it seems to focus almost entirely on the actions I want to
see in the community.</p>
<p>There has been much critism leveled of it, mostly seeming to center on its
religious nature. I don’t feel the Code itself is particularly religious,
although it was written for a group of religious believers who are trying to
better live their (shared) faith. Personally, it seems that some people mistake
the mention of religion as implying that the text <em>is</em> a religious text, when
it more often that the writers lived in a religious society (such as in this
case). Perhaps the right response to these critisms is to request that other
codes of conduct add <em>religious identity</em> and <em>religious expression</em> to their
list of prohitied discrimination grounds (to mirror the current listing of
“gender, gender identity, and gender expression”); often enough, religious
people are asked to keep their faith out of sight as a condition of having it
at all. Although the principals at SQLite do seem to be religious, they have
also been clear that your (personal) religion will not bar you from
participating in the project: in their introduction they explain that you are
not required to believe, agree with, or even accept the Code to participate in
their project.</p>
<p>Another complaint has been that it doesn’t lay out an enforcement mechanism.
Except that it does, asking that those who have failed to live up to the ideals
of the Code to be extended <em>grace</em>; for a first pass of many issues, this seems
a reasonable response.</p>
<p>And the mention of <em>chastity</em> seems like a
brillant way of heading off the sexual harassment and assult concerns that can
be the most impactful for a community to protect against.</p>
<h2>For Me</h2>
<p>For my projects, I’ll be using a version of the Benedictine Code. It seems
short enough that people may actually read it, it promotes the things I want to
see in the community (rather than just being a list of horrible things people
might do to each other), and its references to grace seem like a decent
response to the misunderstanding most commonly encountered. Here’s the text:</p>
<blockquote>
<h3>Code of Conduct</h3>
<h4>Purpose</h4>
<p>The Founder of this project has pledged to govern their interactions
with each other and with the larger this project’s user community in
accordance with the “instruments of good works” from chapter 4 of <a href="https://en.wikipedia.org/wiki/Rule_of_Saint_Benedict">The Rule
of St. Benedict</a>
(hereafter: “The Rule”). This code of ethics has proven its mettle in
thousands of diverse communities for over 1,500 years, and has served as a
baseline for many civil law codes since the time of Charlemagne.</p>
<h5>Scope of Application</h5>
<p>No one is required to follow The Rule, to know The Rule, or even to think
that The Rule is a good idea. The Founder of this project believes that
anyone who follows The Rule will live a happier and more productive life, but
individuals are free to dispute or ignore that advice if they wish.</p>
<p>The Founder of this project and all current developers have pledged to follow
the spirit of The Rule to the best of their ability. They view The Rule as
their promise to all project users of how the developers are expected to
behave. This is a one-way promise, or covenant. In other words, the
developers are saying: “We will treat you this way regardless of how you
treat us.”</p>
<h4>The Rule</h4>
<ol>
<li>First of all, love the Lord God with your whole heart, your whole soul,
and your whole strength.</li>
<li>Then, love your neighbor as yourself.</li>
<li>Do not murder.</li>
<li>Do not commit adultery.</li>
<li>Do not steal.</li>
<li>Do not covet.</li>
<li>Do not bear false witness.</li>
<li>Honor all people.</li>
<li>Do not do to another what you would not have done to yourself.</li>
<li>Deny oneself in order to follow Christ.</li>
<li>Chastise the body.</li>
<li>Do not become attached to pleasures.</li>
<li>Love fasting.</li>
<li>Relieve the poor.</li>
<li>Clothe the naked.</li>
<li>Visit the sick.</li>
<li>Bury the dead.</li>
<li>Be a help in times of trouble.</li>
<li>Console the sorrowing.</li>
<li>Be a stranger to the world’s ways.</li>
<li>Prefer nothing more than the love of Christ.</li>
<li>Do not give way to anger.</li>
<li>Do not nurse a grudge.</li>
<li>Do not entertain deceit in your heart.</li>
<li>Do not give a false peace.</li>
<li>Do not forsake charity.</li>
<li>Do not swear, for fear of perjuring yourself.</li>
<li>Utter only truth from heart and mouth.</li>
<li>Do not return evil for evil.</li>
<li>Do no wrong to anyone, and bear patiently wrongs done to yourself.</li>
<li>Love your enemies.</li>
<li>Do not curse those who curse you, but rather bless them.</li>
<li>Bear persecution for justice’s sake.</li>
<li>Be not proud.</li>
<li>Be not addicted to wine.</li>
<li>Be not a great eater.</li>
<li>Be not drowsy.</li>
<li>Be not lazy.</li>
<li>Be not a grumbler.</li>
<li>Be not a detractor.</li>
<li>Put your hope in God.</li>
<li>Attribute to God, and not to self, whatever good you see in yourself.</li>
<li>Recognize always that evil is your own doing, and to impute it to yourself.</li>
<li>Fear the Day of Judgment.</li>
<li>Be in dread of hell.</li>
<li>Desire eternal life with all the passion of the spirit.</li>
<li>Keep death daily before your eyes.</li>
<li>Keep constant guard over the actions of your life.</li>
<li>Know for certain that God sees you everywhere.</li>
<li>When wrongful thoughts come into your heart, dash them against Christ immediately.</li>
<li>Disclose wrongful thoughts to your spiritual mentor.</li>
<li>Guard your tongue against evil and depraved speech.</li>
<li>Do not love much talking.</li>
<li>Speak no useless words or words that move to laughter.</li>
<li>Do not love much or boisterous laughter.</li>
<li>Listen willingly to holy reading.</li>
<li>Devote yourself frequently to prayer.</li>
<li>Daily in your prayers, with tears and sighs, confess your past sins to
God, and amend them for the future.</li>
<li>Fulfill not the desires of the flesh; hate your own will.</li>
<li>Obey in all things the commands of those whom God has placed in authority
over you even though they (which God forbid) should act otherwise,
mindful of the Lord’s precept, “Do what they say, but not what they do.”</li>
<li>Do not wish to be called holy before one is holy; but first to be holy,
that you may be truly so called.</li>
<li>Fulfill God’s commandments daily in your deeds.</li>
<li>Love chastity.</li>
<li>Hate no one.</li>
<li>Be not jealous, nor harbor envy.</li>
<li>Do not love quarreling.</li>
<li>Shun arrogance.</li>
<li>Respect your seniors.</li>
<li>Love your juniors.</li>
<li>Pray for your enemies in the love of Christ.</li>
<li>Make peace with your adversary before the sun sets.</li>
<li>Never despair of God’s mercy.</li>
</ol>
</blockquote>
<p>(The header image is “St. Benedict delivering his Rule to St. Maurus and other
monks of his order”, from a manuscript from Monastery of St. Gilles in Nîmes,
France, dated 1129. The image is copied from <a href="https://en.wikipedia.org/wiki/File:St._Benedict_delivering_his_rule_to_the_monks_of_his_order.jpg">Wikipedia
Commons</a> )</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:github-cocs">
<p>the <em>Contributor Covent</em>, the <em><a href="https://www.djangoproject.com/conduct/">Django Code of
Conduct</a></em> and the <em><a href="https://web.archive.org/web/20200330154000/http://citizencodeofconduct.org/">Citizen Code of
Conduct</a></em>
are the three codes of conduct, from GitHub’s <a href="https://opensource.guide/code-of-conduct/">Open Source
Guide</a>. <a class="footnote-backref" href="#fnref:github-cocs" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:primary-goal">
<p><span class="dquo">“</span>Purpose” (the first section) of the <em><a href="https://web.archive.org/web/20200330154000/http://citizencodeofconduct.org/">Citizen Code of
Conduct</a></em> <a class="footnote-backref" href="#fnref:primary-goal" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
</ol>
</div></body></html>What Happens If Self-Driving Cars Become a Reality?2023-06-08T15:58:00-06:002024-02-22T21:01:00-07:00Wm. Minchintag:blog.minchin.ca,2023-06-08:/2023/06/self-driving-cars.htmlThis is a thought experiment, and perhaps a glimpse into the future. The
question I start with is, “What happens if self-driving cars become a reality?”
I note that first order effects (like, Now I can eat breakfast in the car
because I don’t have to hold the steering wheel anymore) are the easiest to
see, and with each step out (Does my breakfast change? How does that effect egg
farmers?), the results get easier and easier to get wrong.<html><head></head><body><p>This is a thought experiment, and perhaps a glimpse into the future. The
question I start with is, “What happens if self-driving cars become a reality?”
I note that first order effects (like, Now I can eat breakfast in the car
because I don’t have to hold the steering wheel anymore) are the easiest to
see, and with each step out (Does my breakfast change? How does that effect egg
farmers?), the results get easier and easier to get wrong.</p>
<p>I also note that we may never see true self-driving cars. Self-driving cars are
starting to feel like flying cars, in that they are just around the corner, but
years later have yet to arrive.</p>
<h2>Baseline Assumptions</h2>
<p>First, a few baseline assumptions in this scenario:</p>
<ul>
<li><span class="dquo">“</span>Self-driving” means cars can drive themselves without human control, either
local or remote<sup id="fnref:2023060801"><a class="footnote-ref" href="#fn:2023060801">1</a></sup>. Also, that they can drive in the full range of
conditions and situations that prudent human drivers can (and do) today. So
it’s not going to take you off-roading, but snow on the highway or missing
lane lines through a construction zone are not a problem.</li>
<li>Cars are going to be electric powered. This will be driven by (among other
things) the need to safely “refuel” the car without requiring a human on-site.</li>
<li>People are going to continue to go to the same places (work, school,
shopping, etc.) in roughly the same amount as today (at least in the beginning).</li>
<li>These self-driving cars will be roughly comparable with the cost to current
cars; i.e. anyone who can currently afford a car today would be able to
afford a future self-driving car.</li>
<li>Renting a self-driving car will be cheaper than getting a taxi today, as
there is no human behind the wheel to pay.</li>
</ul>
<h2>Rent vs Own</h2>
<p>The question of whether we will rent or own these new cars has huge
implications for those who are proposing to own the rental fleets (like Uber),
but probably don’t change the may of the outcomes below. I’m going to (mostly)
punt on the question.</p>
<h2>Scenario 1: Driving the Kids to School</h2>
<p>One of the first interesting scenarios is getting the kids to school. Today,
either Mom (or Dad) drives them, or they take a school bus, or they walk.
However, once you have a self-driving car, Mom doesn’t need to do more than
buckle the kid into the car and sent it on its way. Also, as Mom is not longer
tied to dropping off the kids, the kids can now go to school further from home
or (Mom’s) work; really any school within about an hour has become a candidate.
There is also one less thing to encourage sibling to attend the same school, as
they can each take their own car to their respective school.</p>
<p><strong>Guess 1</strong>: There will develop a demand for “kid pods”: small, self-driving
cars that are sized to carry a single child and his school backpack.</p>
<p>One question that arises is, What to do with the kid’s cars while they’re in
school? Do the elementary schools build a parking lot, like my high school had?
Do you send the cars home? Do you send them away to another off-site parking
lot, or on an all day drive?</p>
<p><strong>Guess 2</strong>: Parking “kid pods” will quickly become a policy issue for school boards.</p>
<h2>Scenario 2: Regular Commute</h2>
<p>The most reliable of trips for most people are their trips to and from work.
Almost universally, it is from a one set location (home) to another (the
office) at predicable times.</p>
<p>I’m writing this in the shadow of Covid, and so Work From Home (<span class="caps">WFH</span>) has become
almost universal in certain industries. Time will tell whether <span class="caps">WFH</span> becomes a
norm or these past two years have been an aberration. But regardless, in-person
work is required in many sectors, and many <span class="caps">WFH</span> organizations are pushing for a
hybrid arrangement that would see workers returns to the office several days a
week. In short, I don’t think that this Covid-era has killed the commute.
However, it has become part of the work conversation to discuss non-traditional
arraignments (like <span class="caps">WFH</span>, even for part of the week) or adjusted hours. Where
this intersects with self-driving cars, I expect many couples would request
hours offset from each other so they could use the same car to commute, one
after the other, and I suspect bosses have become increasingly open to
considering this.</p>
<p>As well, if you are commuting in a rental car (rather than one you own), I
expect that the further you get from the traditional 8 to 5 start and end
times, the cheaper the rental rates will be (due to lower demand).</p>
<p><strong>Guess 3</strong>: Bosses will get more requests for slightly offset start and end
times, to arraign commuting.</p>
<p>Like with kids delivered to school by self-driving cars, a question arises of
what to do during the workday with your self-driving car. “Regular” parking
remains an option, but is not free in many downtowns. You could send it to park
elsewhere: perhaps a lot outside of the downtown core or even to your house. Or
you could send it on a trip: circling downtown or on a long loop away and back
in time to take you home.</p>
<p><strong>Guess 4</strong>: Cities will pass “anti-cruising” laws to keep self-driving cars
from endlessly circling the block, sometimes for hours, waiting for their passengers.</p>
<p><strong>Guess 5</strong>: Downtown rush hour traffic will get worse, as many cars will make
two trips (in and out) to the downtown core at each end of the workday, either
to pick up a second passenger for their own trip to work, or to park or loop
outside the downtown core.</p>
<h2>Scenario 3: “Work from Commute”</h2>
<p>Since you don’t have to be the one driving, could you “work from home” during
your commute? Perhaps this would require re-configuring your car from one with
four seats to a single seat and a flat work surface, but you don’t need a
steering wheel so there isn’t much (beyond imagination) keeping this from happening.</p>
<p><strong>Guess 6</strong>: Self-driving cars will be reconfigured inside to allow other uses,
such as being a mobile office.</p>
<p>Once you have a “mobile office” aka a “work from commuting car”, what are the
chances you could convince your boss to count your time traveling (but working)
as part of your 8 hour work shift, effectively reducing how long you’re gone
from the house? Or maybe you just work half days in the office, and count your
commuting time without asking your boss.</p>
<p>But taken (literally) a step further, once you have a “working commute”, how
long could your commute reasonably be? Two or three hours? At that point, you
could still work half days in the office, with the other half from “at home”,
and still not be adding anything to your 8 hour day.</p>
<p>Now you have access to housing far beyond the urban core (up to 300 km/200
miles away?), allowing you to live in rural areas or small towns, presumably
with their lower housing costs, while still having many of the amenities of
urban life (after all, you drive to the “big city” every day.)</p>
<p><strong>Guess 7</strong>: Some commuters will be able to live considerably further from the
office than today, while still working half days in the office. This may prove
the salvation of some rural areas and small towns.</p>
<h2>Scenario 4: Business Trips</h2>
<p>Eventually, some boss is going to look at someone’s two to four hour commute,
and ask them if they’re willing to drive the <em>other</em> way to do a meeting an
another office or a partner’s location. What today would require dedicating
three (or more) days and a couple of flights can be done as “just another day
in the office”. Or perhaps a boss will work like this, managing two different
locations, in cities up to 8 hours apart, while living halfway between them.</p>
<p>Someone in the accounting department will hear rumour of this, and wonder how
much you can push this. I think the next leap would be overnight trips: hop in
the car after supper and arrive in the new office by mid-morning would give you
a range of about 12 or so hours. At this point, your car would need someplace
for you to sleep, whether a “proper” bed or a reclining chair, and possibly a
toilet or at least a modern take on a slop pail. If the person is high enough
up, they might also be able to demand a shower and a sink to get ready at. The
space may be able to be reconfigurable, so you don’t need 4+ rooms. If you add
a kitchen, you would basically have a studio apartment (on wheels!) or a motorhome.</p>
<p>Now you have access to a huge chunk of the continent as basically “another day
in the office”! For example:</p>
<ul>
<li>from Calgary, you could reach Vancouver (10 1/2 hours), Edmonton (3 hours),
Winnipeg (14 hours), and Salt Lake City (13 hours);</li>
<li>from Grande Prairie, Alberta, you could reach Yellowknife, Edmonton, Calgary,
Regina, Great Falls <span class="caps">MT</span>, and Vancouver;</li>
<li>from Chicago, you could reach New York, Washington <span class="caps">DC</span>, Atlanta, Oklahoma
City, Dallas in a pinch, Winnipeg, Ottawa, and Montreal.</li>
</ul>
<p><strong>Guess 8</strong>: If another group doesn’t first, business travellers will create a
demand for “motorhome” versions of the self-driving car.</p>
<p><strong>Guess 9</strong>: The relative ease of travel (i.e. you no longer have to sit behind
the wheel) will see increasing numbers of managers and salesmen managing far
flung territories.</p>
<h2>Scenario 5: Life on the Road</h2>
<p>So at this point in our thought experiment, we now have self driving
motorhomes. New units will likely be as expensive as today’s motorhomes:
starting at $50,000, but rising to $300- or $400,000. Initially this will limit
the market: business executives and traveling salesman (often corporate
bought), and permanent <span class="caps">RV</span>-ers. But given a few years, as sales volumes rise and
a used market develops, the entry price may drop.</p>
<p>At the low end of the housing market, buying a (self-driving) motorhome will
also become increasing appealing; after all, the top end (~$400,000) for a
(new) motorhome is still only the average house prices in many of the more
“reasonable” housing markets, to say nothing of the more expensive ones.</p>
<p><strong>Guess 10:</strong> The relative low cost of self-driving motorohomes, when compared
with other forms of housing in major centers, will lead to a whole class of
“mobile home dwellers,” a landless and mobile class.</p>
<p>What is less clear is how society as a whole will respond to a large,
functionally mobile class (let’s call them <em>The Travellers</em>). There are certain
people that do this today (living out of an <span class="caps">RV</span> or a boat), but the change here
is the sheer scale of it. Much of today’s society has the implicit assumption
that you have an physical (civil) address that is relatively stable. Perhaps
history will be our guide, and we can look at how the Roma (aka the Gypsies) or
the Irish Travellers have been treated, but neither of those are particularly
heartwarming examples, and neither of them had a strong presence in North America.</p>
<p><strong>Guess 11:</strong> Having a large, landless, travelling class of people will change
society far more than self-driving cars do directly. How, I’m less sure, so let
me ask some questions:</p>
<ul>
<li>How do you get mail? Today, your default mailing address is your
physical (civil) address. Will a <span class="caps">PO</span> box be enough?</li>
<li>How do you establish (or un-establish) or prove tax residency
when you’re working on the move? Today, many <span class="caps">US</span> states expect you to file and
pay state level income tax if you do any work in their state, but how do they
go about proving you were in their state when there are no hotel receipts or
plane tickets to tie you somewhere for the night? I mean, they don’t
currently
try and claim for work you do as fly over the state….</li>
<li>Do border crossing change when from the outside someone (in a self-driving
motorhome) going on vacation and someone working from their <span class="caps">RV</span> look much alike?</li>
<li>How do you prove residency for immigration or healthcare or for in-state
tuition? All of these require that you spend a certain number of days within
the jurisdiction over years of time.</li>
<li>How do you establish residency in a local
neighboorhood to allow your kids to attend the local school?</li>
<li>When it is census time, where do you get counted?</li>
<li>How do you establish enough ties to a local riding (or distrcit) to be
allowed to vote there? Today, I can show up with my driver’s license as proof
that I live in the district, so will this get outsourced to whoever prints up
your driver’s license? Currently, if you’re American and you move out of the
county, you continue to vote in the last district you lived in (inside the
<span class="caps">US</span>); will this be extended to <em>Travellers</em>, and you’ll vote at your last
physical address? What happens when you’re the child of <em>Travellers</em>, and
have never had a “physical address”?</li>
<li>What would you put on your security clearance (or immigration) forms when it
asks you for your address for the last five years? Or your mortgage application?</li>
<li>How does a city change when a huge number of their citizens are landless? Do
cities build parking spaces for the <em>Travellers</em>, and try and attract them?
Or do they ignore them, and leave them shut out of the political process?</li>
<li>What happens to the “landed” and their communities? Do land-based communities
become a new form of a gated community?</li>
<li>On a personal level, who stops in to check on your “apartment,” to discover
that you died in front of the <span class="caps">TV</span>, when you never stop anywhere longer than it
takes to refuel? Will your motorhome keep driving, potentially for years,
until your bank account no longer has the funds for the next refueling?</li>
</ul>
<h2>Conclusion</h2>
<p>First order effects are easy enough to guess at, but our society is complex and
varied, and how the “end game” of self-driving cars plays out could result in a
fundamentally different society than the one we assume now.</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:2023060801">
<p><strong>Update</strong>, February 22, 2024: When I wrote this originally, I
was imagining you having to hire a driver to either sit in the car with you or
sit in a window-less room somewhere nearby driving your car around like it’s
some sort of video game. The hiccup to both these is they add a substantial
operating cost to driving, and my predictions that follow are based on the idea
of the vehicle being <em>very</em> cheap to actually operate. However, I have since
been reminded of the idea of <em>artificial artificial intelligence</em>, that is to
say a computer (“<span class="caps">AI</span>”) that operates by outsourcing the thinking parts to a
human elsewhere (and thus is a “fake <span class="caps">AI</span>”). If that elsewhere is somewhere where
local wages are very cheap and latency between there and your car isn’t too
bad, maybe the assumed economics still hold. The advantage of <em>artificial
artificial intelligence</em> is it allows you to launch your service without having
to deal with every edge case, instead allowing your backroom operators to solve
such problems on the fly. <a class="footnote-backref" href="#fnref:2023060801" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
</ol>
</div></body></html>Summary Plugin 1.2.0 for Pelican Released2022-07-11T22:12:00+02:002022-07-11T22:12:00+02:00Wm. Minchintag:blog.minchin.ca,2022-07-11:/2022/07/summary-for-pelican-120-released.html
<p>This release, version 1.2.0, makes a few fixes to allow the plugin to support
Pelican 4 and also supports my
<a href="http://localhost:8000/label/autoloader/">autoloader</a>.</p>
<html><head></head><body><p><strong>Summary</strong> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a static
site generator written in Python.</p>
<p><strong>Summary</strong> allows easy, variable length summaries directly embedded within the
body of your articles.</p>
<h2>This Release</h2>
<p>This release, version 1.2.0, makes a few fixes to allow the plugin to support
Pelican 4 and also supports my
<a href="http://localhost:8000/label/autoloader/">autoloader</a>.</p>
<h2>Upgrading</h2>
<p>To upgrade, simply use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.summary<span class="w"> </span>--upgrade
</code></pre></div>
<p>The autoloader plugin should be automatically installed.</p>
<p>If you are using Pelican 4.5+, the plugin will automatally be loaded.</p>
<p>If you are an earlier version of Pelican, or non-namespace plugins, you will
need to add the auto-loader to your list of plugins (and no longer need to list
the summary plugin):</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># others</span>
<span class="s2">"minchin.pelican.plugins.autoloader"</span><span class="p">,</span>
<span class="c1"># summary plugin no longer needs to be listed</span>
<span class="p">]</span>
</code></pre></div>
<h2>Credits</h2>
<p>Thanks to <a href="https://github.com/HenrySwanson">Henry Swainson</a> for the <a href="https://github.com/MinchinWeb/minchin.pelican.plugins.summary/pull/1">pull
request</a>
that forms the basis of most of this release!</p>
<h2>Personal Note</h2>
<p>With this, I’m finally able to upgrade to Pelican 4! It’s been a long time coming…</p></body></html>Image Process Plugin 3.0.2 for Pelican Released2022-07-11T13:02:00+02:002022-07-11T17:39:00+02:00Wm. Minchintag:blog.minchin.ca,2022-07-11:/2022/07/image-process-302.html
<p>This post actually covers three releases:</p>
<ul>
<li><strong>v3.0.0</strong> adds support for Pillow v9. No changes were made that would make
the plugin incompatible with earlier versions of Pillow, other than the test
output images are slightly different between Pillow 8 and 9. This version
also removed (official) support for Python 3.6, which isn’t supported by
Pillow 9.</li>
<li><strong>v3.0.1</strong> fixes function calls that will be deprcated by Pillow v10,
scheduled to be released in about a year from now. These changes probrobably
make the plugin incompatible with versions of Pillow before v9.1.0.</li>
<li><strong>v3.0.2</strong> bumps the lowest officially supported version of Pillow to v9.1.0
and (preemptively) adds support for v10.</li>
</ul>
<html><head></head><body><p><em>Image Process</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>Image Process</em> let you automate the processing of images based on their <span class="caps">HTML</span>
class attributes. Use this plugin to minimize the overall page weight and to
save you a trip to Gimp or Photoshop each time you include an image in your post.</p>
<p><em>Image Process</em> is used by
<a href="https://github.com/MinchinWeb/seafoam">this blog’s theme</a> to resize the source
images so they are the correct size for thumbnails on the main index page and
the larger size they are displayed at on top of the articles.</p>
<h2>This Release</h2>
<p>This post actually covers three releases:</p>
<ul>
<li><strong>v3.0.0</strong> adds support for Pillow v9. No changes were made that would make
the plugin incompatible with earlier versions of Pillow, other than the test
output images are slightly different between Pillow 8 and 9. This version
also removed (official) support for Python 3.6, which isn’t supported by
Pillow 9.</li>
<li><strong>v3.0.1</strong> fixes function calls that will be deprcated by Pillow v10,
scheduled to be released in about a year from now. These changes probrobably
make the plugin incompatible with versions of Pillow before v9.1.0.</li>
<li><strong>v3.0.2</strong> bumps the lowest officially supported version of Pillow to v9.1.0
and (preemptively) adds support for v10.</li>
</ul>
<h2>Upgrading</h2>
<h3>to v3.0.0</h3>
<p>To upgrade simply use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pelican-image-process<span class="w"> </span>--upgrade
</code></pre></div>
<p>This should automatically upgrade Pillow (if needed) as well.</p>
<p>Python 3.6 is no longer officially supported.</p>
<p>The code for this release was ready back in February, but issues with the
release framework kept the “official” release from happening until now.</p>
<h3>to v3.0.1 and v3.0.2</h3>
<p>Again, use <code>pip</code>, as above.</p>
<p>This should work with Pillow 10 when it is released.</p></body></html>Static Comments Plugin 2.1.1 for Pelican Released2022-04-30T14:17:00-06:002022-04-30T14:17:00-06:00Wm. Minchintag:blog.minchin.ca,2022-04-30:/2022/04/static-comments-211-released.html<p><em>Static Comments</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python. It is meant as a drop in replacement
for the <a href="https://github.com/Scheirle/pelican_comment_system">Pelican Comment System</a>.</p>
<p><em>Static Comments</em> allows you to have a comment section on your Pelican blog,
while maintaining your blog as a completely static webpage and without relying
on any external services or servers; just an email address is required.
Comments are stored as text files, similiar in structure to Pelican articles.
This gives you complete control over the comments appearing on your site and
allows you to back them up with the rest of your site.</p>
<html><head></head><body><p><em>Static Comments</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python. It is meant as a drop in replacement
for the <a href="https://github.com/Scheirle/pelican_comment_system">Pelican Comment System</a>.</p>
<p><em>Static Comments</em> allows you to have a comment section on your Pelican blog,
while maintaining your blog as a completely static webpage and without relying
on any external services or servers; just an email address is required.
Comments are stored as text files, similiar in structure to Pelican articles.
This gives you complete control over the comments appearing on your site and
allows you to back them up with the rest of your site.</p>
<h2>This Release</h2>
<p>This release takes the existing <em>Pelican Comment System</em> codebase and upgrades
it to work with Pelican 4 (and should continue to work with Pelican 3). A few
changes are needed in your configuration, but no changes to your comments files
should be needed.</p>
<h2>Installation</h2>
<p>The simplest way to install the Python code of <em>Static Comments</em> is to use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.static-comments<span class="w"> </span>--upgrade
</code></pre></div>
<p>If you are using Pelican 4.5+, the plugin will automatally be loaded (although
not activated).</p>
<p>If you are an earlier version of Pelican, or non-namespace plugins, you will
need to add the auto-loader to your list of plugins:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># others</span>
<span class="s2">"minchin.pelican.plugins.autoloader"</span><span class="p">,</span>
<span class="p">]</span>
</code></pre></div>
<p>Activate the plugin by adding the following line to your <code>pelicanconf.py</code>:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="n">PELICAN_COMMENT_SYSTEM</span> <span class="o">=</span> <span class="kc">True</span>
</code></pre></div>
<p>and then set the email you want to receive comment emails at:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="n">PELICAN_COMMENT_SYSTEM_EMAIL_USER</span> <span class="o">=</span> <span class="s2">"your.email"</span>
<span class="n">PELICAN_COMMENT_SYSTEM_EMAIL_DOMAIN</span> <span class="o">=</span> <span class="s2">"gmail.com"</span>
</code></pre></div>
<p>Finally, modify the <code>article.html</code> of your theme (if your theme doesn’t
support <em>Static Comments</em> out of the box) to both display comments already
submitted and to have a comment submission form. The sample submission form works
by using JavaScript to convert the form contents (the commenter’s name, site,
and comment body) to an email the user then sends to you. Note, this is an
example of the code you might use for your theme, but please feel free to
modify it to suit your needs.</p>
<div class="highlight"><pre><span></span><code>{% <span class="n">macro</span> <span class="n">comments_styles</span>() %}
{% <span class="k">if</span> <span class="n">PELICAN_COMMENT_SYSTEM</span> %}
{<span class="c1"># NOTE:</span>
<span class="c1"># Instead of using this macro copy these styles in your main css file</span>
<span class="c1"># This marco is only here to allow a quickstart with nice styles</span>
<span class="c1">#}</span>
<span class="s"><style></span>
<span class="c1">#pcs-comment-form input,</span>
<span class="c1">#pcs-comment-form textarea {</span>
<span class="n">width:</span> <span class="mi">100</span>%;
}
<span class="c1">#pcs-comment-form-display-replyto {</span>
<span class="n">border:</span> <span class="n">solid</span> <span class="mi">1</span><span class="n">px</span> <span class="n">black</span>;
<span class="n">padding:</span> <span class="mi">2</span><span class="n">px</span>;
}
<span class="c1">#pcs-comment-form-display-replyto p {</span>
<span class="n">margin-top:</span> <span class="mf">0.5</span><span class="n">em</span>;
<span class="n">margin-bottom:</span> <span class="mf">0.5</span><span class="n">em</span>;
}
<span class="c1">#pcs-comments ul {</span>
<span class="n">list-style:</span> <span class="nb">none</span>;
}
<span class="c1">#pcs-comments .comment-left {</span>
<span class="n">display:</span> <span class="n">table-cell</span>;
<span class="n">padding-right:</span> <span class="mi">10</span><span class="n">px</span>;
}
<span class="c1">#pcs-comments .comment-body {</span>
<span class="n">display:</span> <span class="n">table-cell</span>;
<span class="n">vertical-align:</span> <span class="n">top</span>;
<span class="n">width:</span> <span class="mi">100</span>%;
}
<span class="s"></style></span>
{% <span class="n">endif</span> %}
{% <span class="n">endmacro</span> %}
{% <span class="n">macro</span> <span class="n">comments_form</span>() %}
{% <span class="k">if</span> <span class="n">PELICAN_COMMENT_SYSTEM</span> %}
<span class="s"><section>
<span class="s"><form action="#" id="pcs-comment-form">
<span class="s"><legend></legend></span><span class="n">Add</span> <span class="n">a</span> <span class="n">Comment</span><span class="s"></span>
<span class="s"><input id="pcs-comment-form-input-replyto" type="hidden"/></span>
<span class="s"><fieldset>
<span class="s"><label for="pcs-comment-form-input-name"></label></span><span class="n">Name</span><span class="s"></span>
<span class="s"><input id="pcs-comment-form-input-name" placeholder="Enter your name or nickname" type="text"/></span>
<span class="s"></span></fieldset></span>
<span class="s"><fieldset>
<span class="s"><label for="pcs-comment-form-input-website"></label></span><span class="n">Website</span><span class="s"></span>
<span class="s"><input id="pcs-comment-form-input-website" placeholder="Enter your website (optional)" type="text"/></span>
<span class="s"></span></fieldset></span>
<span class="s"><fieldset>
<span class="s"><label for="pcs-comment-form-input-textarea"></label></span><span class="n">Your</span> <span class="n">Comment</span><span class="s"></span>
<span class="s"><textarea id="pcs-comment-form-input-textarea" placeholder="Your comment" rows="5" style="resize:vertical;"></textarea></span>
<span class="s"><p>You can use the <a href="https://en.wikipedia.org/wiki/Markdown"><span class="n">Markdown</span><span class="s"></span></a> <span class="n">syntax</span> <span class="nb">to</span> <span class="nb">format</span> <span class="n">your</span> <span class="nb">comment</span>.<span class="s"></span></p></span>
<span class="s"><div id="pcs-comment-form-display-replyto" style="display: none; "></div></span>
<span class="s"></span></fieldset></span>
<span class="s"><button <="" span="" type="submit">
<span class="s"> id="pcs-comment-form-button-submit"</span>
<span class="s"> {# Piwik Track click on comment button</span>
<span class="s"> onclick="javascript:_paq.push(['trackEvent', 'comment', '{{ article.title }}', document.getElementById('pcs-comment-form-input-textarea').value]);" #}</span>
<span class="s"> >Post via email</span></button></span>
{% <span class="k">if</span> <span class="n">PELICAN_COMMENT_SYSTEM_FEED</span> <span class="o">and</span> <span class="n">article</span> %}
<span class="s"><a href="{{ SITEURL }}/{{ PELICAN_COMMENT_SYSTEM_FEED|format(article.slug) }}"></a></span><a href="{{ SITEURL }}/{{ PELICAN_COMMENT_SYSTEM_FEED|format(article.slug) }}">
<span class="n">Comment</span> <span class="n">Atom</span> <span class="n">Feed</span>
<span class="s"></span></a>
{% <span class="n">endif</span> %}
<span class="s"></span></form>
<span class="s"></span></span></section></span>
{% <span class="n">endif</span> %}
{% <span class="n">endmacro</span> %}
{% <span class="n">macro</span> <span class="n">comments_with_form</span>() %}
{% <span class="k">if</span> <span class="n">PELICAN_COMMENT_SYSTEM</span> %}
<span class="s"><section id="pcs-comments">
<span class="s"><header>
<span class="s"><h2><span class="n">Comments</span><span class="s"></span></h2></span>
<span class="s"><hr/></span>
<span class="s"></span></header></span>
{% <span class="k">if</span> <span class="n">article</span>.<span class="n">comments</span> %}
<span class="s"><ul>
{% <span class="k">for</span> <span class="nb">comment</span> <span class="nb">in</span> <span class="n">article</span>.<span class="n">comments</span> <span class="n">recursive</span> %}
<span class="s"><li id="comment-{{comment.slug}}">
<span class="s"><div class="comment-left">
<span class="s"><img <="" span="" src="{{ SITEURL }}/{{ comment.avatar }}"/>
<span class="s"> alt="Avatar"</span>
<span class="s"> height="{{ PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE }}"</span>
<span class="s"> width="{{ PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE }}"></span>
<span class="s"></span></span></div></span>
<span class="s"><div class="comment-body">
<span class="s"><div style="float:right;">
<span class="s"><a href="{{ SITEURL }}/{{ article.url }}#comment-{{comment.slug}}" rel="bookmark" role="button" title="Permalink to this comment"></a></span><a href="{{ SITEURL }}/{{ article.url }}#comment-{{comment.slug}}" rel="bookmark" role="button" title="Permalink to this comment"><span class="n">Permalink</span><span class="s"></span></a>
<span class="s"><button onclick="CommentSystem.setReply('{{comment.slug | urlencode}}', '{{comment.author | urlencode}}');"><span class="n">Reply</span><span class="s"></span></button></span>
<span class="s"></span></div></span>
<span class="s"><h4>
{% <span class="k">if</span> <span class="nb">comment</span>.<span class="n">metadata</span>[<span class="s">'website'</span>] %}
<span class="s"><a href="{{comment.metadata['website']}}"></a></span><a href="{{comment.metadata['website']}}">{{ <span class="nb">comment</span>.<span class="n">author</span> }}<span class="s"></span></a>
{% <span class="k">else</span> %}
{{ <span class="nb">comment</span>.<span class="n">author</span> }}
{% <span class="n">endif</span> %}
<span class="s"></span></h4></span>
<span class="s"><p>
<span class="s"> Posted on</span>
<span class="s"> <time datetime="{{ comment.date.isoformat() }}" title="{{ comment.date.isoformat() }}"></time></span>{{ <span class="nb">comment</span>.<span class="n">locale_date</span> }}<span class="s"></span>
<span class="s"></span></p></span>
<span class="s"><div as="" class="pcs-comment-content" comments.js#}="" id="" in="" used="" {#="">
{{ <span class="nb">comment</span>.<span class="n">content</span> }}
<span class="s"></span></div></span>
{% <span class="k">if</span> <span class="nb">comment</span>.<span class="n">replies</span> %}
<span class="s"><hr/></span>
<span class="s"><ul>
{{ <span class="k">loop</span>(<span class="nb">comment</span>.<span class="n">replies</span>) }}
<span class="s"></span></ul></span>
{% <span class="n">endif</span> %}
<span class="s"></span></div></span>
<span class="s"></span></li></span>
{% <span class="n">endfor</span> %}
<span class="s"></span></ul></span>
{% <span class="k">else</span> %}
<span class="s"><p>There are no comments yet.</p></span>
{% <span class="n">endif</span> %}
{{ <span class="n">comments_form</span>() }}
<span class="s"></span></section></span>
{% <span class="n">endif</span> %}
{% <span class="n">endmacro</span> %}
{% <span class="n">macro</span> <span class="n">comments_js</span>(<span class="n">user</span>, <span class="n">domain</span>, <span class="n">includeJquery</span>=<span class="nb">True</span>) %}
{% <span class="k">if</span> <span class="n">PELICAN_COMMENT_SYSTEM</span> %}
{% <span class="k">if</span> <span class="n">includeJquery</span> %}
<span class="s"><script src="http://code.jquery.com/jquery-2.1.4.min.js" type="text/javascript"></script></span>
{% <span class="n">endif</span> %}
<span class="s"><script src="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/js/comments.js" type="text/javascript"></script></span>
<span class="s"><script type="text/javascript"></span>
$(<span class="n">document</span>).<span class="nb">ready</span>(<span class="n">function</span>() {
<span class="n">CommentSystem</span>.<span class="n">email_user</span> = <span class="s">"{{ user }}"</span>;
<span class="n">CommentSystem</span>.<span class="n">email_domain</span> = <span class="s">"{{ domain }}"</span>;
<span class="n">CommentSystem</span>.<span class="n">display_replyto_html</span> = <span class="n">function</span>(<span class="n">comment_content</span>, <span class="n">article_slug</span>, <span class="n">author</span>) {
<span class="k">return</span> <span class="s">''</span>
+ <span class="s">'<button style="float:right;" onclick="CommentSystem.cancelReply(); return false;" title="Cancel the reply">'</span>
+ <span class="s">'×'</span>
+ <span class="s">'</button>'</span>
+ <span class="s">'<p>This comment will be posted as a reply to \'<a title="'</span>+<span class="n">comment_content</span>+<span class="s">'" href="#comment-'</span>+<span class="n">article_slug</span>+<span class="s">'">'</span>+<span class="n">author</span>+<span class="s">'</a>\'</p>'</span>;
};
$(<span class="s">'#pcs-comment-form'</span>).<span class="n">on</span>(<span class="s">"submit"</span>,
<span class="n">function</span>( <span class="n">event</span> )
{
<span class="n">event</span>.<span class="n">preventDefault</span>();
$(<span class="n">location</span>).<span class="n">attr</span>(<span class="s">'href'</span>, <span class="n">CommentSystem</span>.<span class="n">getMailtoLink</span>(<span class="s">"{{ article.slug }}"</span>));
}
);
});
<span class="s"></script></span>
{% <span class="n">endif</span> %}
{% <span class="n">endmacro</span> %}
{% <span class="n">macro</span> <span class="n">comments_quickstart</span>(<span class="n">user</span>, <span class="n">domain</span>) %}
{{ <span class="n">comments_styles</span>() }}
{{ <span class="n">comments_with_form</span>() }}
{{ <span class="n">comments_js</span>(<span class="n">user</span>, <span class="n">domain</span>) }}
{% <span class="n">endmacro</span> %}
</code></pre></div>
<h2>What A Comment File Looks Like</h2>
<p>When a user submits a comment, you will get an email with the details. You then
take those details from your email and create a text file within your Pelican
site, one for each comment. By default, the plugin will look for comments in a
folder <code>comments</code> in your root content folder (probably the same one your have
your Pelican articles in), and then in subfolders that match the slug of the
article the comment applies to.</p>
<p>The actual comment file will look something like this:</p>
<div class="highlight"><pre><span></span><code>email: noreplay@blogger.com
date: 2019-07-15T12:20+01:00
author: Mahassine
replyto: comment-slug-2382md
Sample comment body.
<!-- content/comments/article-slug/comment-slug-2342.md -->
</code></pre></div>
<p>The <code>replyto</code> tag is only needed if this comment is indeed a reply to another
comment. The value of the <code>replyto</code> tag is the slug of the comment, which is
the filename plus the file extension, but not the period between them.</p>
<p>The comment files can be in any format Pelican is set up to read (typically
Markdown and ReStructed Text, but many others supported).</p>
<p>I realize that this is fairly involved to activate as far as Pelican plugins
go, so if you run into issues, please leave a comment on this post!</p>
<h2>Upgrading (from the Pelican Comment System)</h2>
<p>Upgrading from the <em>Pelican Comment System</em> should be seemless, and should be
as simple as uninstalling the <em>Pelican Comment System</em> (and removing it from
your <code>pelicanconf.py</code>) and installing <em>Static Comments</em>.</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>uninstall<span class="w"> </span>pelican-comment-system
pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.static-comments<span class="w"> </span>--upgrade
</code></pre></div>
<p>Existing comments files should work out of the box, and the setting haven’t
been renamed.</p>
<h2>Known Issues</h2>
<ul>
<li>To get this to work, you’ll need to update your theme (or find one, like
<a href="https://blog.minchin.ca/category/seafoam/">Seafoam</a>, that works out of the
box). I appologize for how involved this is, but Pelican doesn’t have any
framework for editing themes from plugins.</li>
<li>The current setup requires JavaScript, and there probably isn’t a great “no
script” solution. As an alternative, you can ask people to just email you
their comments directly and offer to post them. I actually do a version of
this on my <a href="http://minchin.ca/genealogy/">Genealogy site</a> and get a rather
consistent stream of emails, so it may be a viable option.</li>
<li>You have to manually add comments to your site, regenerate it, and reupload
it before the comments will show on your site. This means that comments won’t
immediately show, and the the workflow may not be feasible if you have a high
comment volume.</li>
<li>The documentation that is on the Github repo has been reviewed, but may still
be out of date in places. Please let me know if you notice any issues there.</li>
<li>If you are moving your site to Pelican from another system, importing your
existing comments can be rather involved. There is an included script for
Blogger comments that I used myself, but because such imports tend to be
one-offs, these scripts don’t get checked that often and so could break if
the export format changes. If you have a significant volume of comments you
want to import, you may have to write your own script (which I’d be happy to
include with the plugin if you send me a pull request!).</li>
</ul>
<h2>Future Plans</h2>
<p>At this point, the plugin seems feature complete. I expect future changes will
be about fixing code errors or to keep it working as Pelican progresses.</p>
<h2>Personal Thoughts</h2>
<p>I’m excited to get this updated and out into the world. I’m a little sad that
the old <em>Pelican Comment System</em> seems to be no longer being updated, although
it looks like it got stuck halfway through a “version 2” complete rewrite, so
add this as another warning about ground up rewrites. But this is the wonder of
Open Source: I can take the existing codebase, fix the errors and issues, and
release a new working version back into the world.</p>
<p>There is also a more general question of whether comments are worth keeping
around. Considering that you’re reading this, and I released this plugin, I
think we are both in agreement that the answer is “yes”. I have definitely seen
a the number of comments posted to my blog drop over the years (this blog has
been up since 2006!), but I suspect that is mostly tied to lower traffic
volumes. As for comments generally, Twitter and Reddit I think provides proof
that people still want to add their two cents on things, and personally, I
would rather have the conversion here rather than on another site (like Reddit)
that I don’t control and have the ability to backup that conversation.</p>
<p>Overall, I’m pretty satisfied with this solution. The biggest downside is that
comments don’t post automatically and so can take some time to show as I have
to manually post them, but I think that tradeoff is worth not having to
maintain a separate server just for commenting.</p>
<p>As with all my plugins, if the <em>pelican-plugins</em> group wants to adopt these,
I’d be happy to have the community support there.</p></body></html>AutoLoader Plugin 1.1.0 for Pelican Released2022-04-09T11:14:00-06:002022-04-10T19:56:00-06:00Wm. Minchintag:blog.minchin.ca,2022-04-09:/2022/04/autoloader-112.html<p><em>AutoLoader</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>AutoLoader</em> is a “meta plugin” in that it doesn’t directly affect your Pelican
site, but rather works to make your other plugins better. By way of background,
Pelican 4.5 added the ability to autoload plugins that exist in the
<code>pelican.plugins</code> namespace. This plugin allows you to extend this autoload
ability to any arbitrary namespace. In particlar, it defaults to extending this
ability to my <code
markdown=1>minchin.​pelican.​plugins</code>
namespace, and thus will autoload my other plugins, if installed. It can also
be used to add plugin autoloading to earlier version of Pelican.</p>
<html><head></head><body><p><em>AutoLoader</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>AutoLoader</em> is a “meta plugin” in that it doesn’t directly affect your Pelican
site, but rather works to make your other plugins better. By way of background,
Pelican 4.5 added the ability to autoload plugins that exist in the
<code>pelican.plugins</code> namespace. This plugin allows you to extend this autoload
ability to any arbitrary namespace. In particlar, it defaults to extending this
ability to my <code markdown="1">minchin.pelican.plugins</code>
namespace, and thus will autoload my other plugins, if installed. It can also
be used to add plugin autoloading to earlier version of Pelican.</p>
<h2>This Release</h2>
<p>This release adds the ability to disable auto-loading of specific plugins. In
particular, it defaults to no longer trying to load <code markdown="1">pelican.plugins.signals</code> and
<code>pelican.plugins._utils</code> which are modules within the <code>pelican.plugins</code>
namespace, but are not actually plugins.</p>
<h2>Upgrading</h2>
<p>The simplest way to upgrade (or install) <em>AutoLoader</em> is to use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.autoloader<span class="w"> </span>--upgrade
</code></pre></div>
<p>No configuration changes are needed.</p>
<p>If you want to use these new features, define <code>AUTOLOADER_PLUGIN_BLACKLIST</code> in
your <code>pelicanconf.py</code>:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="kn">from</span> <span class="nn">minchin.pelican.plugins</span> <span class="kn">import</span> <span class="n">autoloader</span>
<span class="n">AUTOLOADER_PLUGIN_BLACKLIST</span> <span class="o">=</span> <span class="n">autoloader</span><span class="o">.</span><span class="n">DEFAULT_PLUGIN_BLACKLIST</span> <span class="o">+</span> <span class="p">[</span>
<span class="s2">"pelican.plugins.misbehaving_plugin"</span><span class="p">,</span>
<span class="c1"># other plugins</span>
<span class="p">]</span>
</code></pre></div>
<h2>Known Issues</h2>
<ul>
<li>the release machinery used relies on <code>invoke</code>, but an update has been pushed
with now supports Python 3.10.</li>
<li>plugins activated by <em>AutoLoader</em> do not show running <code>pelican-plugins</code>.
(<code>pelican-plugins</code> was a script added by Pelican 4.5 to show namespace
plugins currently active). <em>AutoLoader</em> however should be itself listed
(although likely as <code>pelican.plugins.autoloader</code>).</li>
</ul></body></html>Covid’s Second Christmas2022-01-04T17:11:00-07:002022-01-04T17:34:00-07:00Wm. Minchintag:blog.minchin.ca,2022-01-04:/2022/01/covids-second-christmas.html<p>We are now finished up the second Christmas season overshadowed by Covid (and
hopefully the last!).</p>
<p>On one hand, it provides a nice excuse to stay home and have a quiet holiday.
But it’s also been almost two years since we’ve been able to visit some
extended family due to border closures. We ended up in the middle: we had some
extended family travel this way, and other family gatherings happened, although
with several absences due to people in Covid quarantine.</p>
<html><head></head><body><p>We are now finished up the second Christmas season overshadowed by Covid (and
hopefully the last!).</p>
<p>On one hand, it provides a nice excuse to stay home and have a quiet holiday.
But it’s also been almost two years since we’ve been able to visit some
extended family due to border closures. We ended up in the middle: we had some
extended family travel this way, and other family gatherings happened, although
with several absences due to people in Covid quarantine.</p>
<p>The Christmas/holiday season has been extended by at least a week, as they
kids’ school has been closed for this week to try and help control the Covid
spread. It’s nice to have them home, but the logistics of it get a little hairy
with both parents working….</p>
<p>On a non-Covid note, this Christmas season has been frightening cold. I think
we’re going on two weeks now without a single day about -20°C. The good news
is that the car even managed to start at -38°C! Although it is finally
forecast to get to 0°C next week. :)</p>
<p>Looking forward, we’re seriously looking at an international trip this summer.
We’re sort of betting that the Covid situration will be mostly under control
both ‘here’ and ‘there’ in six months. We actually had our flights selected two
weeks ago, but the night before I went to put my money down, the Covid rules
changed, meaning that wouldn’t be able to take our full trip proposed under the
new rules. We reviewed the cancellation rules and figure the Covid rules
will change (several times) before our trip, so we’re going to lock down
tickets as soon as the travel agent returns from holidays.</p>
<p>More broadly, one of the longer-term Covid effects — its destructive effect on
individual’s religious faith — is sadly starting to show. When Covid first
struck, I figured that if Covid lockdowns (and thus only virtual church
meetings) went on for sometime, some of the congregation members would drift
away. For some, the (physical) church meetings was the extent of the religious
expression in the best of times. But for many others, meeting with and being
physically present with their fellow believers served to regularly strengthen
their faith, to find faithful answers the questions that arose from daily life,
and to help put their doubts behind them; to be cut off from this strength and
continue faithful requires developing and maintaining an independent, internal
source of strength and faith. Against this background, I’ve had several friends
(4 come to mind in the last month or so) that have declared their departure
from the Faith; none of them have even hinted that Covid was what brought this
about, but that is the backdrop these things are happening against. To have
predicted this doesn’t mean it’s still not sad to see it happen.</p>
<p>Hopefully, the Covid will soon be behind us. May warm days soon be yours.</p></body></html>AutoLoader Plugin 1.0.2 for Pelican Released2021-10-25T20:29:00-06:002021-10-25T20:29:00-06:00Wm. Minchintag:blog.minchin.ca,2021-10-25:/2021/10/autoloader-102.html<p><em>AutoLoader</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>AutoLoader</em> is a “meta plugin” in that it doesn’t directly affect your Pelican
site, but rather works to make your other plugins better. By way of background,
Pelican 4.5 added the ability to autoload plugins that exist in the
<code>pelican.plugins</code> namespace. This plugin allows you to extend this autoload
ability to any arbitrary namespace. In particlar, it defaults to extending this
ability to my <code markdown=1>minchin.​pelican.​plugins</code> namespace, and thus will autoload my
other plugins, if installed. It can also be used to add plugin autoloading to
earlier version of Pelican.</p>
<html><head></head><body><p><em>AutoLoader</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>AutoLoader</em> is a “meta plugin” in that it doesn’t directly affect your Pelican
site, but rather works to make your other plugins better. By way of background,
Pelican 4.5 added the ability to autoload plugins that exist in the
<code>pelican.plugins</code> namespace. This plugin allows you to extend this autoload
ability to any arbitrary namespace. In particlar, it defaults to extending this
ability to my <code markdown="1">minchin.pelican.plugins</code> namespace, and thus will autoload my
other plugins, if installed. It can also be used to add plugin autoloading to
earlier version of Pelican.</p>
<p>Personally, these two abilities (to autoload my other plugins and to add
autoloading to older versions of Pelican) are significant, because I am
currently in the process of upgrading from Pelican 3.7 to the current version
(4.7) and what was holding me back was the effort to move my plugins from their
current <code markdown="1">minchin.pelican.plugins</code> namespace. With <em>AutoLoader</em>, it makes it
simple to upgrade my Pelican version, regardless of whether the plugins have
(yet? ever?) moved namespaces. It also allows me to continue creating
custom/personal versions of plugins in my namespace, particularly in cases
where the original author is no longer updating their plugin.</p>
<h2>Installation</h2>
<p>The simplest way to install <em>AutoLoader</em> is to use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.autoloader
</code></pre></div>
<h2>Configuration</h2>
<p>If you are running Pelican 4.5 or newer, and haven’t manually defined <code>PLUGINS</code>
in your <code>pelicanconf.py</code> site configuration file, nothing more is needed:
<em>AutoLoader</em> will autoload itself :) Without further configuration, it will
autoload any installed plugins in the <code markdown="1">minchin.pelican.plugins</code> namespace (and
Pelican itself will autoload any installed plugins in the <code>pelican.plugins</code>
namespace).</p>
<p>If you are using a older version of Pelican (i.e. before v4.5) and/or you have
defined <code>PLUGINS</code>, you’ll need to add <em>AutoLoader</em> to your list of plugins,
like this:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="kn">from</span> <span class="nn">minchin.pelican.plugins</span> <span class="kn">import</span> <span class="n">autoloader</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># others...</span>
<span class="n">autoloader</span><span class="p">,</span>
<span class="p">]</span>
</code></pre></div>
<p>If you want to add additional namespaces for <em>AutoLoader</em> to work from, define
<code>AUTOLOADER_NAMESPACES</code> (as a list or other iterable) in your <code>pelicanconf.py</code>
file. For example, if you want to autoload the <code>pelican.plugins</code> namespace
(useful if you’re still using Pelican 3.7 or 4.2 or have defined <code>PLUGINS</code>, and
the configuration this site is currently using):</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="kn">from</span> <span class="nn">minchin.pelican.plugins</span> <span class="kn">import</span> <span class="n">autoloader</span>
<span class="n">AUTOLOADER_NAMESPACES</span> <span class="o">=</span> <span class="n">autoloader</span><span class="o">.</span><span class="n">DEFAULT_NAMESPACE_LIST</span> <span class="o">+</span> <span class="p">[</span>
<span class="s2">"pelican.plugins"</span><span class="p">,</span>
<span class="c1"># other namespaces</span>
<span class="p">]</span>
</code></pre></div>
<h2>This Release</h2>
<p>v1.0.2 is the first public release. Earlier version numbers got “eaten”
making sure the version pushed to PyPI would work as expected.</p>
<h2>Known Issues</h2>
<ul>
<li>the release machinery used relies on <code>invoke</code>, which has not yet been updated
to be compatible with Python 3.10. Beyond pushing out releases, this plugin
should work with Python 3.10.</li>
<li>plugins activated by <em>AutoLoader</em> do not show running <code>pelican-plugins</code>.
(<code>pelican-plugins</code> was a script added by Pelican 4.5 to show namespace
plugins currently active). <em>AutoLoader</em> however should be itself listed
(although likely as <code>pelican.plugins.autoloader</code>).</li>
</ul>
<h2>Forward Looking Thoughts</h2>
<p>In my <a href="https://blog.minchin.ca/2021/05/image-process-211.html">last plugin release post</a>, I’d
commented that there were “only 10 more plugins to go!” (i.e. to move to the
<code>pelican.plugins</code> namespace, and presumably under the
<a href="https://github.com/pelican-plugins/">Pelican-Plugins</a> organization on GitHub).
This handily sidesteps those transitions from keeping me from upgrading Pelican
on my personal site! I’m feeling much closer to getting this site upgraded to
the current version of Pelican.</p>
<p>I plan on making releases of my other plugins shortly to rely on this and to
update the installation instructions to that end.</p></body></html>Updating a Python Package After 5 Years, and Colourettu 2.1.1. Released2021-10-06T14:46:00-06:002021-10-06T14:46:00-06:00Wm. Minchintag:blog.minchin.ca,2021-10-06:/2021/10/colourettu-221.htmlSo over the weekend as a Hackoberfest project, I decided to update a project
that hadn’t been touched in five years.<html><head></head><body><p>So over the weekend as a Hackoberfest project, I decided to update a project
that hadn’t been touched in five years.</p>
<p>That project is <a href="http://minchin.ca/colourettu/">Colourettu</a>, I library I wrote
several years back to do some basic colour theme management. The last release
was <a href="https://blog.minchin.ca/2016/11/colourettu-2-released.html">version 2.0.0</a>, released in
November 2016, basically five years ago. If feels a little bit like cheating to
work on my own project for Hackoberfest, but it is officially allowed and the
project could use a little love, and it is Open Source. So I don’t feel too bad
about it.</p>
<p>Five years ago, Python 3.5 was the most recent Python version out, and the
project had recently dropped official 2.6 support, but was still officially
supporting Python 2.7. Today, the oldest version of Python supported is 3.6,
and version 3.10 just came out a few days ago. Also, I’m not longer worried
about supporting Python 2.</p>
<p>But even having gone through that many Python updates, I actually didn’t need
to make <strong><em>any</em></strong> changes to actual code base. Most of the changes I needed to do
were actually related to the support services around the project. The most
significant was the switch from Travis-<span class="caps">CI</span> to GitHub Actions for automated
testing. Five years ago, Travis was basically the best and only way to do
automated testing on pull requests; today it’s faded away. Although it was
still technically online today, I couldn’t figure out how to get Travis to work
at all. And without an obvious free option with Travis, it seemed like a good
time to move to GitHub Actions. Setting up GitHub Actions proved rather
straightforward, with the only missing piece being how to mark a particular
case as an “allowed failure” (readers, any suggestions?).</p>
<p>There were a couple of other random updates: a couple of private forks for
packages used by the documentation site had to be updated to provide <span class="caps">PEP</span>-440
compatible version numbers. I also had to adjust the format of math formulas in
the documentation to make Sphinx 4 happy. I also updated the release script; I
was running an earlier version of <a href="https://github.com/MinchinWeb/minchin.releaser" title="Minchin dot Releaser">minchin.releaser</a> that I’ve since updated
and packaged. The most changes were due to <em>black-ify-ing</em> the codebase.</p>
<p>So I was happily surprised at how well this “old” Python code ran; hopefully
the next five years will prove as boring!</p>
<h2 id="changelog">Changelog</h2>
<p>See my <a href="https://blog.minchin.ca/2016/11/colourettu-2-released.html">previous post</a> for
earlier changelog entries.</p>
<h3>Version 2.1.1 — October 5, 2021</h3>
<ul>
<li><em>bug</em> Documentation: fix formula rendering for Sphinx 4.2</li>
</ul>
<h3>Version 2.1.0 — October 5, 2021</h3>
<ul>
<li><em>feature</em> <a href="https://github.com/MinchinWeb/colourettu/pull/148">Pull Request 148</a> various updates to ensure that
the package is still installable (and hackable) on current versions of Python</li>
<li><em>bug</em> <a href="https://github.com/MinchinWeb/colourettu/pull/148">Pull Request 148</a> proofread documentation</li>
<li><em>support</em> <a href="https://github.com/MinchinWeb/colourettu/issues/7">Issue 7</a> upgrade to <a href="https://github.com/MinchinWeb/minchin.releaser" title="Minchin dot Releaser">minchin.releaser</a> package.
Colourettu was previously using an early vendorized version of this.</li>
<li><em>support</em> <a href="https://github.com/MinchinWeb/colourettu/pull/147">Pull Request 147</a> update minimum versions of
several dependencies to remove support for version with known security issues.</li>
<li><em>support</em> <a href="https://github.com/MinchinWeb/colourettu/pull/148">Pull Request 148</a> drop official support for
Python < 3.6, including dropping support for Python 2. I haven’t changed
anything in the codebase that I expect will break these earlier versions, but
I’m no longer testing against them.</li>
<li><em>support</em> <a href="https://github.com/MinchinWeb/colourettu/pull/148">Pull Request 148</a> switch to personal fork of
<a href="https://github.com/MinchinWeb/PSphinxTheme/tree/colourettu">PSphinxTheme</a> and <a href="https://github.com/MinchinWeb/python_lconf_lexer/tree/colourettu">lconf lexer</a> (i.e. basically
the documentation theme) as to provide versions that can be installed with
current versions of <em>pip</em>. Specifically, these private version provide
<span class="caps">PEP440</span>-style version numbers.</li>
<li><em>support</em> <a href="https://github.com/MinchinWeb/colourettu/pull/148">Pull Request 148</a> black-ify codebase</li>
<li><em>support</em> <a href="https://github.com/MinchinWeb/colourettu/pull/148">Pull Request 148</a> update <em>isort</em> to v5</li>
<li><em>support</em> <a href="https://github.com/MinchinWeb/colourettu/pull/149">Pull Request 149</a> swap from Travis-<span class="caps">CI</span> to GitHub
Actions for Continuous Integration</li>
</ul></body></html>Advanced Pelican: Self-Configuring Themes, and Seafoam 2.6.0 Released2021-07-05T15:58:00-06:002021-07-07T10:49:00-06:00Wm. Minchintag:blog.minchin.ca,2021-07-05:/2021/07/seafoam-260.html<p>I’ve just released a groundbreaking version of my website theme <em>Seafoam</em>; I
believe this is the first Pelican theme designed to configure itself!</p>
<p>The root of this <em>magic</em> is that the theme has actually been packaged as a
namespace plugin for Pelican. If you’re running a sufficiently recent version
of Pelican (i.e. v4.5 or newer), Pelican will automatically load the <em>Seafoam</em>
plugin; if you’re using an older version of Pelican (or other, non-namespace
plugins), you’ll need to add the <em>Seafoam</em> plugin to you configuration:</p>
<html><head></head><body><p>I’ve just released a groundbreaking version of my website theme <em>Seafoam</em>; I
believe this is the first Pelican theme designed to configure itself!</p>
<p>The root of this <em>magic</em> is that the theme has actually been packaged as a
namespace plugin for Pelican. If you’re running a sufficiently recent version
of Pelican (i.e. v4.5 or newer), Pelican will automatically load the <em>Seafoam</em>
plugin; if you’re using an older version of Pelican (or other, non-namespace
plugins), you’ll need to add the <em>Seafoam</em> plugin to you configuration:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"pelican.plugins.seafoam"</span><span class="p">,</span>
<span class="c1"># others, as needed/desired</span>
<span class="p">]</span>
<span class="c1"># the rest of your configuration file</span>
</code></pre></div>
<p>Unless you need to change <em>Seafoam</em>‘s behaviour from its defaults, you
shouldn’t need do any further configuration!</p>
<h2>Shiny! How Do I Do This For My Theme?</h2>
<h3>Start With a Namespace Plugin</h3>
<p>A namespace plugin works its magic by have the code files in the right folder
(i.e. “namespace”). For Pelican, we want those code files under
<code>pelican/plugins</code>. So our file layout looks like this:</p>
<div class="highlight"><pre><span></span><code><project root="">
+- pelican
| `- plugins
| `- my_theme
| `- __init__.py
`- setup.py
</project></code></pre></div>
<p>Of particular note is that both the <code>pelican</code> and <code>plugins</code> directories have
no Python code directly in them.</p>
<h3>Add Your Theme Files</h3>
<p>For this article, I’m going to treat the actual creation of a theme as
out-of-scope and assume you already have a theme. Your theme will consist of a
<code>templates</code> folder, containing the Jinja2<sup id="fnref:jinja2"><a class="footnote-ref" href="#fn:jinja2">1</a></sup> templates with <code>.html</code> file
extensions, and possibly a <code>static</code> folder containing the theme’s static assets
(images, <span class="caps">CSS</span>, and JavaScript). Add these as a subfolder of your theme. So now
our file layout looks like this (actual static files not shown):</p>
<div class="highlight"><pre><span></span><code><project root="">
+- pelican
| `- plugins
| `- my_theme
| + static
| | +- css
| | +- js
| | `- images
| +- templates
| | +- index.html
| | `- <other templates="">
| `- __init__.py
`- setup.py
</other></project></code></pre></div>
<h3>Find Our Templates</h3>
<p>Next, we’re going to add a function to our theme’s <code>__init__.py</code> file that will
point to the location of our theme:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelican/plugins/my_theme/__init__.py</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="k">def</span> <span class="nf">get_path</span><span class="p">():</span>
<span class="c1"># Theme directory is defined as our parent directory</span>
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">Path</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span>
</code></pre></div>
<p>If we didn’t do anything more, in our <code>pelicanconf.py</code>, we could:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="kn">from</span> <span class="nn">pelican.plugins</span> <span class="kn">import</span> <span class="n">my_theme</span>
<span class="n">THEME</span> <span class="o">=</span> <span class="n">my_theme</span><span class="o">.</span><span class="n">get_path</span><span class="p">()</span>
</code></pre></div>
<p>(And this is exactly how <em>Seafoam</em> worked previously.)</p>
<h3>Automatically Activate Our Theme</h3>
<p>Now we get into the real <em>magic</em>, to which there are two parts: 1) a function
to set the theme, and 2) hook that function into Pelican’s signals.</p>
<p>The function to set the theme is rather simple, but note that we set
<code>pelican_obj.theme</code> directly rather than <code>pelican_obj.settings["THEME"]</code>, as
this setting appears to already have been read.</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelican/plugins/my_theme/__init__.py</span>
<span class="k">def</span> <span class="nf">set_theme</span><span class="p">(</span><span class="n">pelican_obj</span><span class="p">):</span>
<span class="n">pelican_obj</span><span class="o">.</span><span class="n">theme</span> <span class="o">=</span> <span class="n">get_path</span><span class="p">()</span>
</code></pre></div>
<p>If we wanted to, we could extend the above function to massage Pelican’s
settings (this is where <em>Seafoam</em> sets its defaults).</p>
<p>For signals, these are “hooks” that allow you to run custom code as Pelican
goes through the process of generating your site. We’re going to hook into the
very first signal that Pelican offers:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelican/plugins/my_theme/__init__.py</span>
<span class="kn">from</span> <span class="nn">pelican</span> <span class="kn">import</span> <span class="n">signals</span>
<span class="k">def</span> <span class="nf">register</span><span class="p">():</span>
<span class="n">signals</span><span class="o">.</span><span class="n">initialized</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">set_theme</span><span class="p">)</span>
</code></pre></div>
<h3>Complete Python Code</h3>
<p>Adding a couple of niceties (logging, a version number) and above, we end up
with something like this:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelican/plugins/my_theme/__init__.py</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="kn">from</span> <span class="nn">pelican</span> <span class="kn">import</span> <span class="n">signals</span>
<span class="n">__version__</span> <span class="o">=</span> <span class="s2">"1.0.0"</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">get_logger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_path</span><span class="p">():</span>
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">Path</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">set_theme</span><span class="p">(</span><span class="n">pelican_obj</span><span class="p">):</span>
<span class="n">pelican_obj</span><span class="o">.</span><span class="n">theme</span> <span class="o">=</span> <span class="n">get_path</span><span class="p">()</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">"[My Theme] Theme set!"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">register</span><span class="p">():</span>
<span class="n">signals</span><span class="o">.</span><span class="n">initialized</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">set_theme</span><span class="p">)</span>
</code></pre></div>
<h3>Tips for Publishing</h3>
<p>When I went to publish the updated package to PyPI, I found I had to switch
from <code>setuptools.find_packages()</code> to <code>setuptools.find_namespace_packages()</code> in
my theme’s <code>setup.py</code>. I also had to add the non-Python files to my
<code>MANIFEST.in</code> file:</p>
<div class="highlight"><pre><span></span><code><span class="gh">#</span> MANIFEST.in
recursive-include pelican/plugins/seafoam/static <span class="gs">*.*</span>
recursive-include pelican/plugins/seafoam/templates <span class="gs">*.*</span>
</code></pre></div>
<p>Finally, be sure to include the <code>Framework :: Pelican :: Themes</code> PyPI
classifier so others can find your work!</p>
<p>If I missed something in this mini-tutorial, but sure to leave a comment or
send me an email so I can expand/fix it.</p>
<h2>Upgrading</h2>
<p>Upgrading <em>should</em> is straight forward; I haven’t broken anything on purpose
since v2.0.0 came out. With this release several previously mandatory settings
are now optional to specify and so you can remove them if you want, but keeping
them in place shouldn’t cause any problems.</p>
<p>To install or to upgrade, you can use pip:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>seafoam<span class="w"> </span>--upgrade
</code></pre></div>
<p>If you’re already running Pelican v4.5 (or newer) <strong>and</strong> only using namespace
plugins, then the required plugins will automatically load. However, most will
have to update your <code>pelicanconf.py</code> to point to the new plugin names:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"pelican.plugins.seafoam"</span><span class="p">,</span>
<span class="s2">"pelican.plugins.jinja_filters"</span><span class="p">,</span> <span class="c1"># <-- optional now</span>
<span class="s2">"pelican.plugins.image_process"</span><span class="p">,</span> <span class="c1"># <-- optional now</span>
<span class="c1"># others, as desired...</span>
<span class="p">]</span>
</code></pre></div>
<p>All other settings, if it was just to apply the default settings for <em>Seafoam</em>,
can now be removed.</p>
<p>To be clear, <em>Seafoam</em> still supports Pelican 3 (i.e. you don’t need to upgrade
to Pelican 4.5 quite yet).</p>
<h2 id="development-issues">Development Issues</h2>
<p>This is just a section where I make some notes on the issues I ran into while
working on the code.</p>
<p><span class="caps">PEP517</span> <span class="amp">&</span> <span class="caps">PEP518</span> are accepted extensions to Python dealing with the ability to
specify a “build backend”, i.e. how does a given module package itself up for
distribution. The way to do this is to add a few lines to your <code>pyproject.toml</code>
file (the new place to collect settings and metadata about your Python project)
like so:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pyproject.toml</span>
<span class="k">[build-system]</span>
<span class="n">requires</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">"setuptools >= 40.6.0"</span><span class="p">,</span><span class="w"> </span><span class="s2">"wheel"</span><span class="p">]</span>
<span class="n">build-backend</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"setuptools.build_meta"</span>
</code></pre></div>
<p>I figured I would do this to this latest version of <em>Seafoam</em> as this is <em>the
way of the future</em> there really wasn’t any obvious downside. However, I
discovered a downside: when you go to build such a package from source, it
wants to create a isolated environment for doing so, and as part of that, it
wants to download <em>setuptools</em> and <em>wheel</em> (really, whatever you specify in the
<code>build-system</code> key) and doesn’t seem to rely on either the system libraries or
the system’s <em>pip</em> download cache. Normally, this wouldn’t be an issue, but if
you try and install such a package from the Test PyPI server, it also tries to
download and install the build requirements from there too! And many packages,
including <em>setuptools</em> are never posted to the Test PyPI server.</p>
<p>There may be a workaround, but I have somewhat limited internet at the moment
so I haven’t been able to research the issue. I should probably raise an issue, somewhere….</p>
<p><strong>Summary</strong>: Source distributions with a <span class="caps">PEP517</span>/518 <em>setuptools</em> build system
are unable to be installed from the Test PyPI server.</p>
<p><strong>Update (July 7th)</strong>: The answer is to call <em>pip</em> with <code>--no-build-isolation</code>.
This relies on the environment being installed into to provide the needed
packages to actually do the install, and as a byproduct allows offline installs
and installs when the “main” package is hosted on the Test PyPI server.</p>
<p>See this <a href="https://stackoverflow.com/a/67595330/4276230">Stack Overflow answer</a>,
which points to the <em>terse to the point of being cryptic</em> <a href="https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-no-build-isolation">pip
documentation</a>.</p>
<h2 id="future-plans">Future Plans</h2>
<p>This release actually resolves mosts of the <a href="https://blog.minchin.ca/2021/05/seafoam-250.html#future-plans">future
plans</a> I’d written for
the v2.5 release. At this point, further development will likely be tied to my
website demands directly.</p>
<p>One thing I thought about doing was to make the tag pages (called “labels” on
this site) more powerful by adding the ability to add a custom text header to
them. For example, the current “homepage” link for <em>Seafoam</em> is given as such a
tag page (<a href="http://blog.minchin.ca/label/seafoam/">http://blog.minchin.ca/label/seafoam/</a>) and this would be handy to
do for all my Python projects, as most of the project history is already
collected here as blog posts. I haven’t begun to look at this, so I’m not sure
if it could be done from the Jinja templates alone, or if it would require
adjusting the Pelican output with an extention, either as part of <em>Seafoam</em> or
a separate plugin.</p>
<p>Also, as I <a href="https://blog.minchin.ca/2020/07/seafoam-245-released.html#future-plans">wrote
previously</a>, there are
long term plans to upgrade to Bootstrap 5, but nothing started there.</p>
<h2 id="changelog">Changelog</h2>
<p>See my <a href="https://blog.minchin.ca/2021/05/seafoam-250.html#changelog">previous post</a> for
earlier changelog entries.</p>
<h3>Version 2.6.0 — July 5, 2021</h3>
<ul>
<li><em>feature</em> add internal plugin. This will allow the theme to automatically
configure and activate itself. Should significantly reduced installation
complexity. You may be able to completely remove the configuration you have
in place for the plugin.</li>
<li><em>feature</em> include <em>Seafoam</em> version in source <span class="caps">HTML</span> of generated sites</li>
<li><em>bug</em> adjust <span class="caps">HTML</span> to add the <code>.table</code> class where needed, rather
than applying the formatting to all <span class="caps">HTML</span> tables. Effectively a re-work of v.2.4.7.</li>
<li><em>support</em> add screenshots. See issues #1 and #18.</li>
<li><em>support</em> updated <code>setup.py</code>. Include tempalate and static files at new location.</li>
<li><em>support</em> no longer include raw <span class="caps">LESS</span> files in distributions or in
generated sites.</li>
<li><em>support</em> now also requires <code>beautifulsoup4</code> and <code>semantic_version</code></li>
</ul>
<div class="footnote">
<hr/>
<ol>
<li id="fn:jinja2">
<p>Jinja version 3 has now been released, so what does this package go
by now? <em>Jinja3</em>? <em>Jinja2 v3</em>? plain old <em>Jinja</em>? In any case, <code>jinja2</code> seems
to install version 3 off of PyPI. <a class="footnote-backref" href="#fnref:jinja2" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
</ol>
</div></body></html>Covid Vaccine — 2 of 22021-06-28T20:44:00-06:002021-06-28T20:44:00-06:00Wm. Minchintag:blog.minchin.ca,2021-06-28:/2021/06/covid-vaccine-2.htmlSo this last Friday I got my second Covid vaccine shot!<html><head></head><body><p>So this last Friday I got my second Covid vaccine shot!</p>
<p>I went out of town again because the scheduling work well and we could get in
right away. This time, the weather was nice (no rain like <a href="https://blog.minchin.ca/2021/05/covid-vaccine-1.html">last
time</a>!), and so it made for a beautiful
drive (see the header photo).</p>
<p>This turned out to be a lot sooner than the “<a href="https://blog.minchin.ca/2021/02/covid-at-eleven-months.html">by
September</a>” that was talked about
last February and much faster that then “<a href="https://blog.minchin.ca/2021/05/covid-vaccine-1.html">16
weeks</a>” that the told us when I got my
first shot, now about 6 weeks ago. I’m happy on both counts.</p>
<p>For me, the side effects were even more minimal that for the first shot: my arm
(where I got the shot) ached for about a day, and really only if I went to poke it.</p>
<p>The signs that the world is reopening and become more and more apparent: I’ve
probably eaten out more in the last week than in the previous year, I attended
my first social event in at least at long with people that I didn’t already
know, and in-person Church meetings (without restrictions) are set to resume
next Sunday. My Honey actually returned to her one job that laid her off due to
Covid a year ago March; yesterday was her first shift.</p>
<p>But the world isn’t quite open yet. The border remains mostly closed; in theory
I could return to Canada with only a negative Covid test, but the kids aren’t
vaccinated yet (there isn’t even a vaccine announced yet for the youngest ones)
and so they would still be required to quarantine for two weeks upon their
return to Canada. The other complication for family travel plans is that my
Honey’s relief is actually stuck in India at the moment, and all direct flights
from India have been cancelled due to the current Covid situation there. So it
doesn’t look like we’ll be getting in summer travels like we’d hoped.</p>
<p>For future travels, we still want to go and see family in the States. Do we
plan on just going when the border reopens, and pull the kids from school for
two or three weeks when we do? I guess we’ll figure that out when we get there.</p>
<hr/>
<p>In not-Covid news, we’re at the beginning of what looks to be a monster heat
wave, with over a week with highs around 35-40°C. We normally get about a
week above 30°C each summer, but this is promising to be extra hot and
extra long. We’d talked about installing <span class="caps">AC</span> as part of our move-in renovations
at our new house, but hadn’t quite gotten that far yet; that’s now been bumped
way up the list! Stay cool out there!</p></body></html>Seafoam 2.5.0 Released2021-05-15T15:24:00-06:002021-05-15T15:24:00-06:00Wm. Minchintag:blog.minchin.ca,2021-05-15:/2021/05/seafoam-250.htmlIt’s time for a new update to <em>Seafoam</em>, the website theme currently in use
here (on my Blog) and by my wider site.<html><head></head><body><p>It’s time for a new update to <em>Seafoam</em>, the website theme currently in use
here (on my Blog) and by my wider site.</p>
<p>The biggest change this update brings is the addition of <em>period archive</em> (i.e.
daily, month, and yearly) archive pages. I’m actually not sure why they weren’t
include previously, although it is possible that the feature (in Pelican) didn’t
yet exist at the time this theme was first created.</p>
<p>This update also moves from my namespace plugins to the same plugins maintained
by the larger Pelican community (see upgrading for configuration changes
required on your side).</p>
<h2>Upgrading</h2>
<p>Upgrading <em>should</em> is straight forward. I haven’t broken anything on purpose
since v2.0.0 came out.</p>
<p>To install or to upgrade, you can use pip:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>seafoam<span class="w"> </span>--upgrade
</code></pre></div>
<p>If you’re already running Pelican v4.5 (or newer) <strong>and</strong> only using namespace
plugins, then the required plugins will automatically load. However, most will
have to update your <code>pelicanconf.py</code> to point to the new plugin names:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># 'minchin.pelican.jinja_filters', # <-- remove this line</span>
<span class="c1"># 'minchin.pelican.plugins.image_process', # <-- remove this line</span>
<span class="s1">'pelican.plugins.jinja_filters'</span><span class="p">,</span>
<span class="s1">'pelican.plugins.image_process'</span><span class="p">,</span>
<span class="c1"># others, as desired...</span>
<span class="p">]</span>
</code></pre></div>
<p>To be clear, <em>Seafoam</em> still supports Pelican 3 (i.e. you don’t need to upgrade
to Pelican 4.5 quite yet) and the latest versions of the two required plugins
support back to Pelican 3 as well.</p>
<h2 id="future-plans">Future Plans</h2>
<p>I’ve been working a bunch of late to update the plugins used by this blog, and
it got me thinking that perhaps I could/should write a plugin to complement the
theme. At a very basic level, it could used to feed the theme version into the
global (Pelican) configuration so it could be included in the footer. But
expanding on that idea, it could semi-automatically ensure that your Pelican
site is configured as needed (plugins included, <em>image process</em> configured,
theme selected) to speed up first setting up your site. The other place that it
could be interesting is to use it for certain formatting pieces; for example,
v2.4.7 was released to fix table formatting, but it did that by applying
Bootstrap’s table formatting rules to <em>all</em> tables on the site, whereas a plugin
could apply the right <span class="caps">HTML</span> class to only those tables within the body of
articles (so if you use tables for formatting somewhere, it won’t blow up your
site). Nothing has been started yet, but I’m excited by the possibilities.</p>
<p>Also, as I <a href="https://blog.minchin.ca/2020/07/seafoam-245-released.html#future-plans">wrote
previously</a>, this theme
is based on Bootstrap 3, and I’d figured I’d skip Bootstrap 4 and go straight to
Bootstrap 5. Bootstrap 5 is still in alpha testing, and I haven’t done anything
on this since last time, so this is likely a long way out.</p>
<h2 id="changelog">Changelog</h2>
<p>See my <a href="https://blog.minchin.ca/2020/07/seafoam-245-released.html#changelog">previous post</a> for
earlier changelog entries.</p>
<h3>Version 2.4.7 — April 17, 2021</h3>
<ul>
<li><em>bug</em>: apply table formatting without requiring the <code>.table</code> class (as is
normally required by Bootstrap)</li>
</ul>
<h3>Version 2.5.0 — May 15, 2021</h3>
<ul>
<li><em>feature</em>: add stylized period archive pages</li>
<li><em>bug</em>: fix 404 page layout issues and typos</li>
<li><em>support</em>: upgrades from <em>minchin.pelican.jinja-filters</em> to
<em>pelican-jinja-filters</em> (It’s the same plugin, just under a new name on
PyPI and packaged as a namespace plugin for Pelican 4.5 or newer.)</li>
<li><em>support</em>: upgrades from <em>minchin.pelican.plugins.image-process</em> to
<em>pelican-image-process</em> (It’s the same plugin, just under a new name on
PyPI and packaged as a namespace plugin for Pelican 4.5 or newer.)</li>
</ul></body></html>Image Process Plugin 1.2.1 & 2.1.1 for Pelican Released2021-05-15T14:46:00-06:002021-05-15T14:46:00-06:00Wm. Minchintag:blog.minchin.ca,2021-05-15:/2021/05/image-process-211.html
<p>This post actually covers five releases:</p>
<ul>
<li><strong>v1.2.1</strong> doesn’t add any functionality or bugfixes directly, but is
designed to point users to the new v2 releases.</li>
<li><strong>v1.3.0</strong> returned the plugin to the stewardship of <a href="https://github.com/whiskyechobravo/">Whisky Echo
Bravo</a>, who wrote the first versions of
this plugin. This is the first version of the plugin available on PyPI as <code>pelican-image-process</code>.</li>
<li><strong>v2.0.0</strong> reorganized the project codebase to make this work as a “namespace
plugin”. Added by Pelican 4.5 is a feature to automatically activate such
plugins. This release also fixed a bug with the crop <span class="caps">API</span>, and added the
ability to create progressive JPEGs and to work within Atom feeds. It also
transfers the code repo (and project stewardship) to the
<a href="https://github.com/pelican-plugins/image-process">Pelican-Plugins</a> organization.</li>
<li><strong>v2.1.0</strong> adds the ability to copy <span class="caps">EXIF</span> data to processed photos. </li>
<li><strong>v2.1.1</strong> lowers the minimum Pelican version to 3 (from 4.5). Under the hood,
it also updates the local development infrastructure to work better on Windows.</li>
</ul>
<html><head></head><body><p><em>Image Process</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>Image Process</em> let you automate the processing of images based on their <span class="caps">HTML</span>
class attributes. Use this plugin to minimize the overall page weight and to
save you a trip to Gimp or Photoshop each time you include an image in your post.</p>
<p><em>Image Process</em> is used by
<a href="https://github.com/MinchinWeb/seafoam">this blog’s theme</a> to resize the source
images so they are the correct size for thumbnails on the main index page and
the larger size they are displayed at on top of the articles.</p>
<h2>This Release</h2>
<p>This post actually covers five releases:</p>
<ul>
<li><strong>v1.2.1</strong> doesn’t add any functionality or bugfixes directly, but is
designed to point users to the new v2 releases.</li>
<li><strong>v1.3.0</strong> returned the plugin to the stewardship of <a href="https://github.com/whiskyechobravo/">Whisky Echo
Bravo</a>, who wrote the first versions of
this plugin. This is the first version of the plugin available on PyPI as <code>pelican-image-process</code>.</li>
<li><strong>v2.0.0</strong> reorganized the project codebase to make this work as a “namespace
plugin”. Added by Pelican 4.5 is a feature to automatically activate such
plugins. This release also fixed a bug with the crop <span class="caps">API</span>, and added the
ability to create progressive JPEGs and to work within Atom feeds. It also
transfers the code repo (and project stewardship) to the
<a href="https://github.com/pelican-plugins/image-process">Pelican-Plugins</a> organization.</li>
<li><strong>v2.1.0</strong> adds the ability to copy <span class="caps">EXIF</span> data to processed photos. </li>
<li><strong>v2.1.1</strong> lowers the minimum Pelican version to 3 (from 4.5). Under the hood,
it also updates the local development infrastructure to work better on Windows.</li>
</ul>
<h2>Upgrading</h2>
<h3>to v1.2.1</h3>
<p>To upgrade simply use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.image-process<span class="w"> </span>--upgrade
</code></pre></div>
<p>If you run v1.2.1, you will get a warning message when you generate your site
with Pelican encouraging you to upgrade to v2. This is mostly for those who
won’t stumble upon this blog entry! That said, the plugin will continue to work
as it has previously without further effort on your part.</p>
<h3>to v1.3</h3>
<p>I’d recommend you skip this update, at this point, and go straight to v2.
There’s nothing wrong with this release, <em>pre se</em>, but I’m not in a position to
test any installation instructions.</p>
<h3>to v2</h3>
<p>v1.3 introduced a different package name, so you’ll have to uninstall the old
package and install the new one. Again, <code>pip</code> is the simplest way:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pelican-image-process<span class="w"> </span>--upgrade
pip<span class="w"> </span>uninstall<span class="w"> </span>minchin.pelican.plugins.image-process
</code></pre></div>
<p>The new package name and file layout is to make the plugin a “namespace
plugin”. <em>Namespace plugins</em> are actually a really cool idea that if you create
your package in the right way, your “host” program can find the plugins simply
by having them installed on your system! For Pelican, they need to be in the
<code>pelican.plugins</code> namespace.</p>
<p>Two caveats of this approach is that you’ll need Pelican version 4.5 (or later)
to automatically load these namespace plugins, and (at least if my
understanding is correct) you have to either rely on namespace plugins alone <span class="caps">OR</span>
the <code>PLUGINS</code> setting of your <code>pelicanconf.py</code>; i.e. if you specify <code>PLUGINS</code>
in your settings, auto-loading of namespace plugins is turned off. Neither of
these are deal breakers, but this background may prove useful in debugging your
setup. Overall, I think namespace plugins are an awesome idea, and I hope it
doesn’t take too long to get everything switched over.</p>
<p>So if you’re using other non-namespace plugins, or a Pelican version before
4.5, you’ll also need to update your <code>pelicanconf.py</code> with the new plugin name:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># others...</span>
<span class="c1"># minchin.pelican.plugins.image_process # <-- remove this line</span>
<span class="s2">"pelican.plugins.image_process"</span><span class="p">,</span>
<span class="p">]</span>
</code></pre></div>
<p>Finally, v2.0.0 bumps the minimum Pelican version up to 4.5; if you’re using an
older version of Pelican and don’t want to upgrade yet, then use v2.1.1 of the plugin.</p>
<p>The new features (generating progressive JPEGs and applying to Atom feed images)
are automatically enabled.</p>
<p>As for the change in the crop <span class="caps">API</span>, it’s a bugfix so the plugin behaviour should now match the documented (anticipated) behaviour; specifically <code>crop <top> <left> <right> <bottom></bottom></right></left></top></code>.</p>
<h3>to v2.1.0</h3>
<p>Assuming you’ve done the steps listed above to upgrade to v2.0.0, <code>pip</code> remains
the simplest way to upgrade:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pelican-image-process<span class="w"> </span>--upgrade
</code></pre></div>
<p>To copy over <span class="caps">EXIF</span> data, you’ll need to set <code>IMAGE_PROCESS_COPY_EXIF_TAGS</code> (in your <code>pelicanconf.py</code>) to <code>True</code>. You will also need to install <a href="https://exiftool.org/">ExifTool</a>. I haven’t tried it but it looks like ExifTool supports Windows, just be sure that it’s been added to your <span class="caps">PATH</span>.</p>
<h3>to v2.1.1</h3>
<p>Assuming you’ve done the steps listed above to upgrade to v2.1.0, <code>pip</code> remains
the simplest way to upgrade:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pelican-image-process<span class="w"> </span>--upgrade
</code></pre></div>
<p>This version lowers the minimum Pelican version 3 (which is something I needed
to incrementally upgrade my site; I’m stuck at v3.7.1 for a bit yet while I
upgrade some other plugins).</p>
<h2>Thoughts on These Releases and the Future</h2>
<p>This part is more of a personal than technical note, and continuation of my
thoughts about the last <em><a href="https://blog.minchin.ca/2021/04/jinja-filters-210.html#personal-thoughts">Jinja
Filters</a></em> release.</p>
<p>The “ownership” of this code is even more involved that the <em>Jinja Filters</em>
plugin. With <em>Jinja Filters</em>, that was code that I’d written myself, packaged,
and eventually moved (at my request) to be under the Pelican Plugins
organization. Here, I adopted someone else’s existing code, packaged it and used
it myself, and eventually they returned from the woodwork to reclaim it (and
then transferred it to the Pelican Plugins organization). On one hand, this
represent the wonder of Open Source in that I resurrect a “dead” plugin; on the
other, it raises an interesting question of what does <em>ownership</em> mean in such a
landscape? Did I ever “own” this code? Was it mine to give away or surrender? I
think the language fails here, and so perhaps the term “stewardship” rather than
“ownership” is more helpful.</p>
<p>In any case, I’m excited to see that the plugin is being maintained without
requiring a bunch of my personal effort and is getting features added as well.
When I had assumed stewardship for maintenance, I always felt at a disadvantage
because I didn’t have the deep understanding that would have come from writing
the original code, so I’m happy to let someone else take that on. I’m slightly
sad though because this plugin represented my most starred repo on GitHub, and
was the one Pelican plugin that I’d put out to the world that I knew other
people were actually using.</p>
<p>Moving forward, I’m not sure if every release will get a release post. I
suspect the releases I’m involved in will get a post, but hopefully there will
continue to be some without my involvement!</p>
<p>Now, only 10 more plugins to go! I want to move all the plugins I use to
namespace plugins and then upgrade from Pelican 3.7.1 to 4.6 (or whatever the
then-current version is). I’m a little bit closer. :)</p></body></html>Covid Vaccine — 1 of 22021-05-11T21:37:00-06:002021-05-11T21:37:00-06:00Wm. Minchintag:blog.minchin.ca,2021-05-11:/2021/05/covid-vaccine-1.htmlSo, on Saturday, I ended up getting my first <span class="caps">COVID</span> vaccine shot.<html><head></head><body><p>So, on Saturday, I ended up getting my first <span class="caps">COVID</span> vaccine shot.</p>
<p>I actually ended up driving an hour out of town for it. Last Thursday I became
eligible to sign up, but I was kind of stumped for when to book because our
second car was in the shop. Normally (or what passes as “normal” in these Covid
times…), that wouldn’t really be an issue what with working from home these
days, but I need a car to get to the vaccination site if I did it during the
workday. On Friday, when I went to book and as luck would have it, there was
one opening about an hour out of town that I could do the very next day; it was
the only opening in the next week! So I jumped on it.</p>
<p>The drive out ended up being in the rain. It’s kind of ridiculous to have to
drive an hour, but on the other hand, it’s done and it didn’t hurt to get out
of the house.</p>
<p>I got the Pfizer shot (although the appointment was selected based entirely on
when it was). As for side effects, they’ve been pretty minimal: I had a sore
shoulder (where I was given the injection) for a couple of days. It was a weird
sort of not-quite “bruise”: it was felt tender to the touch, but it didn’t
really limit my movement the same ways a normal bruise often does. I have been
told that the follow up short is worse, but we’ll deal with that when it happens.</p>
<p>For follow up, they say our second shot will be available in about 16 weeks,
which seems an incredibly long time. On one hand, with vaccines going out, and
more and more people being vaccinated, things should start opening up,
hopefully because the Covid case count should start falling. But 16
weeks…that’s four months — May, June, July, August — we’ll almost be done
the summer by the time that comes. And there still isn’t a shot approved for
the young ones yet, for the kids under 12. Will this ever really go away until
something is approved for the kids? I don’t know. For now, in-person schooling
has been cancelled (“for the next two weeks”) to try and keep the case count down.</p>
<p>Overall, I’ll take this bit of hope. Hopefully, more reasons to hope follow soon!</p></body></html>Jinja Filters 1.1.0 & 2.1.0 for Pelican Released2021-04-30T09:37:00-06:002021-04-30T09:37:00-06:00Wm. Minchintag:blog.minchin.ca,2021-04-30:/2021/04/jinja-filters-210.html
<p>This post actually covers three releases:</p>
<ul>
<li><strong>v1.1.0</strong> doesn’t add any functionality or bugfixes directly, but is
designed to point users to the new v2 releases.</li>
<li><strong>v2.0.0</strong> reorganized the project codebase to make this work as a “namespace
plugin”. Added by Pelican 4.5 is a feature to automatically activate such
plugins. It also transfers the code repo to the
<a href="https://github.com/pelican-plugins/jinja-filters">Pelican-Plugins</a>
organization and moved the PyPI package to <code>pelican-jinja-filters</code>.</li>
<li><strong>v2.1.0</strong> adds two filters — <em>merge_date_url</em> and <em>datetime_from_period</em>.
It also lowers the minimum Pelican version to 3 (from 4.5). Under the hood,
it also updates the local development infrastructure to work better on Windows.</li>
</ul>
<html><head></head><body><p><em>Jinja Filters</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a
static site generator written in Python.</p>
<p><em>Jinja Filters</em> provides a selection of functions (called <em>filters</em>) for
templates to use when building your website. They are packaged for Pelican, but
may prove useful for other projects that make use of
<a href="http://jinja.pocoo.org/">Jinja2</a>.</p>
<h2>This Release</h2>
<p>This post actually covers three releases:</p>
<ul>
<li><strong>v1.1.0</strong> doesn’t add any functionality or bugfixes directly, but is
designed to point users to the new v2 releases.</li>
<li><strong>v2.0.0</strong> reorganized the project codebase to make this work as a “namespace
plugin”. Added by Pelican 4.5 is a feature to automatically activate such
plugins. It also transfers the code repo to the
<a href="https://github.com/pelican-plugins/jinja-filters">Pelican-Plugins</a>
organization and moved the PyPI package to <code>pelican-jinja-filters</code>.</li>
<li><strong>v2.1.0</strong> adds two filters — <em>merge_date_url</em> and <em>datetime_from_period</em>.
It also lowers the minimum Pelican version to 3 (from 4.5). Under the hood,
it also updates the local development infrastructure to work better on Windows.</li>
</ul>
<h2>Upgrading</h2>
<h3>to v1.1.0</h3>
<p>To upgrade simply use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.jinja-filters<span class="w"> </span>--upgrade
</code></pre></div>
<p>v1.1.0 actually depends on v2.1.0 or newer, which will automatically be installed.</p>
<p>As a side, this (having one version of a package rely on another
version of the “same” package) is generally not desirable or even possible. But
PyPI/pip has no real concept of package re-naming, and the two different
package names is what makes this work.</p>
<p>If you run v1.1.0, you will get a warning message when you generate your site
with Pelican encouraging you to upgrade to v2. This is mostly for those who
won’t stumble upon this blog entry! That said, the plugin will continue to work
as it has previously without further effort on your part.</p>
<h3>to v2.0.0</h3>
<p>v2.0.0 has a different package name, so you’ll have to uninstall the old
package and install the new one. Again, <code>pip</code> is the simplest way:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pelican-jinja-filters<span class="w"> </span>--upgrade
pip<span class="w"> </span>uninstall<span class="w"> </span>minchin.pelican.jinja-filters
</code></pre></div>
<p>The new package name and file layout is to make the plugin a “namespace
plugin”. <em>Namespace plugins</em> are actually a really cool idea that if you create
your package in the right way, your “host” program can find the plugins simply
by having them installed on your system! For Pelican, they need to be in the
<code>pelican.plugins</code> namespace.</p>
<p>Two caveats of this approach is that you’ll need Pelican version 4.5 (or later)
to automatically load these namespace plugins, and (at least if my
understanding is correct) you have to either rely on namespace plugins alone <span class="caps">OR</span>
the <code>PLUGINS</code> setting of your <code>pelicanconf.py</code>; i.e. if you specify <code>PLUGINS</code>
in your settings, auto-loading of namespace plugins is turned off. Neither of
these are deal breakers, but this background may prove useful in debugging your
setup. Overall, I think namespace plugins are an awesome idea, and I hope it
doesn’t take too long to get everything switched over.</p>
<p>So if you’re using other non-namespace plugins, or a Pelican version before
4.5, you’ll also need to update your <code>pelicanconf.py</code> with the new plugin name:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># others...</span>
<span class="c1"># minchin.pelican.jinja_filters # <-- remove this line</span>
<span class="s2">"pelican.plugins.jinja_filters"</span><span class="p">,</span>
<span class="p">]</span>
</code></pre></div>
<p>Finally, v2.0.0 bumps the minimum Pelican version up to 4.5; if you’re using an
older version of Pelican and don’t want to upgrade yet, then use v2.1.0 of the plugin.</p>
<h3>to v2.1.0</h3>
<p>Assuming you’ve done the steps listed above to upgrade to v2.0.0, <code>pip</code> remains
the simplest way to upgrade:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pelican-jinja-filters<span class="w"> </span>--upgrade
</code></pre></div>
<p>This version lowers the minimum Pelican version 3 (which is something I needed
to incrementally upgrade my site; I’m stuck at v3.7.1 for a bit yet while I
upgrade some other plugins).</p>
<p>It also added two new filters: <em>merge_date_url</em> and <em>datetime_from_period</em>. I
added these in particular for use on the period archive pages (i.e. yearly,
monthly, and daily archives) and will be used by the next version of
<a href="https://blog.minchin.ca/label/seafoam/">seafoam</a> (the theme on this site).</p>
<h2>Details on the New Features (of v2.1.0)</h2>
<h3>datetime_from_period</h3>
<p>By way of background, Pelican feeds a tempate variable called <code>period</code> to the
template when generating period archive pages. However, the variable is a tuple
with the month (when present) as a string. If the text is already formatted the
way you want (and the default is generally sensible), then you can just display
it as is. However, this filter can turn that tuple into a “proper”
<code>datetime.datetime</code> object to be further processed.</p>
<p>You might use it like this:</p>
<div class="highlight"><pre><span></span><code><span class="cp">{{</span> <span class="nv">period</span> <span class="o">|</span> <span class="nf">datetime_from_period</span> <span class="o">|</span> <span class="nf">datetime</span><span class="o">(</span><span class="s1">'%Y'</span><span class="o">)</span> <span class="cp">}}</span>
</code></pre></div>
<p>(<em>datetime</em>, as used here, is another filter provided by this plugin.)</p>
<p>As an implementation note, if the month is not supplied (e.g. on a yearly
archive page), this filter will assume it is January; if the date is not
supplied (i.e. on a yearly or monthly archive page), the 1st is assumed.</p>
<h3>merge_date_url</h3>
<p>When given a datetime (on the left to operate on), and provided with a period
archive url (typically a Pelican setting like <code>YEAR_ARCHIVE_URL</code>), it will
“apply” the date to the <span class="caps">URL</span>.</p>
<p>So the two might be used together like this (example pulled from the pending
<a href="https://blog.minchin.ca/label/seafoam/">seafoam</a> release):</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">SITEURL</span> -<span class="cp">}}</span><span class="s"> /</span>
<span class="s"> </span><span class="cp">{{</span><span class="o">-</span> <span class="nv">period</span> <span class="o">|</span> <span class="nf">datetime_from_period</span> <span class="o">|</span> <span class="nf">merge_date_url</span><span class="o">(</span><span class="nv">YEAR_ARCHIVE_URL</span><span class="o">)</span> <span class="cp">}}</span><span class="s">"</span><span class="p">></span>
<span class="cp">{{</span> <span class="nv">period</span> <span class="o">|</span> <span class="nf">datetime_from_period</span> <span class="o">|</span> <span class="nf">datetime</span><span class="o">(</span><span class="s1">'%Y'</span><span class="o">)</span> <span class="cp">}}</span>
<span class="p"><!--</span--><span class="nt">a</span><span class="p">></span>
</span></code></pre></div>
<p>while will result in the generated <span class="caps">HTML</span>:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"https://blog.minchin.ca/posts/2021/</span><span class="p">></span>2021<span class="p"><!--</span--><span class="nt">a</span><span class="p">></span>
</span></code></pre></div>
<h2 id="personal-thoughts">Thoughts on These Releases and the Future</h2>
<p>This part is more of a personal than technical note.</p>
<p>Eagle-eyed among you may notice that the initial version 2 release happened
back in August. I was actively involved in <a href="https://github.com/pelican-plugins/jinja-filters/pull/4">the
process</a>, but it’s
taken some time to decide what to make the changes that involved.</p>
<p>Overall, I still think moving the plugin to a organization repo is a good
thing. This should help with visibility, and hopefully this will result in more
people contributing to it. But it is also weird to “let go” of this code, in
the sense that it is no longer under my username on GitHub.</p>
<p>The biggest technical changes on the backend side involved a complete
replacement of the release machinery. Previously I’d been using a home-grown
script (<a href="https://github.com/MinchinWeb/minchin.releaser">minchin.releaser</a>) to
do releases; it was mine, and I could punch out a release from a single
command. That was replaced by <a href="https://github.com/autopub/autopub">AutoPub</a>,
which is a “bot” to automatically publish releases from your project’s
continuous integration system; this has the benefit of basically being
completely automated. And for a multi-user environment (like the hope is with
the new code repo location), this new setup makes a lot of sense.</p>
<p>One oddity of giving up, or a least sharing, ownership of the code, is that I
had <a href="https://github.com/justinmayer">Justin Mayer</a> (who manages both the
Pelican project and Pelican-Plugins) review my proposed changes. I think my
code is better for it; I know for many of the changes I ended up writing a
whole bunch about what and why I wanted the changes I was proposing, sometimes
longer than the actual code in question.</p>
<p>The last big change was a switch from a <code>setup.py</code>/<code>requirements.txt</code> setup
(supported by <a href="https://github.com/jazzband/pip-tools">pip-tools</a>) to a
<a href="https://python-poetry.org/">poetry</a>/<code>pyproject.toml</code> setup. I don’t know if
this is “the way of the future” (as some have made it out to be), but this is
the change I’m least sold on. <em>poetry</em> has proved tricky to configure at times,
is very opinionated, and sometimes hides it’s logic under the covers. As well,
I’m not sure it’s solved any problem I didn’t already have solved. But I’ll use
it for projects like this, and just count it among the (slight) costs of
working with others.</p>
<p>Moving forward, I’m not sure if every release will get a release post. I
suspect the releases I’m involved in will get a post, but hopefully there will
be some without my involvement!</p>
<p>Now, only 11 more plugins to go! I want to move all the plugins I use to
namespace plugins and then upgrade from Pelican 3.7.1 to 4.6 (or whatever the
then-current version is). I’m a little bit closer. :)</p></body></html>Global Git Ignore on Windows2021-04-28T21:16:00-06:002021-04-28T21:16:00-06:00Wm. Minchintag:blog.minchin.ca,2021-04-28:/2021/04/global-git-ignore-on-windows.html<p><em>Git</em> has an handy feature that you can get a system-wide <em>.gitignore</em> file. This
is particularly helpful to keep your project <em>.gitignore</em> files lean. However,
<em>Git</em> was originally built for Linux and sort of begrudgingly supports Windows,
which sometimes leads to oddities (like this):</p>
<p><strong>Where do you put your global <em>.gitignore</em> file on Windows to automatically
have it loaded?</strong></p>
<html><head></head><body><p><em>Git</em> has an handy feature that you can get a system-wide <em>.gitignore</em> file. This
is particularly helpful to keep your project <em>.gitignore</em> files lean. However,
<em>Git</em> was originally built for Linux and sort of begrudgingly supports Windows,
which sometimes leads to oddities (like this):</p>
<p><strong>Where do you put your global <em>.gitignore</em> file on Windows to automatically
have it loaded?</strong></p>
<p>Turns out it’s ridiculously hard to find the answer; it’s not
even on the official <a href="https://git-scm.com/docs/gitignore">Git docs</a> or in
<a href="https://docs.github.com/en/github/getting-started-with-github/ignoring-files">GitHub
help</a>.
(Note I want the auto-loading location.)</p>
<p>In the end, I found the answer on an issue for a random project
(<a href="https://github.com/Canop/broot/issues/212">Broot Issue #212</a>):</p>
<blockquote>
<p>it is stated in the [Git] documentation that <code>$XDG_CONFIG_HOME/git/ignore</code>
(or <code>$HOME/.config/git/ignore</code> if <code>$XDG_CONFIG_HOME</code> is not set) is the
default value of core.excludesFile.</p>
</blockquote>
<p>So, translated to Windows-ese, the answer is:</p>
<p><code>C:\Users\<username>\.config\git\ignore</username></code></p>
<p><code>ignore</code> is the filename. The contents of the file are the same as any other
<em>.gitignore</em> file, except they will be applied to all your projects.</p>
<p>Hope this is helpful to someone else!</p></body></html>Covid Personal Update — 11 Months In2021-02-14T00:00:00-07:002021-03-15T21:35:00-06:00Wm. Minchintag:blog.minchin.ca,2021-02-14:/2021/02/covid-at-eleven-months.html<p>So it’s almost exactly 11 months since the first Covid restrictions were
implemented…. It’s getting long enough that “last year” will soon stop
referring to “normal” times.<html><head></head><body><p>So it’s almost exactly 11 months since the first Covid restrictions were
implemented…. It’s getting long enough that “last year” will soon stop
referring to “normal” times. The vaccine is now starting to
become a thing, and I know of at least one cousin that has received it (she
works in health care), but no one has offered me (personally) a timeline beyond
the vague “by September”. September is still 7 months away, on the other side
of summer, and still too non-committal for me to add to my calender or to make
plans based on.</p>
<p>At the moment, life feels an extra sort of bleak. Layered on top of Covid is
that fact that it’s been -30°C this last week; I don’t think I would
have wanted to leave the house even if there way somewhere to go. The dimmer
and shorter days of winter don’t help either.</p>
<p>This coming summer, we’d hoped to take an exciting summer vacation. We’re at a
point, that we hadn’t been in the past, where we could take several weeks off
as a chunk from work and have the money to actually take the family somewhere.
With a two week quarantine on returning from out of the country, the idea of
spending a week in Europe followed by two weeks locked up at home didn’t seem
to balance out. We considered a road trip to the East coast, as that wouldn’t
involve international borders and so no quarantine on returning home, but now
Manitoba and the Atlantic provinces have their own bubbles, so would I have to
add 4 weeks to my travel time to Halifax if I drive, and 2 weeks on the way
back? We’d tentatively settled on visiting relatives in the <span class="caps">US</span>: we could go for
6 weeks and then spend 2 weeks on quarantine on returning home. Maybe the
quarantine requirement would be lifted by then and we could spend 7 or 8 weeks
with them. But the Canadian government has moved to make quarantining way more
expensive by requiring you to stay the first 3 days in a hotel of their choice
at for $2,000. That’s within spitting distance of what we paid for an overwater
bungalow in Tahiti! And it also completely blows up our road trip budget. Well
maybe the <span class="caps">BC</span> coast remains an option…</p>
<hr/>
<p>For now, at least, the kids remain in school. School has always been full of
odd rules, and Covid has resulting in more oddball rules, and probably lowered
my patience to deal with them. One new rule, designed to limit the spread of
the virus, is that inter-class interactions are severely limited. For example,
the playground is now on a rotation with only one class/grade using it at a
time. So any given grade only gets to use it only once a week.</p>
<p>Another particular rule the school brought in was that no papers could be
brought from home (a possible virus vector?). One of the most obvious results
was that the kids no longer bring home an agenda from school, but now the
parent-teacher communication happens via email. I can’t decide is this is
better or not. Another oddity that grew out of this rule is that the kids just
had Valentines Day…without (paper) Valentines. One of my kid’s classes sort
of got around this by having each student creating a “Valentine” for another
particular student in their class as an art project, and then posting them all
on the wall. Props to the teacher there!</p></body></html>Negative Covid Test Results2020-09-27T00:00:00-06:002020-11-21T16:33:00-07:00Wm. Minchintag:blog.minchin.ca,2020-09-27:/2020/09/negative-covid-test-results.htmlSo my Honey and the kids got their Covid test back: negative. But, odd to me
at least, they’ve been asked to continue to isolate for the rest of the
original two week isolation period.<html><head></head><body><p>So my Honey and the kids got their Covid test back: negative. But, odd to me
at least, they’ve been asked to continue to isolate for the rest of the
original two week isolation period.</p>
<p>Thankfully, I’ve not been asked to isolate, so I’ve taken to running all the
errands. Grocery delivery has become more of thing of late, but I’m glad we
don’t have to rely on it, or worry about it’s cost.</p></body></html>Covid — 6 1/2 Months In2020-09-24T00:00:00-06:002020-11-21T16:29:00-07:00Wm. Minchintag:blog.minchin.ca,2020-09-24:/2020/09/covid-at-six-and-a-half-months.htmlSo <a href="{filename}20200913-covid-at-six-months.md">normal</a> went out the window
pretty fast. After two and a half weeks of school (i.e. “normality”) my Honey
and some of the kids are at home, being asked to isolate for the next two
weeks. It turns out one of the other kids at the daycare tested positive for Covid.<html><head></head><body><p>So <a href="https://blog.minchin.ca/2020/09/covid-at-six-months.html">normal</a> went out the window
pretty fast. After two and a half weeks of school (i.e. “normality”) my Honey
and some of the kids are at home, being asked to isolate for the next two
weeks. It turns out one of the other kids at the daycare tested positive for Covid.</p></body></html>Covid — 6 Months In2020-09-13T00:00:00-06:002020-11-21T16:26:00-07:00Wm. Minchintag:blog.minchin.ca,2020-09-13:/2020/09/covid-at-six-months.htmlLife feels like it’s returning to normal. But I wonder if that’s mostly because
the kids are back at school and my Honey is once again working outside the
house (so I have the house to myself during the day again).<html><head></head><body><p>Life feels like it’s returning to normal. But I wonder if that’s mostly because
the kids are back at school and my Honey is once again working outside the
house (so I have the house to myself during the day again).</p>
<p>In other, somewhat surprising news, we bought a new house! It seems somewhat
ironic that it would happen during these Covid times, but here we are.
Actually, we’ve done rather well though this Covid spell so far: my work was
unaffected, my Honey was off work but the government programs more than made up
for her lost paycheck, plus no childcare to pay for and nowhere to travel to.
We’d been looking at houses for some time (perhaps 3 years?) and so when
something came up that we knew was a great combination of the price and the
location we were after, we knew it was time to jump. It ended up happening
really fast. I’m still surprised it happened (as recently as January, I figured
it would never happen due to housing prices…). Even more surprising, we were
able to do it without increasing our monthly mortgage payment, although we did
add another 12 years until our mortgage will be paid off.</p></body></html>Economics of Land Redevelopment2020-08-16T00:00:00-06:002020-11-21T16:18:00-07:00Wm. Minchintag:blog.minchin.ca,2020-08-16:/2020/08/economics-of-land-redevelopment.htmlI’ve been house hunting for a while, seeing if I can mind a bigger place at an
affordable price. As part of that, I’ve been thinking a little about urban
renewal and redevelopment and their effect on house pricing. The long and the
short of it is that I don’t see how they can bring down house prices (directly).<html><head></head><body><p>I’ve been house hunting for a while, seeing if I can mind a bigger place at an
affordable price. As part of that, I’ve been thinking a little about urban
renewal and redevelopment and their effect on house pricing. The long and the
short of it is that I don’t see how they can bring down house prices (directly).</p>
<p>We’ve also previously looked into real estate investing (i.e. buying a rental
house), and so I’ve been to perhaps more than my fair share of open houses. In
the neighbourhoods I’ve most been interested in (both for rentals and for my
own house), they were originally developed in the 1950’s and 1960’s. It’s an
age where the homes were mostly built on the same basic principals as current
construction (i.e. 2x4 wood frame construction on poured concrete foundations),
but the houses are old enough to see their original owners start to move into
old folks homes. Together, it means that the houses are typically livable as is
(although sometimes dated inside) but some of them are in really rough shape,
or are very small (~800 sq ft) by modern standards, and so become attractive to
be demolished and the lots redeveloped.</p>
<p>But when you buy a house, it’s actually a combined price of the land its
sitting on plus the standing house itself. It can be hard to tease the two
apart, but in general, it seems that these “houses” are priced based on 75% lot
value and 25% structure. To put numbers on it, houses will go for $325,000 to
$450,000 (baring exceptional houses, and those at the high end being recently
renovated/updated), while lots (sometimes with a house in poor shape that will
need to be removed, sometimes already prepped for re-development) will go for
$250,000 to $275,000. This would imply that the existing house structure is
worth $50,000 to $200,000. These houses are around 1,100 to 1,250 sq ft in
size, and based on building costs of ~$200/sq ft, that gives these houses a
rebuilding cost of $220,000 to $250,000, plus an additional $40,000 for a
double garage (which is common on many of these existing houses). The numbers
aren’t actually all that important, but the point is it costs more to buy a lot
and build a new house on it than it is to just buy a standing house, sometimes
by a considerable margin. And thus, you can’t just build houses to lower
housing costs in these neighbourhoods.</p>
<p>One not-uncommon development technique is for a developer to buy a lot, split
it in two (the City now permits houses on 25 ft wide lots, and many of the old
lots were around 50 ft wide), and build a house on each side. These new houses
are two stories tall to try and get more usable space, but even so, they seem
to max out around 1,600 sq ft (so 800 sq ft per level). But the pricing can be
high: $550,000 to $800,000 each!</p>
<p>So if your goal is to bring down housing costs, other options are needed. One
option would seem to be putting more units on the same land, but we just saw
how splitting the lot in two didn’t seem to work. Row housing may work, but
doesn’t actually provide much of a density increase over houses on 25 foot
lots. Going to apartment-style housing might work, but high rises are actually
more expensive to build to “regular” housing on a square foot basis, in part
because now you have to build underground parking for everyone. Wood frame
low-rises (up to about 4 stories) might hit the sweet spot where in makes a difference.</p>
<p>We actually considered apartment-style living for a little bit, however most
everything we could find was either too small or way more expensive that a
“regular” house (or both!). Apartment-style units seemed to max out a 3
bedrooms as well. To be fair, houses with more than 3 bedrooms are not common
either, but a house (at least here) would typically have a basement that could
be developed into additional bedrooms. I have yet to see an apartment-style
unit with 1,000 sq ft of undeveloped space that you, the owner, can build out
at a later time. And for us, with the kids and working from home (which started
well before Covid), 3 bedrooms simply isn’t enough long term.</p>
<p>So this is a rather long way of saying that redevelopment doesn’t seem to lower
housing prices, and until apartment-style buildings offer 4, 5, and 6 bedroom
unit at prices lower that single family housing, apartment-style isn’t going to
be a serious alternative for family housing either.</p>
<p>I wish I could offer a better solution.</p></body></html>Seafoam 2.4.5 Released2020-07-17T20:54:00-06:002020-07-18T11:41:00-06:00Wm. Minchintag:blog.minchin.ca,2020-07-17:/2020/07/seafoam-245-released.htmlIt’s time for a new update to <em>Seafoam</em>, the website theme currently in use
here (on my Blog) and by my wider site.<html><head></head><body><p>It’s time for a new update to <em>Seafoam</em>, the website theme currently in use
here (on my Blog) and by my wider site.</p>
<p>In reviewing my blog, I realized it’s been many versions and a couple of years
since I did a post about a <em>Seafoam</em> release. In the background, I’ve continued
to make small improvements. I also use the project for private, personal
projects, so some of the improvements are centered on those. What drove this
particular release was that something has happened that broke my fonts.
Previously, they were hosted directly on Google Fonts, but they seem to have
stopped loading, so with this release they are “internal” to the theme.</p>
<p>As well, <a href="https://github.com/MinchinWeb/minchin.releaser/">minchin.releaser</a>
has made putting out a release very simple, to the point where a blog post
about the release in question can be the hardest part of the whole process (and
so they often just never happen…). You’ll notice in the changelog below it’s
not uncommon to see multiple releases the same day.</p>
<p>A final thing I noticed was lacking from the project readme was any sort of
sample images. This is hope will prove simple and easy to fix.</p>
<h2>Fallback fonts (i.e. “broken”)</h2>
<p><img alt="fallback fonts" src="https://blog.minchin.ca/images/2020/broken_fonts.png"/></p>
<h2>Working fonts</h2>
<p><img alt="workingfonts" src="https://blog.minchin.ca/images/2020/working_fonts.png"/></p>
<h2 id="future-plans">Future Plans</h2>
<p>This theme is based on <a href="https://getbootstrap.com/docs/3.3/">Bootstrap 3</a>. About
a year ago, I’d started an effort to move to Bootstrap 4. Turns out that
transition is a bit of a pain, and I never did finish it to my liking. Now
Bootstrap 5 (at least <a href="https://blog.getbootstrap.com/2020/06/16/bootstrap-5-alpha/">the first alphas
build</a>) have been
released, so I’ll probably just skip 4 if I ever do that update. A big issue on
updating Bootstrap, at least for me, is that they changed their preferred <span class="caps">CSS</span>
processor from <span class="caps">LESS</span> to <span class="caps">SASS</span>: Bootstrap 3 supported both, while version 4 is
<span class="caps">SASS</span> only. All my modifications had been done in <span class="caps">LESS</span>, so that provided a large
effort to “translate”.</p>
<h2>Upgrading</h2>
<p>Upgrading <em>should</em> is straight forward. I haven’t broken anything on purpose
since v2.0.0 came out.</p>
<p>To install or to upgrade, you can use pip:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>seafoam<span class="w"> </span>--upgrade
</code></pre></div>
<h2>Update: July 18, 2020</h2>
<p>New day, new update! Version 2.4.6 adds a few non-breaking spaces to improve
the flow of the acticle details on the article index page.</p>
<h2 id="changelog">Changelog</h2>
<p>See my <a href="https://blog.minchin.ca/2017/01/seafoam-2-released.html#changelog">previous post</a> for earlier
changelog entries.</p>
<h3>Version 2.1.0 — February 20, 2017</h3>
<ul>
<li><em>feature</em>: add support for the
<a href="https://pypi.python.org/pypi/pelican-readtime">readtime</a> plugin in
preference to the <em>post-stats</em> plugin to get article reading time. The
former is available on PyPI (as <em>pelican-readtime</em>), while the latter is not.</li>
<li><em>support</em>: document optionally supported plugins (see <a href="https://github.com/MinchinWeb/seafoam/issues/2">issue
2</a>.)</li>
</ul>
<h3>Version 2.1.1 — March 8, 2017</h3>
<ul>
<li><em>bug</em>: fix pagination links on category and tag pages. See <a href="https://github.com/MinchinWeb/seafoam/issues/6">issue
6</a>.</li>
<li><em>bug</em>: remove unused code in pagination template. Thanks
<a href="https://github.com/jorgesumle">@jorgesumle</a>! (see <a href="https://github.com/MinchinWeb/seafoam/issues/13">issue
13</a>.)</li>
</ul>
<h3>Version 2.1.2 — March 8, 2017</h3>
<ul>
<li><em>bug</em>: provide universal wheels. On versions of Python before 3.4 (when the
<em>pathlib</em> module was added to the standard library), we now depend on
<a href="https://pypi.python.org/pypi/pathlib2">pathlib2</a>.</li>
<li><em>bug</em>: provide an absolute path.</li>
</ul>
<h3>Version 2.1.3 — April 19, 2017</h3>
<ul>
<li><em>support</em>: document most theme options (see <a href="https://github.com/MinchinWeb/seafoam/issues/2">issue
2</a>).</li>
</ul>
<h3>Version 2.1.4 — April 9, 2017</h3>
<ul>
<li><em>suupport</em>: <a href="https://pypi.org/search/?c=Framework+%3A%3A+Pelican+%3A%3A+Themes">Framework :: Pelican ::
Themes</a>
trove classifier on PyPI now available.</li>
</ul>
<h3>Version 2.1.5 — May 31, 2017</h3>
<ul>
<li><em>bug</em>: indent definition list items (see <a href="https://github.com/MinchinWeb/seafoam/issues/11">issue
11</a>).</li>
<li><em>bug</em>: note that <em>Image Processing</em> v1.1.2 is broken (see their
<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.image_process/issues/2">issue
2</a>).
Use a different version of that plugin.</li>
</ul>
<h3>Version 2.2.0 — November 13, 2017</h3>
<ul>
<li><em>feature</em>: include <a href="https://github.com/MinchinWeb/prjct">prjct</a> template.</li>
<li><em>feature</em>: include 404 template (see <a href="https://github.com/MinchinWeb/seafoam/issues/15">issue
15</a>).</li>
<li><em>feature</em>: use <code>NAVBAR_ON_TOP</code> to move the menu from the left side of the
page to the top (the Bootstrap default).</li>
<li><em>bug</em>: respect Pelican’s <code>THEME_STATIC_DIR</code> setting.</li>
<li><em>support</em>: use
<a href="https://github.com/MinchinWeb/minchin.releaser/">minchin.releaser</a> to put
out releases.</li>
</ul>
<h3>Version 2.2.1 — November 13, 2017</h3>
<ul>
<li><em>bug</em>: fix reference to <em>python-dateutil</em> in project metadata.</li>
</ul>
<h3>Version 2.3.0 — November 29, 2017</h3>
<ul>
<li><em>feature</em>: add basic support for Tuque Search plugin.</li>
<li><em>feature</em>: added support for <a href="https://github.com/MinchinWeb/prjct">prjct</a>.</li>
<li><em>bug</em>: fix issues with navbar coloring, navbar brand text + logo
layout, and sidebar alinement.</li>
</ul>
<h3>Version 2.3.1 — November 30, 2017</h3>
<ul>
<li><em>bug</em>: fix styling of breadcrumbs on article pages.</li>
<li><em>bug</em>: fix styling of pager on search results.</li>
</ul>
<h3>Version 2.3.2 — December 8, 2017</h3>
<ul>
<li><em>bug</em>: fix styling of main text body when using vertical menu.</li>
</ul>
<h3>Version 2.3.3 — January 18, 2018</h3>
<ul>
<li><em>bug</em>: make <em>Archives</em> link work better with vertical menu and with sub-sites.</li>
</ul>
<h3>Version 2.3.4 — January 18, 2018</h3>
<ul>
<li><em>bug</em>: Add instructions on how to use the <em>404 Error</em> page.</li>
</ul>
<h3>Version 2.4.0 — February 3, 2018</h3>
<ul>
<li><em>feature</em>: various <span class="caps">CSS</span> additions to support
<a href="https://gigatrees.appspot.com/">Gigatrees</a> 4.4.1 (genealogy site generator).</li>
<li><em>support</em>: upgrade to <em>respond.js</em> v1.4.2.</li>
<li><em>feature</em>: add ability to add Javascript to <code></code> with
<code>CUSTOM_JS_LIST_HEAD</code>, which is designed to work very similar to
<code>CUSTOM_JS_LIST</code>.</li>
<li><em>feature</em>: add <code>JQUERY_JS_IN_HEAD</code> to move loading JQuery from the end of
the page to the head section.</li>
<li><em>feature</em>: support local and absolute URLs for <code>CUSTOM_CSS_LIST</code> and
<code>CUSTOM_JS_LIST</code>, and scripts directly for <code>CUSTOM_JS_LIST</code>.</li>
<li><em>bug</em>: Make the output <span class="caps">HTML</span> a little cleaner.</li>
<li><em>support</em>: edit some <span class="caps">JS</span> and <span class="caps">CSS</span> links to explicitly note the version of the
library being loaded. This should make both cache-ing and library upgrading a
little simpler.</li>
</ul>
<h3>Version 2.4.1 — October 25, 2018</h3>
<ul>
<li><em>bug</em>: adjust 404 page text.</li>
</ul>
<h3>Version 2.4.2 — September 2, 2019</h3>
<ul>
<li><em>bug</em>: limit width of images on index pages to 100%.</li>
</ul>
<h3>Version 2.4.3 — September 2, 2019</h3>
<ul>
<li><em>bug</em>: upgrade Tipue Search to version 7.1, and update templates to match.</li>
</ul>
<h3>Version 2.4.4 — June 26, 2020</h3>
<ul>
<li><em>bug</em>: use local version of fonts. (see <a href="https://github.com/MinchinWeb/seafoam/issues/16">issue
16</a>.)</li>
</ul>
<h3>Version 2.4.5 — July 16, 2020</h3>
<ul>
<li><em>bug</em>: have bullet points list separators go to the next line.</li>
<li><em>bug</em>: only display comment count if there are comments.</li>
</ul>
<h3>Version 2.4.6 — July 18, 2020</h3>
<ul>
<li><em>bug</em>: add a new non-breaking spaces to help flow of article details on blog index.</li>
</ul></body></html>Covid and Bankruptcy2020-04-29T00:00:00-06:002020-11-21T16:00:00-07:00Wm. Minchintag:blog.minchin.ca,2020-04-29:/2020/04/covid-and-bankruptcy.htmlSo we are about two months in to the local <span class="caps">COVID</span> crisis. I work from home and
am healthy, so not a tonne has changed in my day to day (although there are
more people running around the house). But my company almost went bankrupt.<html><head></head><body><p>So we are about two months in to the local <span class="caps">COVID</span> crisis. I work from home and
am healthy, so not a tonne has changed in my day to day (although there are
more people running around the house). But my company almost went bankrupt.</p>
<h2>Staring Down Bankruptcy</h2>
<p>My company provides what is arguable an “essential service” and operates in a
pretty regulated industry, and so changing government regulations is always a
background concern. That said, the regulations are both good and bad: they’re
complicated and can add cost a lot of money to comply with but they have also
kept some much larger potential competitors from entering the market.</p>
<p>So about 7 weeks ago, the government announced (on television, without
notifying us beforehand) that we are not longer (temporarily) allowed to force
our customers to pay us, and that we can’t refuse service to those customers
who have decided not to pay. In following communications, it became clearer
that this was meant as a <em>deferral</em> program and not a bill <em>holiday</em> (the term
the government used in their original press release…yeah). On the face of it,
this should quickly drive us to a cash flow crisis: by the time the customer
pays us normally, over 90% of that money has already gone out the door to pay
our suppliers, leaving under 10% for our internal costs and a tiny sliver of
that to take home as profit or to save against a rainy day. The government
fairly quickly reached out to the industry as a whole, and assured us that they
were working on a backstop plan to cover the cashflow issues, but they had no
specifics beyond a start and end date of the program.</p>
<p>I spent that first night terrified I was going to loose the company, and
without government support, it wouldn’t have lasted the month. Today, there was
a meeting with those on the government side and they still don’t have the
program figured out. But what has allowed me to sleep these last weeks is the
assurance that if the government doesn’t backstop my company (and we go
bankrupt), it won’t only be me but a bunch of my competitors too, and then my
suppliers are in for a world of hurt, if not bankruptcy themselves; the
government might not care about me 9 I’m just a little fish…), but they would
have hell to pay from the populous if the entire industry gets blown into disarray.</p>
<h2>Bankruptcy and Renewal</h2>
<p>Stepping beyond my personal circumstances, bankruptcy is supposed to play a
vital part in the renewal of our (capitalist) economy: historically (or
mythically), when weak, or just plain unlucky, companies go bankrupt, the
current owners get wiped out; the current creditors get some portion of their
money back (or ownership in the renewed company) but have no further claim; the
the company (or the company’s assets) pass to a new owner without the overhang
of the old debt load. If this works well, we (as society) see the company
re-invigorated, as well as the communities the company operates in.</p>
<p>This change-over on the back of bankruptcy (or near bankruptcy) tends to favour
consolidation in an industry, and taken to an extreme, you can end up with
situations like the original Standard Oil where a single company can ride
bankruptcies to monopolizing an industry.</p>
<p>However, a wave of bankruptcies has also sometimes completely opened up an
industry for the better:</p>
<ul>
<li>Example 1: In the mid-1800’s, railways were the “hot new thing” and so
railroad lines extremely overbuilt in the <span class="caps">US</span> as all sorts of people rushed in
to try and take advantage of the moment. A wave of bankruptcies allowed new
companies into the industry at a much lower capital cost, and thus shipping
by railway became much cheaper.</li>
<li>Example 2: During the DotCom boom (in the late 1990’s and early 2000’s),
tonnes of fibre connections were built around the globe. After a wave of
bankruptcies, this “dark” fibre found it’s way to new owners and
communication costs dropped as these companies started to try and put their
new fibre to work. To a considerable extent, this is a reason outsourcing
computer work to India (from the <span class="caps">US</span>) became affordable.</li>
</ul>
<p>However, if your upbringing was somewhat like mine, you were instilled with a
moral obligation to pay back your debts, and thus bankruptcy is something of a
moral failing (because your debts “go away”). Although society probably runs
much better with a default assumption that debts will get repaid, the interest
rate charged on a loan is supposed to compensated the lender against the risk
that they might not be repaid.</p>
<p>Together, Covid should bring a wave of bankruptcies; however, I think we’re
unlikely to see that among large corporations as the government fears the mass
disruption that would bring. We saw this during the 2008 crisis, when several
companies were deemed “to big to fail” (i.e. too big to exist??): banks but
also large manufacters like <span class="caps">GM</span>. If such a thing (more bankruptcies) were to
happen, this would serve to deleverage the economy and would likely be
deflationary, and thus would favour those with cash on hand. As well, it might
be the most straightforward way to the Debt Jubilee some quarters have been
clamouring for.</p>
<p>However, companies today run cash-lean, often to an extreme. I remember one
time being pointed to the recent financial statements of the large company I
used to work at (the manager thought this would be impressive to us): their
cash on hand equalled <strong>two days</strong> of expenses! I wondered if my next paycheque
would bounce…. Spoiler: it didn’t; somehow, they raised something like $350
million dollars that week. But this exact scenario is part of what turned the
2008 crisis into such a mess: many companies were used to borrowing to cover
short term needs (like cash to cover payroll) couldn’t anymore (with the change
happening basically overnight) and had basically no cash on hand to make up the
sudden shortfall.</p>
<p>On the plus side, running companies lean like this allows entry into new
markets with much less capital (and thus opens up all sorts of fields to
entrepreneurs, including my current business).</p>
<p>However, many companies have shifted from trying to make any sort of product to
just chasing financial returns. And one of the best ways to increase your
cash-on-cash return is to lower your initial investment!</p>
<p>Personally, I think the economy would be better off if the threat of
bankruptcy became real again. I think it would make companies more honest about
what the risks they face are, and more prepared to meet them.</p></body></html>Covid Personal Update — One Month In2020-04-13T00:00:00-06:002020-11-21T15:49:00-07:00Wm. Minchintag:blog.minchin.ca,2020-04-13:/2020/04/covid-at-one-month.htmlSo we’re maybe a month into the semi-quarantine imposed by <span class="caps">COVID</span> going around.
As of yet, I don’t know anyone that’s come down with it, and I’m okay if it
stays that way.<html><head></head><body><p>So we’re maybe a month into the semi-quarantine imposed by <span class="caps">COVID</span> going around.
As of yet, I don’t know anyone that’s come down with it, and I’m okay if it
stays that way.</p>
<p>I am starting to wonder how long this quarantine will last though, although I
realize this is one of those things that no one <em>really</em> knows right now. At
this point, no one really knows when the restrictions will end. “Social
distancing” and “flatten the curve” have become the buzzwords <em>du jour</em>;
basically we are encouraged that by staying home we <em>really are</em> doing our part.</p>
<p>In general, having the kids home has been good. They seem to be less
emotionally drained at the end of the day compared to school/daycare. We have
sort of settled into a remote learning routine, with a couple of hours of group
video chat a week, and daily worksheet. An interesting observation is that the
kids <em>could</em> do the worksheets on the computer, but <em>we</em> (the parents) found it
unworkable because the kids hadn’t figured out how to do “good enough” on the
computer yet, and would strive for perfection, even if that meant deleting all
their work and recommencing several times. For our (the parents’) sanity, it
proved to be better to print the worksheets, and let them do it by hand with
pen and paper. Overall, it felt much like Summer Break had come early.</p>
<p>The most noticeable downside of this whole set up (the kids and my Honey home)
is that it has thrown my attempts to build a “morning routine” out the window.
Basically, there is nothing external to attach a routine to. For my sanity, it
just works better to all get up about the same time, and my bedtime is largely
dictated by when we get the kiddos in bed, and then add a little bit of
Honey-and-me time.</p>
<p>Thus actual timing of bedtime is thus without much in the way of an anchor in
either the morning or the evening, and instead we try to make sure everyone is
well slept rather than worrying about timelines as much. On that account, I
think we’re succeeding at least.</p></body></html>Personal Update on COVID-19 — The “Beginning”2020-03-16T00:00:00-06:002020-11-21T15:40:00-07:00Wm. Minchintag:blog.minchin.ca,2020-03-16:/2020/03/covid-the-beginning.htmlI thought I should write a (small?) note on the virus (<em>Novel Coronavirus
(2019-nCoV)</em>) currently overrunning the world. Events seems to be happening so
fast, that I think it will be hard to remember how I felt at various points otherwise…<html><head></head><body><p>I thought I should write a (small?) note on the virus (<em>Novel Coronavirus
(2019-nCoV)</em>) currently overrunning the world. Events seems to be happening so
fast, that I think it will be hard to remember how I felt at various points otherwise…</p>
<p>Firstly, it’s been amazing how fast the world has changed. Almost exactly a
month ago (on February 18), we left on a trip to Tahiti. At that point, I’d
heard about the virus, but it seemed to be basically limited to China and a few
surrounding counties (Japan, South Korea, Hong Kong, etc). Honestly, it seemed
like something, in Canada, we could mostly ignore; maybe like <span class="caps">SARS</span>.</p>
<p>Our first clue that this was serious was when we boarded our flight from San
Francisco to Pape’ete, Tahiti and they announced that anyone who had travelled
to several parts of Asia since the start of the year needed a note from their
doctor, no less than 5 days old, stating that they were in good health. The
airline announced it again and again as we sat on the tarmac, getting ready to
leave; I got the vibe it was a brand new policy.</p>
<p>When we landed in Pape’ete, we were greeted by two nurses who took everyone’s
temperature, and then sent us on our way.</p>
<p>When we returned to Canada 16 days later (on Wednesday, March 4), the health
concerns seemed even more remote. There wasn’t even someone with a thermometer
this time. We were asked if we had been to Iran or China in the last 14 days,
but otherwise unquestioned. Our transit througuh the <span class="caps">US</span> was similiar. My Honey
is normally scheduled to work Thursday mornings, and so 6 hours after our
flight landed, she was at work, with her boss thanking her for coming in (that
shift was supposed to been covered by someone else…).</p>
<p>We watched Italy go into crisis, heard that Iran was bad, and saw a few cases
show up in Toronto (the other end of the country).</p>
<p>And then in a weekend, it hit home. That Friday work ended; the kids came home
from another day of school. My Honey was to start a new job at a daycare the
following Monday. We settled in to do weekend-y things. Sunday, though, the
bombshell dropped: school were closed, public pools were closed, daycares were
closed, all effective immediately. There was no lead time for any of this;
personal effects were still scattered at work and school. There was no
corresponding announcement about how remote schooling was supposed to work,
just that details would follow. And there was no end date in sight and no
attempt to even provide one. All of a sudden, most of the household had nowhere
to be Monday morning….</p>
<p>Luckily, I was already working from home, and with my Honey (temporarily?)
jobless, she was around to look after the kids. I’m not sure we would be
“successful” if we were both trying to work from home with the kids underfoot….</p></body></html>Image Process Plugin 1.2.0 for Pelican Released2019-08-18T17:13:00-06:002019-08-18T17:13:00-06:00Wm. Minchintag:blog.minchin.ca,2019-08-18:/2019/08/image-process-120.html
<p>Version 1.2.0 of the plugin has been released and
posted <a href="https://pypi.org/project/minchin.pelican.plugins.image-process/">PyPI</a>.</p>
<p>The biggest change this version brings is support for Pelican version 4. Thanks
to Nick Perkins for <a href="https://github.com/MinchinWeb/minchin.pelican.plugins.image_process/issues/5">reporting the
issue</a>,
and to Therry van Neerven for providing a <a href="https://github.com/whiskyechobravo/image_process/pull/19">Pull
Request</a> I could crib
a solution from.</p>
<html><head></head><body><p><em>Image Process</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>Image Process</em> let you automate the processing of images based on their
class attribute. Use this plugin to minimize the overall page weight and
to save you a trip to Gimp or Photoshop each time you include an image
in your post.</p>
<p><em>Image Process</em> is used by
<a href="https://github.com/MinchinWeb/seafoam">this blog’s theme</a> to resize the source
images so they are the correct size for thumbnails on the main index page and
the larger size they are displayed at on top of the articles.</p>
<h2>This Release</h2>
<p>Version 1.2.0 of the plugin has been released and
posted <a href="https://pypi.org/project/minchin.pelican.plugins.image-process/">PyPI</a>.</p>
<p>The biggest change this version brings is support for Pelican version 4. Thanks
to Nick Perkins for <a href="https://github.com/MinchinWeb/minchin.pelican.plugins.image_process/issues/5">reporting the
issue</a>,
and to Therry van Neerven for providing a <a href="https://github.com/whiskyechobravo/image_process/pull/19">Pull
Request</a> I could crib
a solution from.</p>
<p>I’ve also made some improvements in the test suite. It still fails on Windows
due to issues with filepath separators, but most tests now pass on Travis. The
remaining failing test appears to be due to some changes in exactly how Pillow
(the image processing library used here) transforms the images.</p>
<h2>Upgrading</h2>
<p>To upgrade simply use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.image_process<span class="w"> </span>--upgrade
</code></pre></div></body></html>CName Plugin 1.2.1 for Pelican Released2018-10-25T20:37:00-06:002018-10-25T20:37:00-06:00Wm. Minchintag:blog.minchin.ca,2018-10-25:/2018/10/cname-plugin-121-for-pelican-released.html
<p>Writing these posts about the new releases is often a little funny because the
changes made are often so small that they don’t really feel worthy of their own
post, but collectively, they start adding up. So this post actually covers five
releases combined: 1.0.3, 1.0.4, 1.1.0, 1.2.0, 1.2.1.</p>
<html><head></head><body><p><strong>CName</strong> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><strong>CName</strong> creates a <em><span class="caps">CNAME</span></em> file in the root of your output directory. This
is useful when you are publishing your site to
<a href="https://pages.github.com/">GitHub Pages</a> on a
<a href="https://help.github.com/articles/using-a-custom-domain-with-github-pages/">custom domain</a>.</p>
<h2>Updates</h2>
<p>Writing these posts about the new releases is often a little funny because the
changes made are often so small that they don’t really feel worthy of their own
post, but collectively, they start adding up. So this post actually covers five
releases combined: 1.0.3, 1.0.4, 1.1.0, 1.2.0, 1.2.1.</p>
<ul>
<li><strong>v1.0.3</strong> was updated to include the <a href="https://pypi.org/search/?c=Framework+%3A%3A+Pelican+%3A%3A+Plugins">Framework :: Pelican ::
Plugins</a>
classifier that had been added to PyPI. Sadly, there hasn’t been much uptake
of the classifer: I have 6 of the 7 packages listed. But maybe I should be so
surprised: Pelican plugins have traditionally been distributed via a large
<a href="https://github.com/getpelican/pelican-plugins">shared repository</a> rather
than via PyPI.</li>
<li><strong>v1.0.4</strong> was released to make sure the license file was included in the
distribution uploaded to PyPI.</li>
<li><strong>v1.1.0</strong> was me reworking the release process to be based on my
<em><a href="https://github.com/MinchinWeb/minchin.releaser">minchin.releaser</a></em>. I was
already using an earlier version of the scripts, but I find it helpful to
have my release process standardized and semi-automated. This in turn is part
of the reason there have so many small releases: it’s easy to do. So easy,
that these last three releases were all pushed out today!</li>
<li><strong>v1.2.0</strong> added support for protocol-less <code>SITEURL</code> settings. I’ve been in
the process (for some time now) of moving my site to <span class="caps">HTTPS</span>. However,
sometimes a site will be available on both <span class="caps">HTTP</span> and <span class="caps">HTTPS</span>, and so to serve
the same files, you can specify links without a protocol using just the
double slashes: i.e. <code>SITEURL = "//minchin.ca"</code>. Because of this, the main
part of my site (<a href="https://minchin.ca">https://minchin.ca</a>) is now available on both <span class="caps">HTTP</span> and
<span class="caps">HTTPS</span>.</li>
<li><strong>v1.2.1</strong> limits some of the internal text replacements to try and avoid
future bugs.</li>
</ul>
<p>You should be able to update through pip without any issues</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>--upgrade<span class="w"> </span>minchin.pelican.plugins.cname
</code></pre></div>
<p>The code for this project is available on
<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.cname">GitHub</a>.
Contributions are welcome!</p></body></html>Building a Traffic Model2018-06-07T14:01:00-06:002018-06-07T14:01:00-06:00Wm. Minchintag:blog.minchin.ca,2018-06-07:/2018/06/traffic-model-start.htmlI’ve decided to start a new project: building a traffic model.<html><head></head><body><p>I’ve decided to start a new project: building a traffic model.</p>
<h2>Schooling</h2>
<p>I went to school and got my undergrad in Civil Engineering. The classes and
work experience I liked best revolved around traffic and transportation
planning. When I came across the opportunity to get my Masters in
transportation planning, I jumped at it. Following school, I was able to work
in the discipline for a time, but then I tried to change jobs, had it blow up,
and then discovered there no more work in the field. It turns out my job
prospects were tied much more closely to the economy generally and oil prices
specifically than I’d expected. I’ve sent out hundreds of resumes, landed a few
interviews, but never an engineering job offer. That was five years ago.</p>
<h2>Intermission</h2>
<p>In the intervening time, I’ve kept busy. I was starting to learn Python about
the time I changed engineering jobs and have used it extensively since. I’ve
tried staying in the loop for engineering, but never felt particularly skilled
at that sort of networking. I’ve worked some other jobs and built a company.
Through it all, the bills have been paid. But it feels like time to put more
time and effort into getting back into traffic engineering. But by combining my
programming skills, my business experience, and my technical background, I hope
to do something more effective (and fun!) than just firing off random resumes.
Hence building a traffic model.</p>
<h2>Today</h2>
<h3>Scope</h3>
<p>Scope for this project is rather had to define from the onset, and as long as I
keep having fun, I’ll probably keep expanding it. Just the same, it’s helpful
to have a scope defined to help understand what to focus on. So here is my
“starter scope”:</p>
<ol>
<li>Implement the 4-step model in Python.</li>
<li>Visual the model output; generated 3D video is the ultimate goal.</li>
<li>Explore and attempt to model multi-modal transportation systems.</li>
<li>Explore and attempt to quantify and model the impact of that new
technologies such as driverless cars and smart roads will have on the
transportation system.</li>
</ol>
<h3>Measures of Success</h3>
<p>I expect this project will take some time, and be somewhat large, and so I
figure it’s worthwhile to lay out some of the ways I’ll know if this is a
success. These, of course, are subject to change, and for now, remain mostly
qualitative rather than quantitative.</p>
<p>I’ll consider the project a success if it accomplishes several of the following:</p>
<ul>
<li>expands my Python skills.<ul>
<li>In particular, I expect that this project will become rather complex, and
when dealing with complex program, one has to organize its functionality
much more deliberately.</li>
<li>The project will consume and produce large datasets. I want to learn more
about dealing with such datasets.</li>
<li>I want to explore options for parallel processing.</li>
</ul>
</li>
<li>refreshes and expands my traffic planning skills. I’m planning to learn more
about traffic planning, rather than just reiterate was I already know. One
exciting option would be to somehow turn this project in a PhD degree.</li>
<li>earns me money. I imagine it could happen in a number of ways:<ul>
<li>turns into a full-time engineering job</li>
<li>turns into a full-time programming job</li>
<li>provides engineering consulting opportunities</li>
</ul>
</li>
<li>makes “pretty pictures”. I think this is essential to explaining the results
of the model to a lay audience (including both hiring managers and
politicians). These are the particular forms of pictures come to mind:<ul>
<li>2D images: This is the standard for much of this type of work, and
probably where I’ll start.</li>
<li>2D video: showing how 2D images change based on different parameters
(time of day, future time horizons).</li>
<li>3D video: I think this is one of more exciting things I can do with the
model. Current 3D video options tend to be very expensive, and so
rarely used. Thus, it becomes a high value skill for me to develop.
Plus, I think this is one of the most striking ways to explain the
power of the model, its results, and implications.</li>
<li>replace a video game’s traffic model. At the moment, I’m thinking of
Cities: Skyline, which is a city building game I enjoy playing (and
where the top image comes from). The game can be changed (“modded”)
through plugins written in C#, so it may well the possible to swap out
the game’s traffic model for my own.</li>
</ul>
</li>
<li>is fun!</li>
</ul>
<h3>Next Steps</h3>
<p>This post was the first concrete step toward building my traffic model,
particularly defining the goals of the project. Here are some of the next steps
that need to happen:</p>
<ul>
<li>register the project name of PyPI;</li>
<li>create a repository of the project code on GitHub;</li>
<li>set up basic project structure;</li>
<li>write a blog post reviewing the <em>4 Step Model</em>, which will serve as the basic
structure for my traffic model;</li>
<li>set up a basic project website; and</li>
<li>look into the availability of free/trial/educational copies of other
transportation planning and modeling software. Part of the is to avoid
re-inventing the wheel, but also to provide inter-operability with current
industry tools.</li>
</ul>
<h2>Conclusion</h2>
<p>So, the journey begins. I’ll post updates here to my blog, as appropriate.</p></body></html>On Vacations and Oceans2018-06-06T17:29:00-06:002018-06-06T17:29:00-06:00Wm. Minchintag:blog.minchin.ca,2018-06-06:/2018/06/trip-to-the-ocean.html<p>After 5 years away, I return to the ocean.</p><html><head></head><body><p>I recently returned from a trip to the ocean. I grew up far from the ocean, and
so perhaps that’s why it holds a certain, almost mystical, place in my psyche.
It has been at least 5 years since the last time I’d been, and so about a
month ago I commented, on a whim, that it was time to go again and I’m
surrounded by amazing people who made it happen.</p>
<p>The distance from home to the ocean is measured in <em>days</em>. <em>Days</em>, you might
keenly notice, is not a traditional measure of distance, but rather is a
practical measure of how long it takes to cover the distance. In this case, the
ocean was <em>2 days</em> away, or <em>16 hours</em> driving, or about 1,500 km. By way
of comparison, in Europe you could easily visit 3 or 4 countries on a trip
that long!</p>
<p>But the drive was beautiful.</p>
<div class="text-center image-process-col-9">
<p><img alt="Forested Road to the Ocean" src="https://blog.minchin.ca/images/2018/road_to_the_ocean.jpg"/></p>
</div>
<p>The landscape I travelled through was wild and empty. There was one stretch of
200 km without so much as a gas station. The biggest town along the whole
drive was about halfway along, with a population of 70,000. There were perhaps
another half dozen towns with populations between 10 and 15 thousand, but
mostly I was left to admire the trees. I also found two black bears ambling
along the side of the road.</p>
<p>Once I got to the coast, I actually didn’t spend much time looking out over
the ocean. Part of this was due to the fact that the town I ended up in was in
a sort of fjord, so it wasn’t exposed to the full weight of the expansive sea.
But the influence of the ocean was everywhere: it rained more days than not
while I was there, the joggers weren’t deterred by the rain, the trees and
plants seemed greener, and I ate some of the best Fish <span class="amp">&</span> Chips I’ve eaten (and
I like Fish and Chips).</p>
<p>One of my days there, I joined a tour up the coast. The boat was a catamaran
and thus fast (25-ish knots) and so we were able to travel relatively far. I’d
hoped to see whales, but it turned out to be the wrong season. But we were
lucky enough to see some Grizzly bears in the wild!</p>
<div class="text-center image-process-col-9">
<p><img alt="Grizzly Bears" src="https://blog.minchin.ca/images/2018/grizzly_bears_2.jpg"/></p>
</div>
<div class="text-center image-process-col-9">
<p><img alt="Grizzly Bears" src="https://blog.minchin.ca/images/2018/grizzly_bears_1.jpg"/></p>
</div>
<p>Over all, it was a great break from work and my normal routine. And I hope to
return much sooner than in 5 years from now.</p></body></html>TRB (Transportation Research Board) Bibliography Style Updated for Word 2013/20162017-08-30T12:04:00-06:002017-08-30T12:04:00-06:00Wm. Minchintag:blog.minchin.ca,2017-08-30:/2017/08/trb-bibliography-style-for-word-updated.html<p>Updating my version of the Transportation Research Board (<span class="caps">TRB</span>) reference style for use with the reference manager in Microsoft Word 2013 and 2016.</p><html><head></head><body><div class="text-center">
<p><img alt="Word 2016 Reference Tab" src="https://blog.minchin.ca/images/2017/word-2016-reference-tab.png"/></p>
</div>
<p>Seven years ago, I released a
<a href="https://blog.minchin.ca/2010/07/trb-transportation-research-board-bibliography-style-for-word-2007-2010.html"><span class="caps">TRB</span> (Transportation Research Board) Bibliography Style for Word 2010</a>.
In the years since, that post has proved to be one of my most popular. However,
after the initial project I needed it for, I haven’t needed it again, and so it
just let it lie. Then, internal changes in Word made the style un-usable with
the 2013 release. I was disappointed, but unsure of how to fix it.</p>
<p>So imagine my delight last week to receive an email, out of the blue, from
Maggie McNamara saying that she’d found a way to update the Bibiliography style
for Word 2013! As a bonus, she’s also added <span class="caps">DOI</span> support (which I’d never heard
of when I first created the style).</p>
<h3>Installation</h3>
<p>This updated version should work with all versions of Word after 2007 (although
I haven’t personally been able to test them all). One thing that does change
between Word versions is where to put the <span class="caps">XSL</span> file:</p>
<dl>
<dt>Word 2007 (Windows)</dt>
<dd><code><winword.exe directory="">\Bibliography\Style</winword.exe></code></dd>
<dt>Word 2010 (Windows)</dt>
<dd><code><winword.exe directory="">\Bibliography\Style</winword.exe></code></dd>
<dt>Word 2010 (32 bit systems) (Windows)</dt>
<dd><code>%programfiles%\Microsoft Office\Office14\Bibliography\Style</code></dd>
<dt>Word 2016 and Office 365 (Windows)</dt>
<dd><code>C:\Users\<currentusername>\AppData\Roaming\Microsoft\Bibliography\Style</currentusername></code>, or
<code>%AppData%\Microsoft\Templates\LiveContent\15\Managed\Word Document Bibliography Styles</code></dd>
<dt>Word 2008 and Word 2011 (Mac <span class="caps">OS</span>)</dt>
<dd>To use the bibliography styles, right-click on Microsoft Word 2008 and
select show package contents. Put the files in <code>Contents/Resources/Style/</code>.
On most Macs with Microsoft Word 2008 this will be
<code>/Applications/Microsoft Office 2008/Microsoft Word.app/Contents/Resources/Style/</code></dd>
<dt>Word 2016 for Mac (version 15.17.0 and up)</dt>
<dd><code>/Library/AppSupport/Microsoft/Office365/Citations/</code></dd>
<dt>Office 365 (Mas <span class="caps">OS</span>)</dt>
<dd><code>/Applications/Microsoft Word.app/Contents/Resources/Style</code></dd>
</dl>
<h3>Example Output</h3>
<p>In-text references are numeric, based on reference order.</p>
<p>Repeated references reuse the original number.</p>
<blockquote>
<p>… Different pedestrian behaviour is associated not only
with different physical characteristics but also the differing purpose
of
pedestrians (<em>1, 2</em>). Studies have been
carried out for crowds associated with transportation systems (<em>3, 4,
5</em>), sporting and
general spectator occasions (<em>6</em>), holy sites (<em>7, 8</em>), political
demonstrations (<em>9</em>),
and fire escapes (<em>10</em>).</p>
</blockquote>
<p>The bibliography output is as follows:</p>
<blockquote>
<ol>
<li>
<p>Polus, A., <span class="caps">J.L.
</span>Schofer, and A. Ushpi. Pedestrian flow and level of service.
<i>Journal of
Transportation Engineering Proceedings, <span class="caps">ASCE</span></i>, Vol. 109, 1983, pp. 46-57.</p>
</li>
<li>
<p>Toshiyuki, A.
Prediction system of passenger flow, in <em>Engineering for Crowd
Safety</em>,
Smith, R.A., and <span class="caps">J.F.</span> Dickie. Amsterdam: Elsevier, 1993, pp. 249-258.</p>
</li>
<li>
<p>Smith, R.A., and <span class="caps">J.F.
</span>Dickie. <em>Engineering for Crowd Safety</em>. Amsterdam, 1993.</p>
</li>
<li>
<p>Tanaka, T. A study for
performance based design of means of escape in fire, in <i>Fire Safety
Science
— Proceedings of the 3rd International Symposium</i>, Cox, G., and B.
Langford.
Amsterdam: Elsevier, 1991, pp. 729-738.</p>
</li>
<li>
<p>Wikipedia
contributors. List of metro systems. <em>Wikipedia, The Free
Encyclopedia</em>,
June 17, 2010.
<a href="http://en.wikipedia.org/w/index.php?title=List_of_metro_systems&oldid=368658114">http://en.wikipedia.org/w/index.php?title=List_of_metro_systems&oldid=368658114</a>.
Accessed June 17, 2010.</p>
</li>
</ol>
</blockquote>
<h3>Known Issues</h3>
<ul>
<li>in Word 2016, it lists the style as <em>First Edition</em>, and I haven’t figured
out how to change this yet.</li>
</ul>
<h3>Download</h3>
<p><a href="http://minchin.ca/TRB_Minchin.ca.2013.XSL"><span class="caps">TRB</span> Bibliography format for Word
2007/2010/2013/2016</a> (right-click and select
“Save Link As…”)</p>
<p>Thanks again Maggie for the update!</p>
<h3>Links</h3>
<ul>
<li><a href="https://blog.minchin.ca/2010/07/trb-transportation-research-board-bibliography-style-for-word-2007-2010.html">Original Release Post</a>
(July 7, 2010)</li>
<li><a href="https://github.com/codingo/BibWord">BibWord source code</a> — This is the
original project for creating custom Bibliography styles for Word. If you
need a style other than <span class="caps">TRB</span> and the built-in styles, this may provide a
starting point for you to build your own custom style.</li>
</ul></body></html>PhotoSorter Python script 2.1.0 Released2017-08-28T15:16:00-06:002017-08-28T15:16:00-06:00Wm. Minchintag:blog.minchin.ca,2017-08-28:/2017/08/photosorter-210-released.html<p><em>Photosorter</em> is a little Python script to keep my photos from Dropbox organized.</p>
<p>It watches a <em>source directory</em> for modifications and moves new image files to
a <em>target directory</em> depending on when the photo was taken, using <span class="caps">EXIF</span> data and
creation date as a fallback. There is also an option to move existing photos.</p>
<html><head></head><body><p><em>Photosorter</em> is a little Python script to keep my photos from Dropbox organized.</p>
<p>It watches a <em>source directory</em> for modifications and moves new image files to
a <em>target directory</em> depending on when the photo was taken, using <span class="caps">EXIF</span> data and
creation date as a fallback. There is also an option to move existing photos.</p>
<p>Directory and file names follow a simple naming convention
(<code>YYYY-MM/YYY_MM_DD/YYYY-MM-DD hh:mm:ss.ext</code>) that keeps everything neatly
organized. Duplicates are detected and ignored based on their <span class="caps">SHA1</span> hash and
folder path. Photos taken in the same instant get de-duplicated by adding a
suffix (<code>-1</code>, <code>-2</code>, etc) to their filenames.</p>
<p>The result looks somewhat like this::</p>
<div class="highlight"><pre><span></span><code>├── 2013-01
│ ├── 2013_01_05
│ │ ├── 2013-01-05\ 13.24.45.jpg
│ │ ├── 2013-01-05\ 14.25.54.jpg
│ │ └── 2013-01-05\ 21.28.48-1.jpg
│ ├── 2013_01_06
│ │ ├── 2013-01-06\ 16.05.02.jpg
│ │ ├── 2013-01-06\ 19.59.25.jpg
│ │ ├── 2013-01-06\ 20.40.28.jpg
│ │ └── 2013-01-06\ 21.14.38.jpg
│ └── 2013_01_08
│ └── 2013-01-08\ 11.45.51.jpg
├── 2013-02
| └─ ...
├── ...
├── 2013-12
├── 2014-01
├── 2014-02
├── ...
├── 2014-12
├── ...
</code></pre></div>
<p>I use <code>C:\Users\[windows username]\Dropbox\Camera Uploads</code> as the source
directory and <code>Z:\Photos</code> as the target. This allows me to move my photo from
Dropbox to a local drive, and merge them with the rest of my photo collection.</p>
<h2>Impletmentation Notes</h2>
<p>I use a different folder set-up that Dan (in his original script) used. The one
I’m using matches the default folder set-up for my Canon camera.</p>
<h2>Installation</h2>
<p>The easiest way to install the script is through pip::</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.scripts.photosorter
</code></pre></div>
<h2>Requirements</h2>
<p>The script’s requirements will be automatically installed in the script is
installed via <em>pip</em> as recommended above. They can also be installed manually,
if required::</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>argcomplete><span class="o">=</span><span class="m">1</span>.3.0
pip<span class="w"> </span>install<span class="w"> </span>exifread><span class="o">=</span><span class="m">2</span>.1.2
pip<span class="w"> </span>install<span class="w"> </span>watchdog><span class="o">=</span><span class="m">0</span>.8.3
</code></pre></div>
<h2>Usage</h2>
<p>Watch <code>src_dir</code> and sort incoming photos into <code>dest_dir</code>::</p>
<div class="highlight"><pre><span></span><code>photosorter<span class="w"> </span>src_dir<span class="w"> </span>dest_dir
</code></pre></div>
<p>When you’re done with it, <code>Ctrl + C</code> will end the program.</p>
<p>If you also want to move the existing files in <code>src_dir</code> (which are, by
default, ignored)::</p>
<div class="highlight"><pre><span></span><code>photosorter<span class="w"> </span>src_dir<span class="w"> </span>dest_dir<span class="w"> </span>--move-existing
</code></pre></div>
<h2>Known Issues</h2>
<ul>
<li>the tests do not currently run.</li>
<li>matching (to provide de-duplication) is based on full filepaths matching.
I.e. if the per day folder is renamed, the script will not look in the
renamed folder for photo matches.</li>
<li>Linux daemon setup is untested by myself.</li>
</ul>
<h2>Changes</h2>
<h3>2.1.0 — 2017-08-28</h3>
<ul>
<li>also move <span class="caps">MP4</span> files</li>
<li>add changelog to readme</li>
</ul>
<h3>2.0.0 — 2017-08-27</h3>
<ul>
<li>move to <code>minchin.scripts.photosorter</code> namespace</li>
<li>do releases via <code>minchin.releaser</code></li>
<li>changed generated file folder layout</li>
<li>add option to move existing files</li>
</ul>
<h2>License</h2>
<p>Distributed under the <span class="caps">MIT</span> license. See
<a href="https://raw.githubusercontent.com/MinchinWeb/minchin.scripts.photosorter/master/LICENSE.txt"><span class="caps">LICENSE</span>.txt</a>
for more information.</p>
<h2>Credit</h2>
<p>This script is a modified version of the one put together by
<a href="https://dbader.org/blog/how-to-store-photos-in-the-cloud-and-avoid-vendor-lock-in">Dan Bader</a>.
Thanks for providing a great template Dan!</p></body></html>Advanced Pelican: Automated Site Updates with Travis-CI and GitHub Pages2017-05-31T19:50:00-06:002017-05-31T19:50:00-06:00Wm. Minchintag:blog.minchin.ca,2017-05-31:/2017/05/deploying-pelican-with-travis-to-github.htmlIn this article I’ll talk about how I automated updating my
<a href="http://docs.getpelican.com/en/stable/">Pelican</a>-generated blog using a
combination of <a href="https://pypi.org/">PyPI</a>, <a href="https://travis-ci.org/">Travis-<span class="caps">CI</span></a>,
and <a href="https://pages.github.com/">GitHub pages</a>. This is something that has been
in the works since I first switched by blog to Pelican.<html><head></head><body><p>In this article I’ll talk about how I automated updating my
<a href="http://docs.getpelican.com/en/stable/">Pelican</a>-generated blog using a
combination of <a href="https://pypi.org/">PyPI</a>, <a href="https://travis-ci.org/">Travis-<span class="caps">CI</span></a>,
and <a href="https://pages.github.com/">GitHub pages</a>. This is something that has been
in the works since I first switched by blog to Pelican.</p>
<p>If you are wanting to follow along and do this yourself, I will assume that you
have a Pelican blog that you can generate locally. (Even if you move to this
method for your public version of your blog, it can be helpful to be able to
generate your blog locally to preview theme changes and new articles.) Many of
these principals will apply if you use a different static site generator, but
I’ll focus on using Pelican.</p>
<h2>Static vs Dynamic Websites</h2>
<p>Static site generators (including Pelican) are fundamentally different from
dynamic websites (such as those run on WordPress) in when the pages of your
website are generated, or rendered. With a static site, all the pages are
completed rendered beforehand, and the resulting <span class="caps">HTML</span> is uploaded to the
webserver. Dynamic websites, on the other hand, generate the page (and that one
only) as you request it. For static websites, the advantages include:</p>
<ul>
<li>web hosting is simple: you only need a web server.</li>
<li>web hosting is secure: no databases or scripting providers to secure or scale.</li>
<li>web hosting scales well: on a dynamic website, your capacity constraint is
almost always either database access or your ability to run the script that
is your website; neither of these are used by a static site at display time.</li>
<li>generally, no database: with Pelican, all site content (like this post) is
stored in text files, greatly simplifying backups and the ability to use
version control (like <a href="https://git-scm.com/">Git</a>).</li>
</ul>
<p>On the flip side, the disadvantages (or trade-offs) of static sites are:</p>
<ul>
<li>no dynamic content from the server: some dynamic content can be provided
client-side (i.e. in the web browser) through the use of JavaScript, but
you’re not going to be able to do anything that requires real-time server
data or updates. For certain sites, this will be a deal-breaker, but in my
case, I designed my site around this.</li>
<li>two computers: with static site generators, you generally end up having two
computers — one used to edit the site and its content, and a second
webserver. In general, this is a small concern because you can use your
existing desktop to edit the site and the webserver is much simpler than it
would be for a comparable dynamic site. Although not the primary focus of
this post, by the end, we will be able to update our static site from the
web, similar to a dynamic site.</li>
<li>the server can’t update itself: most dynamic sites allow you to update them
directly, with immediate effect. This is the disadvantage that this blog post
is about remedying.</li>
</ul>
<p>My solution to being able to update my Pelican site auto-magically is what this
post details. We will use GitHub for storage, PyPI for Pelican extras,
Travis-<span class="caps">CI</span> for processing (site-generation), and GitHub Pages for site hosting.</p>
<h2>GitHub for Storage</h2>
<p>My setup stores the site data and configuration as <a href="https://github.com/MinchinWeb/blog.minchin.ca">a repo on
GitHub</a>. This does require some
understanding of Git, but GitHub makes it very approachable.</p>
<p>Configuration consists of the Pelican configuration (two Python files), my
<code>requirements.txt</code> file which will tell Travis-<span class="caps">CI</span> which packages to install,
and the Travis configuration file (<code>.travis.yml</code>) itself.</p>
<p>The theme of the site (which is sometimes stored next to the site content) is
actually uploaded as a module to PyPI (see the next section).</p>
<p>The posts are stored as “flat” text files, mostly written in Markdown.</p>
<p>Comments are also stored in per-post folders, also written in Markdown.</p>
<p>One thing to be aware of is that your source material is publicly available on
the free version of GitHub. I don’t consider this an issue as my blog is
publicly available with the same content. However, this might be an issue if
you store pre-publication drafts or un-watermarked images as part of your blog.
Also consider whether you want email addresses on comments to be publicly
available. You can create private (i.e. not publicly viewable) repos if you are
on one of GitHub’s paid plans, but then you’ll also have to pay to use
Travis-<span class="caps">CI</span>.</p>
<h2>Pelican Extras on PyPI</h2>
<p>Because we are automating the generation of our site, we want to make sure we
have replicate-able builds. One way I did this was I pushed a copy of all the
plugins I used, plus my theme, to the Python package index,
<a href="https://pypi.org/">PyPI</a>.</p>
<p>Pelican, historically, has used communal repos (on GitHub) to host
<a href="https://github.com/getpelican/pelican-plugins">plugins</a> and
<a href="https://github.com/getpelican/pelican-themes">themes</a>. To use these themes and
plugins, you would clone these two repos to your local machine, and then point
your Pelican configuration to these local folders. However, maintaining these
local paths becomes extra complicated on Travis-<span class="caps">CI</span>, where the filesystem is
mostly out-of-sight and if you get the link wrong, your build will just crash.
Furthermore, versioning is complicated because you have to specify a Git commit
hash (which looks like this —> <code>0d4b2c26f8703fe9665fd4e94bba31be02e51fc8</code>),
and if you upgrade one plugin (by selecting a later commit), you end up
upgrading all plugins. Python modules, uploaded to PyPI, is an established
solution that works well for both these issues.</p>
<p>But many pelican plugins aren’t available on PyPI (although a surprising number
are). So my first order of business was to check PyPI, and upload any missing ones:</p>
<ul>
<li>four plugins
(<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.image_process">image_process</a>,
<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.summary">summary</a>,
<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.post_stats/">post_stats</a>,
and
<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.optimize_images">optimize_images</a>)
were existing plugins that I packaged on put on PyPI myself,</li>
<li>one plugin
(<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.cname">CName</a>) is
based on code I found languishing in a <a href="https://github.com/getpelican/pelican-plugins/pull/566/files">pull
request</a> to the
original Pelican Plugin repo,</li>
<li>two plugins
(<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.nojekyll/">nojekyll</a>
and
<a href="https://github.com/MinchinWeb/minchin.pelican.jinja_filters">jinja_filters</a>)
I wrote myself, and then posted to PyPI,</li>
<li>one plugin
(<a href="https://bernhard.scheirle.de/posts/2014/March/29/static-comments-via-email/">pelican_comment_system</a>)
I worked with the author to post on PyPI himself, and</li>
<li>two plugins (<a href="https://github.com/Nitron/pelican-alias">pelican-alias</a> and
<a href="https://pypi.python.org/pypi/pelican-neighbors">pelican-neighbors</a>) were
already on PyPI.</li>
</ul>
<p>Packaging other’s code and posting it to PyPI felt kind of wierd. I wasn’t so
much worried about licensing, but sometime people (me, at least) are more
attached to their project than the text of the license might suggest. To this
end, I decided to name all the plugins I posted to PyPI in my own ‘namespace’
(i.e. <code>minchin.pelican.plugin.[something]</code>); if the original author does post
the plugin to PyPI themselves, I can always point mine to their “official” version.</p>
<p>Packaging the theme was less of an emotional issue — although it is based on
DandyDev’s
<a href="https://github.com/getpelican/pelican-themes/tree/master/pelican-bootstrap3">Bootstrap3</a>
theme, I have modified it considerably, beyond what I though would be accepted
back upstream. In other words, I feel like the theme design is “mine”.</p>
<p>In specifying a Pelican theme, the Pelican configuration file expects a folder
location. Installing the theme as a Python module means that I can’t know the
file location ahead of time, but I can ask Python at run time! So my theme has
a short function:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># seafoam/__init__.py</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="k">def</span> <span class="nf">get_path</span><span class="p">():</span>
<span class="w"> </span><span class="sd">"""</span>
<span class="sd"> Shortcut for users whose theme is not next to their pelicanconf.py.</span>
<span class="sd"> Returns:</span>
<span class="sd"> str: file path to folder containing theme</span>
<span class="sd"> """</span>
<span class="c1"># Theme directory is defined as our parent directory</span>
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">Path</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span>
</code></pre></div>
<p>that is called in the pelican configuration file:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="kn">import</span> <span class="nn">seafoam</span>
<span class="n">THEME</span> <span class="o">=</span> <span class="n">seafoam</span><span class="o">.</span><span class="n">get_path</span><span class="p">()</span>
<span class="n">BOOTSTRAP_THEME</span> <span class="o">=</span> <span class="s1">'seafoam'</span>
<span class="c1"># ... other settings</span>
</code></pre></div>
<p>(Please feel free to copy this coding pattern! I borrowed it from a Sphinx theme.)</p>
<p>Things posted by me to PyPI, as a community resource, I feel should have some
utility beyond my own. The theme is perfectly usable, as is, for other site,
but doesn’t give you any simple options for changing the colour scheme, but I’m
working to change that too.</p>
<h2>Travis-<span class="caps">CI</span> for Processing</h2>
<p>Travis-<span class="caps">CI</span> really is a wonder. At it’s core, what it sets out to do is make it
simple to run automated tests for open source software. (The “<span class="caps">CI</span>” stands for
“Continuous Integration”, which is just the fancy way of saying “run the tests
all the time!”) At its core though, Travis provides a Linux virtual machine,
and therefore, we can have Travis build and publish our blog, if we can figure
out the automation steps. In Travis-land, we configure building our blog as the
“test” and uploading as the “deployment”. Travis even has a built-in
(<del>though experimental</del>) deployment procedure to <a href="https://docs.travis-ci.com/user/deployment/pages/">push a static site to
GitHub Pages</a>!</p>
<p>Several other articles exist online explaining how to publish your Pelican blog
like this via Travis, but the vast majority rely on a bash script rather than
Travis’ build-in deployment method. I didn’t want to deal with bash; working on
a Windows machine at home makes debugging bash scripts a slow and painful
process done on Travis directly. I do use <a href="http://www.pyinvoke.org/">Invoke</a> (a
Python library) to store the command I use to build the blog (mostly so I don’t
have to remember it and type it out each time), which specifies the Pelican
preference file to use, and stops the build if a Pelican warning is
encountered. The actual commend is:</p>
<div class="highlight"><pre><span></span><code>pelican<span class="w"> </span>-s<span class="w"> </span>publishconf.py<span class="w"> </span>--fatal<span class="o">=</span>warnings
</code></pre></div>
<p>So my final Travis script (below) does four things:</p>
<ul>
<li>set up Travis for a Python project,</li>
<li>installs my (frozen) Python requirements for building the blog,</li>
<li>builds my Pelican blog, and</li>
<li>pushes (“deploys”) my published blog to GitHub Pages.</li>
</ul>
<div class="highlight"><pre><span></span><code><span class="c1"># .travis.yml</span>
<span class="c1"># build using Python 3.6</span>
<span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span>
<span class="nt">python</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">3.6</span>
<span class="c1"># Cache Dependencies</span>
<span class="nt">cache</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pip</span>
<span class="c1"># we need two Linux packages for the optimize_images plugin</span>
<span class="nt">addons</span><span class="p">:</span>
<span class="w"> </span><span class="nt">apt</span><span class="p">:</span>
<span class="w"> </span><span class="nt">packages</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">optipng</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">libjpeg-progs</span>
<span class="c1"># install dependencies</span>
<span class="nt">install</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pip install --upgrade pip</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pip install -r requirements.txt</span>
<span class="c1"># build our blog</span>
<span class="nt">script</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">invoke publish_carefully</span>
<span class="c1"># deploy to GitHub pages</span>
<span class="nt">deploy</span><span class="p">:</span>
<span class="w"> </span><span class="nt">provider</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pages</span>
<span class="w"> </span><span class="nt">local_dir</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">../blog.minchin.ca-master/</span>
<span class="w"> </span><span class="nt">target-branch</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">master</span>
<span class="w"> </span><span class="nt">skip_cleanup</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">github_token</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">$GITHUB_TOKEN</span><span class="w"> </span><span class="c1"># Set in travis-ci.org dashboard</span>
<span class="w"> </span><span class="c1"># only deploy on "main" branch</span>
<span class="w"> </span><span class="nt">on</span><span class="p">:</span>
<span class="w"> </span><span class="nt">branch</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pelican</span>
</code></pre></div>
<p>As long as your code is publicly available on GitHub, Travis-<span class="caps">CI</span> is free to use.</p>
<h2>GitHub Pages for Site Hosting</h2>
<p>The actual hosting is done by GitHub Pages. There are a few settings we need
set up to get this to work the way we want:</p>
<ul>
<li>Travis-<span class="caps">CI</span> requires a GitHub Token to be able to push the generated blog to
GitHub. <a href="https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/">GitHub
help</a>
explains how to create it, and <a href="https://docs.travis-ci.com/user/deployment/pages/#Setting-the-GitHub-token">Travis’
help</a>
explains what to do with it.</li>
<li>I am publishing from the <em>master</em> branch of my repo. Be aware that Travis is
going to destroy the history of this branch each time it publishes your site,
so don’t store anything else in this Git branch.</li>
<li>I am publishing to a custom domain
(<a href="http://blog.minchin.ca/">blog.minchin.ca</a>), so I set this up here, and use
my <em>CName</em> plugin automatically to create the appropriate <code>CNAME</code> file in my
generated site.</li>
</ul>
<p><img alt="GitHub Pages settings" src="https://blog.minchin.ca/images/2017/github-pages-settings.png"/></p>
<h2>Closing Thoughts</h2>
<p>Yes, this is more work to set up, that just publishing your blog from your
desktop computer. But the advantage is now, anytime you push a change to
GitHub, your blog will automatically be updated. This change can come from your
home computer, or <em>you can do it directly from the GitHub website</em>. This means
that you can update your website from anywhere you have an internet connection!
You now have a static site that you can update from the internet. Congratulations!</p></body></html>Introduction to Pelican2017-05-31T15:41:00-06:002017-05-31T15:41:00-06:00Wm. Minchintag:blog.minchin.ca,2017-05-31:/2017/05/introduction-to-pelican.html<p>…and static site generators more generally.</p>
<p>This is a summary of a presentation on Pelican I presented on May 9, 2016. It
covers some of the building blocks of Pelican, and then provides several
examples of Pelican in use.</p>
<p>As a personal note, this presentation follows my own path of learning and
growth with these technologies. I should note that I’m not a programmer or a
web developer by trade; that everything I’ve done with Pelican has been as a
part of another job or for personal projects. I hope to show you very practical
applications of Pelican, rather than theoretical projects.</p>
<html><head></head><body><p>…and static site generators more generally.</p>
<p>This is a summary of a presentation on Pelican I presented on May 9, 2016. It
covers some of the building blocks of Pelican, and then provides several
examples of Pelican in use.</p>
<p>As a personal note, this presentation follows my own path of learning and
growth with these technologies. I should note that I’m not a programmer or a
web developer by trade; that everything I’ve done with Pelican has been as a
part of another job or for personal projects. I hope to show you very practical
applications of Pelican, rather than theoretical projects.</p>
<h2>Intended Audience</h2>
<p>I’m assuming:</p>
<ul>
<li>you know the basics of <span class="caps">HTML</span> (or are willing to learn them on your own)</li>
<li>comfortable with Python, and have it installed on your machine</li>
<li>you’ve worked with Git and Github</li>
</ul>
<h2><span class="caps">HTML</span> Was Simple</h2>
<p>There was a time when you could write <span class="caps">HTML</span> in Notepad (or really any text
editor). Your <span class="caps">HTML</span> file might have looked like this:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>My Simple Webpage<span class="p"><!--</span--><span class="nt">title</span><span class="p">></span>
<span class="p"><!--</span--><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span>I can write anything I want here. Add some <span class="p"><</span><span class="nt">b</span><span class="p">></span>bold<span class="p"><!--</span--><span class="nt">b</span><span class="p">></span> or
<span class="p"><</span><span class="nt">i</span><span class="p">></span>italics<span class="p"><!--</span--><span class="nt">i</span><span class="p">></span> text. For everything else, I'll send you to
<span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"https://www.google.com"</span><span class="p">></span>Google<span class="p"><!--</span--><span class="nt">a</span><span class="p">></span>!<span class="p"><!--</span--><span class="nt">p</span><span class="p">></span>
<span class="p"><!--</span--><span class="nt">body</span><span class="p">></span>
<span class="p"><!--</span--><span class="nt">html</span><span class="p">></span>
</span></span></span></span></span></span></span></span></code></pre></div>
<p>which would turn into something like this:</p>
<div class="well">
<p>I can write anything I want here. Add some <b>bold</b> or
<i>italics</i> text. For everything else, I’ll send you to
<a href="https://www.google.com">Google</a>!</p>
</div>
<p><strong>This is foundational!</strong> It is true that most the <span class="caps">HTML</span> we generate, or want to
generate, is more complicated that this, but understanding basic <span class="caps">HTML</span> is very
helpful to make adjustments to any website.</p>
<p>On a personal note, I found the idea of <span class="caps">HTML</span> simple to grasp as I was already
familiar with WordPerfect’s <em>Reveal Codes</em>.</p>
<h2>The <span class="caps">LAMP</span> Stack</h2>
<p>Today, many (most?) websites are run on a combination of technologies referred
to as the “<span class="caps">LAMP</span> Stack”:</p>
<ul>
<li><strong>L</strong>inux (Operating System)</li>
<li><strong>A</strong>pache (Web Server)</li>
<li><strong>M</strong>ySQL (Database/data store)</li>
<li><strong>P</strong><span class="caps">HP</span> (Scripting Language)</li>
</ul>
<p>This combination provides the basis for a <em>Dynamic Website</em>.</p>
<p>This setup is hugely common:</p>
<ul>
<li>Linux is used by 36% of websites
(<a href="http://w3techs.com/technologies/details/os-linux/all/all">source</a>)</li>
<li>Apache is used by 53% of websites
(<a href="http://w3techs.com/technologies/overview/web_server/all">source</a>)</li>
<li><span class="caps">PHP</span> is used on 82% of websites using a scripting language
(<a href="http://w3techs.com/technologies/overview/programming_language/all">source</a>)</li>
</ul>
<p>WordPress is (probably) the most common use of the <span class="caps">LAMP</span> stack, and powers 26%
of all websites!
(<a href="http://w3techs.com/technologies/overview/content_management/all">source</a>)</p>
<h2>Downtime</h2>
<p><img alt="Fail Whale" src="https://blog.minchin.ca/images/2017/lifting-a-dreamer-2009.jpg"/></p>
<p>In recent years, various technologies have stepped forward to replace parts of
the <span class="caps">LAMP</span> stack (such as Nginx for the webserver, multiple options for
databases, or even Python as the scripting language), but the basic combination
of pieces remains largely the same. The combination of all these moving parts
increases complexity, and can lead to downtime for myriad reasons. Consider
Twitter’s <em>Fail Whale</em> (above). This is the image Twitter used to show when
some part of their website wasn’t working as expected. The fact that this image
would enter into popular culture is a testament to how hard it is to manage a
<span class="caps">LAMP</span> stack as scale.</p>
<p>This is an area that Pelican, and static site generators more generally, aim to
solve by eliminating the need for a database and scripting language on your webserver.</p>
<h2>Competitors</h2>
<p>This presentation is about Pelican, but Pelican, of course, is no the only
option. Some of the competitors include:</p>
<ul>
<li>for dynamically generated sites:<ul>
<li><a href="https://wordpress.org/">Wordpress</a> — written in <span class="caps">PHP</span>, one of the most
popular ways to run a website. Personally, probably because I wasn’t
working full time with it, I seemed that every time I touched it I was
constantly trying to keep up with the security updates and trying to make
it run faster. The advantage of WordPress is that it’s hugely common, so
it’s (relatively) easy to find people to work on your site for you.
WordPress’ out-of-the-box experience is also one of the best on this
list, making it easy for non-technical people to get it up and running.</li>
<li><a href="https://www.djangoproject.com/">Django</a> — written in Python. I don’t
have much personal experience with Django.</li>
</ul>
</li>
<li>for statically generated sites:<ul>
<li><a href="https://jekyllrb.com/">Jekyll</a> — written in Ruby. This is the default
provided by Github’s pages. When I considered it, now a couple of years
ago, I couldn’t get it to run on Windows.</li>
<li><a href="http://octopress.org/">Octopress</a> — a fork of Jekyll, written to
provide expanded features, still in Ruby.</li>
<li><a href="http://www.sphinx-doc.org/en/stable/">Sphinx</a> — written in Python,
originally designed for documenting Python source code. Very powerful for
writing general content (and probably more powerful that Pelican), I
haven’t found a way I was satisfied with to use it with blog content.</li>
<li>hand-crafted, or roll-your-own — the principals behind a static site
generator are rather simple, so to roll you own could maybe be done as a
weekend project.</li>
<li>and <a href="https://www.staticgen.com/">100’s of other options</a>…</li>
</ul>
</li>
</ul>
<p>Your situation is probably different than mine, but I considered all of the
above listed options, and for various reasons, decided Pelican would be the
best fit for me.</p>
<h2>Pelican Building Blocks</h2>
<p>We’re almost ready to jump into Pelican proper, but there’s a few more things
to introduce before we jump:</p>
<ul>
<li>markup languages: Markdown, Restructured Text (ReST), and <span class="caps">HTML</span></li>
<li>tempalating: Jinja</li>
<li>layout: Bootstrap</li>
</ul>
<h3>Markdown</h3>
<p>Markdown was created to be a lightweight markup language what remains readable
even as the plain-text version. It was modelled after the conversions used in
plain text emails and Usenet.</p>
<p>Markdown is used many places around the web
(<a href="https://help.github.com/articles/basic-writing-and-formatting-syntax/">Github</a>,
<a href="https://stackoverflow.com/editing-help">Stack Overflow</a>, etc). There are
several dialects, but the core of the language is common and well-understood.</p>
<p>An example markdown Pelican post:</p>
<div class="highlight"><pre><span></span><code>Title: My super title
Date: 2010-12-03 10:20
Modified: 2010-12-05 19:30
Category: Python
Tags: pelican, publishing
Slug: my-super-post
Authors: Alexis Metaireau, Conan Doyle
Summary: Short version for index and feeds
This is the content of my super blog post.
![<span class="nt">Image Alt Text</span>](<span class="na">/path/to/image.jpg</span>)
<span class="sb">```python</span>
<span class="c1"># python code block</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"A literal block directive explicitly marked as python code"</span><span class="p">)</span>
<span class="sb">```</span>
A sentence with links to [<span class="nt">Wikipedia</span>](<span class="na">http://www.wikipedia.org/</span>) and the
[<span class="nt">Linux kernel archive</span>](<span class="na">http://www.kernel.org/</span>).
</code></pre></div>
<h3>Restructured Text</h3>
<p>Sometimes shorted as <em>ReST</em> or <em><span class="caps">RST</span></em>, this was originally created for
documenting the Python standard library. As such, it has great Python support
(but somewhat limited support in other programming languages), and a
well-defined specification.</p>
<p>An example Restructured Text Pelican post:</p>
<div class="highlight"><pre><span></span><code><span class="gh">My super title</span>
<span class="gh">##############</span>
<span class="nc">:date:</span> 2010-10-03 10:20
<span class="nc">:modified:</span> 2010-10-04 18:40
<span class="nc">:tags:</span> thats, awesome
<span class="nc">:category:</span> yeah
<span class="nc">:slug:</span> my-super-post
<span class="nc">:authors:</span> Alexis Metaireau, Conan Doyle
<span class="nc">:summary:</span> Short version for index and feeds
This will be turned into <span class="na">:abbr:</span><span class="nv">`HTML (HyperText Markup Language)`</span>.
<span class="p">..</span> <span class="ow">image</span><span class="p">::</span> /path/to/image.jpg
<span class="p">..</span> <span class="ow">code-block</span><span class="p">::</span> python
<span class="nc">:classprefix:</span> pgcss
<span class="nc">:linenos:</span> table
<span class="nc">:linenostart:</span> 153
print("A literal block directive explicitly marked as python code")
A sentence with links to Wikipedia_ and the <span class="s">`Linux kernel archive`_</span>.
<span class="p">..</span> <span class="nt">_Wikipedia:</span> http://www.wikipedia.org/
<span class="p">..</span> <span class="nt">_Linux kernel archive:</span> http://www.kernel.org/
</code></pre></div>
<h3><span class="caps">HTML</span></h3>
<p>Pelican can also use <span class="caps">HTML</span> files as a source. Post metadata is read from the
pages <code>meta</code> tags.</p>
<p>An example <span class="caps">HTML</span> Pelican post:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>My super title<span class="p"><!--</span--><span class="nt">title</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"tags"</span> <span class="na">content</span><span class="o">=</span><span class="s">"thats, awesome"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"date"</span> <span class="na">content</span><span class="o">=</span><span class="s">"2012-07-09 22:28"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"modified"</span> <span class="na">content</span><span class="o">=</span><span class="s">"2012-07-10 20:14"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"category"</span> <span class="na">content</span><span class="o">=</span><span class="s">"yeah"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"authors"</span> <span class="na">content</span><span class="o">=</span><span class="s">"Alexis Métaireau, Conan Doyle"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"summary"</span> <span class="na">content</span><span class="o">=</span><span class="s">"Short version for index and feeds"</span> <span class="p">/></span>
<span class="p"><!--</span--><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
This is the content of my super blog post.
<span class="p"><!--</span--><span class="nt">body</span><span class="p">></span>
<span class="p"><!--</span--><span class="nt">html</span><span class="p">></span>
</span></span></span></span></code></pre></div>
<h3>Jinja</h3>
<p>Jinja is a templating language (we technically use Jinja2). Pelican uses this
to define the templates that the blog content is then dropped into.</p>
<p>Jinja isn’t something you need to understand on day 1 to get your Pelican site
up and running, but eventually you’ll probably want to adjust your theme, and
for that you’ll need some Jinja.</p>
<p>A basic Jinja template:</p>
<h4>layout.html</h4>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span><span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}{%</span> <span class="k">endblock</span> <span class="cp">%}</span><span class="p"><!--</span--><span class="nt">title</span><span class="p">></span>
<span class="p"><!--</span--><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="p"><!--</span--><span class="nt">body</span><span class="p">></span>
<span class="p"><!--</span--><span class="nt">html</span><span class="p">></span>
</span></span></span></span></code></pre></div>
<h4>users.html</h4>
<div class="highlight"><pre><span></span><code><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">"layout.html"</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> -<span class="cp">%}</span>
All Users
<span class="cp">{%</span>- <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
<span class="p"><</span><span class="nt">ul</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">for</span> <span class="nv">user</span> <span class="k">in</span> <span class="nv">users</span> <span class="cp">%}</span>
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">user.url</span> <span class="cp">}}</span><span class="s">"</span><span class="p">></span><span class="cp">{{</span> <span class="nv">user.username</span> <span class="cp">}}</span><span class="p"><!--</span--><span class="nt">a</span><span class="p">><!--</span--><span class="nt">li</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
<span class="p"><!--</span--><span class="nt">ul</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</span></span></span></code></pre></div>
<p>A more advanced Jinja template:</p>
<h4>archives.html</h4>
<div class="highlight"><pre><span></span><code><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">"base.html"</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}</span>Archives - <span class="cp">{{</span> <span class="nv">SITENAME</span> <span class="cp">}}{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">breadcrumbs</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">if</span> <span class="nv">DISPLAY_BREADCRUMBS</span> <span class="cp">%}</span>
<span class="p"><</span><span class="nt">ol</span> <span class="na">class</span><span class="o">=</span><span class="s">"breadcrumb"</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">SITEURL</span> <span class="cp">}}</span><span class="s">"</span> <span class="na">title</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">SITENAME</span> <span class="cp">}}</span><span class="s">"</span><span class="p">><</span><span class="nt">i</span> <span class="na">class</span><span class="o">=</span><span class="s">"fa fa-home fa-lg"</span><span class="p">><!--</span--><span class="nt">i</span><span class="p">><!--</span--><span class="nt">a</span><span class="p">><!--</span--><span class="nt">li</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span> <span class="na">class</span><span class="o">=</span><span class="s">"active"</span><span class="p">></span>Archives<span class="p"><!--</span--><span class="nt">li</span><span class="p">></span>
<span class="p"><!--</span--><span class="nt">ol</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">content</span> <span class="cp">%}</span>
<span class="p"><</span><span class="nt">section</span> <span class="na">id</span><span class="o">=</span><span class="s">"content"</span><span class="p">></span>
<span class="p"><</span><span class="nt">h1</span><span class="p">></span>Archives for <span class="cp">{{</span> <span class="nv">SITENAME</span> <span class="cp">}}</span><span class="p"><!--</span--><span class="nt">h1</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">"archives"</span><span class="p">></span>
<span class="cp">{%</span>- <span class="k">set</span> <span class="nv">last_year</span> <span class="o">=</span> <span class="kp">None</span> -<span class="cp">%}</span>
<span class="cp">{%</span>- <span class="k">set</span> <span class="nv">last_month</span> <span class="o">=</span> <span class="kp">None</span> -<span class="cp">%}</span>
<span class="cp">{%</span>- <span class="k">set</span> <span class="nv">last_day</span> <span class="o">=</span> <span class="kp">None</span> -<span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">for</span> <span class="nv">article</span> <span class="k">in</span> <span class="nv">dates</span> <span class="cp">%}</span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"row"</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">if</span> <span class="nv">article.date.year</span> <span class="o">!=</span> <span class="nv">last_year</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">if</span> <span class="nv">last_year</span> <span class="o">!=</span> <span class="kp">None</span> -<span class="cp">%}</span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"archives-spacer col-xs-12"</span><span class="p">></span><span class="ni"> </span><span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"archives-spacer col-xs-12"</span><span class="p">></span><span class="ni"> </span><span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="cp">{%</span>- <span class="k">endif</span> <span class="cp">%}</span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"archives-date archives-year col-xs-4 col-sm-2 col-sm-offset-2"</span><span class="p">></span>
<span class="p"><</span><span class="nt">a</span> <span class="na">name</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">article.date.year</span> <span class="cp">}}</span><span class="s">"</span><span class="p">><!--</span--><span class="nt">a</span><span class="p">></span>
<span class="cp">{{</span><span class="o">-</span> <span class="nv">article.date.year</span> -<span class="cp">}}</span>
<span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"archives-title col-xs-7 col-sm-6"</span><span class="p">></span>
<span class="ni"> </span>
<span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="cp">{%</span>- <span class="k">set</span> <span class="nv">last_year</span> <span class="o">=</span> <span class="nv">article.date.year</span> -<span class="cp">%}</span>
<span class="cp">{%</span>- <span class="k">set</span> <span class="nv">last_month</span> <span class="o">=</span> <span class="kp">None</span> -<span class="cp">%}</span>
<span class="cp">{%</span>- <span class="k">set</span> <span class="nv">last_day</span> <span class="o">=</span> <span class="kp">None</span> -<span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">if</span> <span class="nv">article.date.month</span> <span class="o">!=</span> <span class="nv">last_month</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">if</span> <span class="nv">last_month</span> <span class="o">!=</span> <span class="kp">None</span> -<span class="cp">%}</span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"archives-spacer col-xs-12"</span><span class="p">></span><span class="ni"> </span><span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="cp">{%</span>- <span class="k">endif</span> <span class="cp">%}</span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"archives-date archives-month col-xs-4 col-sm-2 col-sm-offset-2"</span><span class="p">></span>
<span class="p"><</span><span class="nt">a</span> <span class="na">name</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">article.date.year</span> <span class="cp">}}</span><span class="s">-</span><span class="cp">{{</span> <span class="nv">article.date.month</span> <span class="cp">}}</span><span class="s">"</span><span class="p">><!--</span--><span class="nt">a</span><span class="p">></span>
<span class="cp">{{</span><span class="o">-</span> <span class="nv">article.date</span><span class="o">|</span><span class="nf">strftime</span><span class="o">(</span><span class="s1">'%B'</span><span class="o">)</span> -<span class="cp">}}</span>
<span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"archives-title col-xs-7 col-sm-6"</span><span class="p">></span>
<span class="ni"> </span>
<span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="cp">{%</span>- <span class="k">set</span> <span class="nv">last_month</span> <span class="o">=</span> <span class="nv">article.date.month</span> -<span class="cp">%}</span>
<span class="cp">{%</span>- <span class="k">set</span> <span class="nv">last_day</span> <span class="o">=</span> <span class="kp">None</span> -<span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"archives-date categories-timestamp col-xs-4 col-sm-2 col-sm-offset-2"</span><span class="p">></span>
<span class="cp">{%</span>- <span class="k">if</span> <span class="nv">last_day</span> <span class="o">!=</span> <span class="nv">article.date.day</span> <span class="cp">%}</span>
<span class="p"><</span><span class="nt">time</span> <span class="na">datetime</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">article.date.isoformat</span><span class="o">()</span> <span class="cp">}}</span><span class="s">"</span><span class="p">></span><span class="cp">{{</span> <span class="nv">article.date</span> <span class="o">|</span> <span class="nf">strftime</span><span class="o">(</span><span class="s1">'%a %-d'</span><span class="o">)</span> <span class="cp">}}</span><span class="p"><!--</span--><span class="nt">time</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">else</span> -<span class="cp">%}</span>
<span class="ni"> </span>
<span class="cp">{%</span>- <span class="k">endif</span> -<span class="cp">%}</span>
<span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"archives-title col-xs-7 col-sm-6"</span><span class="p">></span>
<span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">SITEURL</span> <span class="cp">}}</span><span class="s">/</span><span class="cp">{{</span> <span class="nv">article.url</span> <span class="cp">}}</span><span class="s">"</span><span class="p">></span><span class="cp">{{</span> <span class="nv">article.title</span> <span class="cp">}}</span><span class="p"><!--</span--><span class="nt">a</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">if</span> <span class="nv">article.subtitle</span> <span class="cp">%}</span><span class="p"><</span><span class="nt">br</span> <span class="p">/></span><span class="cp">{{</span> <span class="nv">article.subtitle</span> <span class="cp">}}{%</span> <span class="k">endif</span> <span class="cp">%}</span>
<span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="cp">{%</span>- <span class="k">set</span> <span class="nv">last_day</span> <span class="o">=</span> <span class="nv">article.date.day</span> <span class="cp">%}</span>
<span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
<span class="p"><!--</span--><span class="nt">div</span><span class="p">></span>
<span class="p"><!--</span--><span class="nt">section</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre></div>
<h3>Responsive Design</h3>
<p><img alt="Responsive Design" src="https://blog.minchin.ca/images/2017/responsive-web-design-a-working-example.gif"/></p>
<p><em>Responsive Design</em> is a way a designing a website so that it’s layout will
change between mobile and desktop browsers, but the actual content doesn’t
change. For this, we can use <a href="http://getbootstrap.com/">Bootstrap</a>.</p>
<h2>Pelican</h2>
<h3>Quickstart</h3>
<p>Yes, that took long enough. But let’s jump into using Pelican. We can use
<code>pelican-quickstart</code> to quickly create a skeleton site.</p>
<p>First, let’s install Pelican. We also install Markdown, as it’s not
automatically installed.</p>
<div class="highlight"><pre><span></span><code>C:> pip install pelican markdown
[...]
C:> pelican-quickstart
</code></pre></div>
<p><img alt="Quickstart" src="https://blog.minchin.ca/images/2017/pelican-quickstart.gif"/></p>
<p>And now you have a basic Pelican site.</p>
<h4>(Basic) File Structure</h4>
<div class="highlight"><pre><span></span><code>yourproject/
├── content/ # articles and pages go here
├── output/
├── develop_server.sh
├── fabfile.py
├── Makefile
├── pelicanconf.py # Main settings file
└── publishconf.py # Settings to use when ready to publish
</code></pre></div>
<h3>Internal Links</h3>
<p>Pelican allows us to specify links between files. To do so, we use the <strong>source
content hierarchy</strong> rather than the generated hierarchy.</p>
<p>For example, if your site is laid out like this:</p>
<div class="highlight"><pre><span></span><code>yourproject/
├── content/
│ ├── article1.rst
│ ├── cat/
│ │ └── article2.md
│ ├── images
│ │ └── han.jpg
│ ├── pages/
│ │ ├── about.md
│ │ └── (other articles)
│ └── (other articles)
├── output
├── develop_server.sh
├── fabfile.py
├── Makefile
├── pelicanconf.py # Main settings file
└── publishconf.py # Settings to use when ready to publish
</code></pre></div>
<p>so then you would create your entry (with links) like this:</p>
<h4>article2.md</h4>
<div class="highlight"><pre><span></span><code>Title: The second article
Date: 2012-12-01 10:02
See below intra-site link examples in Markdown format.
[<span class="nt">a link relative to the current file</span>](<span class="na">{static}../article1.rst</span>)
[<span class="nt">a link relative to the content root</span>](<span class="na">{static}/article1.rst</span>)
![<span class="nt">Alt Text</span>](<span class="na">{static}/images/han.jpg</span>)
</code></pre></div>
<h3>Quirks</h3>
<p>Pelican does have a couple of other quirks:</p>
<ul>
<li>slugs for articles and pages must be unique. Duplicate slugs are assumed to
be translations.</li>
<li>Pelican itself is under the
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html"><span class="caps">AGPL</span></a>. For practical
purposes, if you are just running Pelican for yourself and pushing the output
to a server, I don’t think this is an issue. If you are running a service
that use Pelican to generate sites under the hood, there may be a requirement
to provide your full source code. (That said, get your own legal advice if
this is really a concern.)</li>
</ul>
<p>Using GitHub pages for hosting also has a couple of quirks too:</p>
<ul>
<li>use the <em>master</em> branch for your personal or organization homepage, and the
<em>gh-pages</em> branch for project pages (see <a href="https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/">GitHub
help</a>
for more details, although they have a couple more options now).</li>
<li>project pages will show up as a sub-directory of your main website. (i.e. my
<a href="https://github.com/MinchinWeb/colourettu">colourettu</a> project’s generated
site lives at <a href="http://minchin.ca/colourettu/">minchin.ca/colourettu/</a>)</li>
<li>GitHub pages will automatically run your site through Jekyll before
publishing it. I haven’t had this cause problems directly, but it does add a
delay between pushing your site to GitHub and having it go live. To remove
this, add a <code>.nojekyll</code> file to the root of your project. I created a plugin
(<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.nojekyll">minchin.pelican.plugins.nojekyll</a>)
to take care of this automatically.</li>
<li>you have use a custom domain for your GitHub pages. Create a <code>CNAME</code> file
that just contains your domain name, and change your <span class="caps">DNS</span> settings to point to
GitHub to enable this. (See <a href="https://help.github.com/articles/using-a-custom-domain-with-github-pages/">GitHub
Help</a>
for full details.) I created a plugin
(<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.cname">CName</a>) to
take care of this automatically.</li>
</ul>
<h2>Pelican Configuration — <code>pelicanconf.py</code></h2>
<p>Your configuration file for Pelican are regular Python files. This means you
can run Python function and import other Python files.</p>
<p>Pelican settings generally separated between development (<code>pelicanconf.py</code>) and
production (<code>publishconf.py</code>, which usually imports <code>pelicanconf.py</code>).</p>
<p>An example <code>pelicanconf.py</code>:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># -*- coding: utf-8 -*- #</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">unicode_literals</span>
<span class="n">AUTHOR</span> <span class="o">=</span> <span class="s1">'Wm Minchin'</span>
<span class="n">SITENAME</span> <span class="o">=</span> <span class="s1">'Introduction to Pelican'</span>
<span class="n">SITEURL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">PATH</span> <span class="o">=</span> <span class="s1">'content'</span>
<span class="n">ARTICLE_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'posts'</span><span class="p">]</span>
<span class="n">PAGE_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'.'</span><span class="p">]</span>
<span class="n">TIMEZONE</span> <span class="o">=</span> <span class="s1">'America/Edmonton'</span>
<span class="n">DEFAULT_LANG</span> <span class="o">=</span> <span class="s1">'en'</span>
<span class="c1"># Feed generation is usually not desired when developing</span>
<span class="n">FEED_ALL_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">CATEGORY_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">TRANSLATION_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">AUTHOR_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">AUTHOR_FEED_RSS</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># Blogroll</span>
<span class="n">LINKS</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'Pelican'</span><span class="p">,</span> <span class="s1">'http://getpelican.com/'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Python.org'</span><span class="p">,</span> <span class="s1">'http://python.org/'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Jinja2'</span><span class="p">,</span> <span class="s1">'http://jinja.pocoo.org/'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'You can modify those links in your config file'</span><span class="p">,</span> <span class="s1">'#'</span><span class="p">),)</span>
<span class="c1"># Social widget</span>
<span class="n">SOCIAL</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'You can add links in your config file'</span><span class="p">,</span> <span class="s1">'#'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Another social link'</span><span class="p">,</span> <span class="s1">'#'</span><span class="p">),)</span>
<span class="n">DEFAULT_PAGINATION</span> <span class="o">=</span> <span class="mi">10</span>
</code></pre></div>
<p>and an example <code>publishconf.py</code>:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># -*- coding: utf-8 -*- #</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">unicode_literals</span>
<span class="c1"># This file is only used if you use `make publish` or</span>
<span class="c1"># explicitly specify it as your config file.</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">curdir</span><span class="p">)</span>
<span class="kn">from</span> <span class="nn">pelicanconf</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">SITEURL</span> <span class="o">=</span> <span class="s1">'http://minchin.ca'</span>
<span class="n">RELATIVE_URLS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">FEED_ALL_ATOM</span> <span class="o">=</span> <span class="s1">'feeds/all.atom.xml'</span>
<span class="n">CATEGORY_FEED_ATOM</span> <span class="o">=</span> <span class="s1">'feeds/</span><span class="si">%s</span><span class="s1">.atom.xml'</span>
<span class="n">DELETE_OUTPUT_DIRECTORY</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># Following items are often useful when publishing</span>
<span class="c1">#DISQUS_SITENAME = ""</span>
<span class="c1">#GOOGLE_ANALYTICS = ""</span>
</code></pre></div>
<h2>Overview of Examples</h2>
<p>Below are four examples of sites I have build with Pelican. I have included
full configuration files here, which makes this post particularly long.
However, I hope having full working examples will help you get started on your
own site.</p>
<p>If the code below doesn’t work, please let me know so I can fix it; Pelican
does tweak its setting from time to time.</p>
<p>The four examples are:</p>
<ul>
<li><strong>jnrl</strong> — a have a personal-use site where I display the notes I keep for myself.</li>
<li><strong>Burst Energy</strong> — a full production site I used to be responsible. In this
case, it was mostly content pages rather than blog posts (although there was
a small “News” section). I have also included some of the support code I used
to generate pieces used by the website, and to format the generated site for deployment.</li>
<li><strong>Minchin.ca</strong> — my personal website (available at
<a href="http://minchin.ca">minchin.ca</a>).</li>
<li><strong>Minchin Genealogy</strong> — a site of my personal genealogy. This poses certain
issues due to it’s size (over 11,000 pages), but Pelican can still deal with
it. I have included the build script for this site below, and you will see
that Pelican is just one step — the data is cleaned before it is fed to
Pelican the resulting site is also adjusted. (Available at
<a href="http://minchin.ca/genealogy/">minchin.ca/genealogy</a>).</li>
</ul>
<p>Not listed here, but this site (<a href="http://blog.minchin.ca">blog.minchin.ca</a>), is
also generated with Pelican, and separately from the main part of Minchin.ca.
The code for the site is <a href="https://github.com/MinchinWeb/blog.minchin.ca">posted to
GitHub</a> and open to review.</p>
<h2>Example 1 — Jrnl</h2>
<p>In this example, I use Pelican to display notes that I store in
<a href="http://jrnl.sh/">jrnl</a>. I use <em>jrnl</em> to export these notes to individual
markdown files, which serve as the source files for Pelican. I uses a separate
Python program (<a href="https://github.com/MinchinWeb/prjct">prjct</a>) and a custom
Pelican template to include my todo list items on tag pages.</p>
<!-- Link to Demonstration version -->
<h3>Batch Script</h3>
<p>I use this script to (re-)generate the site from the Windows command line.</p>
<div class="highlight"><pre><span></span><code><span class="p">@</span><span class="k">ECHO</span> off
<span class="p">@</span><span class="k">ECHO</span> Delete old JRNL export
<span class="k">del</span> S:\Documents\jrnl-pelican\content\*.md
<span class="p">:</span><span class="c1">: Export from JRNL</span>
jrnl --export yaml -o S:\Documents\jrnl-pelican\content\
jrnl dayone --export yaml -o S:\Documents\jrnl-pelican\content\
<span class="p">:</span><span class="c1">: Export from PRJCT</span>
prjct project_entry S:\Documents\jrnl-pelican\content\all_projects.md
<span class="p">:</span><span class="c1">: Pelican</span>
<span class="k">cd</span> S:\Documents\jrnl-pelican
<span class="p">@</span><span class="k">ECHO</span> Running Pelican...
pelican -s publishconf.py
robocopy S:\Documents\jrnl-pelican\output\ \\myserver\web\jrnl\ /MIR /nfl /ndl
</code></pre></div>
<h3>Sample <span class="caps">JRNL</span> Exported Entry</h3>
<div class="highlight"><pre><span></span><code>title: Kurt Vonnegut's 8 Tips on How to Write a Great Story
date: 2012-10-15 14:04
stared: False
tags: whitespace, bookmarks, writing, writing_tips
<span class="gs">**The Atlantic Home**</span> --
Saturday, July 7, 2012 --
By Maria Popova --
[<span class="nt">Source</span>](<span class="na">http://www.theatlantic.com/entertainment/archive/2012/04/kurt-vonneguts-8-tips-on-how-to-write-a-great-story/255401/</span>)
Why you should be cruel to your readers
The year of reading more and writing better is well underway with writing advice
the likes of David Ogilvy's 10 no-bullshit tips, Henry Miller's 11 commandments,
Jack Kerouac's 30 beliefs and techniques, John Steinbeck's 6 pointers, and
various invaluable insight from other great writers.
Now comes Kurt Vonnegut -- anarchist, Second Life dweller, imaginary interviewer
of the dead, sad soul -- with eight tips on how to write a good short story,
narrated by the author himself.
<span class="k">1.</span> Use the time of a total stranger in such a way that he or she will not feel the time was wasted.
<span class="k">2.</span> Give the reader at least one character he or she can root for.
<span class="k">3.</span> Every character should want something, even if it is only a glass of water.
<span class="k">4.</span> Every sentence must do one of two things—reveal character or advance the action.
<span class="k">5.</span> Start as close to the end as possible.
<span class="k">6.</span> Be a Sadist. No matter how sweet and innocent your leading characters, make awful things happen to them—in order that the reader may see what they are made of.
<span class="k">7.</span> Write to please just one person. If you open a window and make love to the world, so to speak, your story will get pneumonia.
<span class="k">8.</span> Give your readers as much information as possible as soon as possible. To hell with suspense. Readers should have such complete understanding of what is going on, where and why, that they could finish the story themselves, should cockroaches eat the last few pages.
</code></pre></div>
<h3>Pelicanconf.py</h3>
<p>This is my settings file:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># -*- coding: utf-8 -*- #</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">unicode_literals</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">prjct</span>
<span class="c1"># requires pymdown-extensions</span>
<span class="n">AUTHOR</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'WM'</span>
<span class="n">SITENAME</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'Jrnl Notebook'</span>
<span class="n">SITEURL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">PATH</span> <span class="o">=</span> <span class="s1">'content'</span>
<span class="n">CUSTOM_CSS</span> <span class="o">=</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'_css/jrnl.css'</span>
<span class="n">CACHE_CONTENT</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">LOAD_CACHE_CONTENT</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">CHECK_MODIFIED_METHOD</span> <span class="o">=</span> <span class="s1">'md5'</span> <span class="c1"># default is the file's modified time, which is</span>
<span class="c1"># useless for us because we keep regenerating</span>
<span class="c1"># the source files</span>
<span class="n">TIMEZONE</span> <span class="o">=</span> <span class="s1">'America/Edmonton'</span>
<span class="n">DEFAULT_LANG</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'en'</span>
<span class="n">DEFAULT_DATE</span> <span class="o">=</span> <span class="s1">'fs'</span>
<span class="c1"># Feed generation is usually not desired when developing</span>
<span class="n">FEED_ALL_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">CATEGORY_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">TRANSLATION_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">DEFAULT_PAGINATION</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">TYPOGRIFY</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">TYPOGRIFY_IGNORE_TAGS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'code'</span><span class="p">,</span> <span class="s1">'pre'</span><span class="p">,</span> <span class="s1">'tt'</span><span class="p">]</span>
<span class="n">STATIC_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'images'</span><span class="p">,</span> <span class="s1">'_css'</span><span class="p">]</span>
<span class="n">THEME</span> <span class="o">=</span> <span class="s1">'themes/pelican-bootstrap3'</span>
<span class="n">FAVICON</span> <span class="o">=</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'images/favicon.ico'</span>
<span class="n">FAVICON_IE</span> <span class="o">=</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'images/favicon.ico'</span>
<span class="n">SITELOGO</span> <span class="o">=</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'images/favicon-48.png'</span>
<span class="n">ELEVATOR</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">BOOTSTRAP_THEME</span> <span class="o">=</span> <span class="s1">'superhero'</span>
<span class="n">USE_PAGER</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">BOOTSTRAP_NAVBAR_INVERSE</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">DISPLAY_TAGS_INLINE</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">DISPLAY_BREADCRUMBS</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># see https://pythonhosted.org/Markdown/reference.html#markdown</span>
<span class="c1"># format required for Pelican 3.7</span>
<span class="n">MARKDOWN</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'extension_configs'</span><span class="p">:</span> <span class="p">{</span>
<span class="c1"># set linenums=True for line numbers</span>
<span class="c1"># https://pythonhosted.org/Markdown/extensions/code_hilite.html</span>
<span class="s1">'markdown.extensions.codehilite'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'css_class'</span><span class="p">:</span> <span class="s1">'highlight'</span><span class="p">},</span>
<span class="c1"># on by default</span>
<span class="c1"># see https://pythonhosted.org/Markdown/extensions/extra.html</span>
<span class="s1">'markdown.extensions.extra'</span><span class="p">:</span> <span class="p">{},</span>
<span class="s1">'markdown.extensions.meta'</span><span class="p">:</span> <span class="p">{},</span>
<span class="c1"># https://facelessuser.github.io/pymdown-extensions/extensions/superfences/</span>
<span class="s1">'pymdownx.superfences'</span><span class="p">:</span> <span class="p">{},</span>
<span class="c1"># use single caret for superscript</span>
<span class="c1"># use double caret for insertion (underline?)</span>
<span class="c1"># https://facelessuser.github.io/pymdown-extensions/extensions/caret/</span>
<span class="s1">'pymdownx.caret'</span><span class="p">:</span> <span class="p">{},</span>
<span class="c1"># use single tildes for subscript</span>
<span class="c1"># use double tildes for deletion</span>
<span class="c1"># https://facelessuser.github.io/pymdown-extensions/extensions/tilde/</span>
<span class="s1">'pymdownx.tilde'</span><span class="p">:</span> <span class="p">{},</span>
<span class="c1"># https://pythonhosted.org/Markdown/extensions/smarty.html </span>
<span class="s1">'markdown.extensions.smarty'</span><span class="p">:</span> <span class="p">{},</span>
<span class="c1"># so MathJax equations (always) make it through</span>
<span class="c1"># https://facelessuser.github.io/pymdown-extensions/extensions/arithmatex/</span>
<span class="s1">'pymdownx.arithmatex'</span><span class="p">:</span> <span class="p">{},</span>
<span class="c1"># Auto-convert special symbols, like (tm) to ™</span>
<span class="c1"># https://facelessuser.github.io/pymdown-extensions/extensions/smartsymbols/</span>
<span class="s1">'pymdownx.smartsymbols'</span><span class="p">:</span> <span class="p">{},</span>
<span class="p">},</span>
<span class="s1">'output_format'</span><span class="p">:</span> <span class="s1">'html5'</span><span class="p">,</span>
<span class="s1">'lazy_ol'</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>
<span class="p">}</span>
<span class="c1"># A list of metadata fields containing reST/Markdown content to be parsed and translated to HTML.</span>
<span class="n">FORMATTED_FIELDS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'title'</span><span class="p">,</span> <span class="s1">'summary'</span><span class="p">]</span>
<span class="n">PLUGIN_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'../GitHub/pelican-plugins'</span><span class="p">,</span> <span class="p">]</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'tipue_search'</span><span class="p">,</span>
<span class="s1">'tag_cloud'</span><span class="p">,</span>
<span class="s1">'prjct.titlecase'</span><span class="p">,</span> <span class="c1"># titlecase Jinja filter</span>
<span class="p">]</span>
<span class="n">TAG_CLOUD_STEPS</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">TAG_CLOUD_MAX_ITEMS</span> <span class="o">=</span> <span class="mi">100</span>
<span class="n">prjct_release</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">prjct</span><span class="o">.</span><span class="n">__version__</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s1">'\d+\.\d+(\.\d+)?'</span><span class="p">)</span>
<span class="n">prjct_versionmatch</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">prjct_release</span><span class="p">)</span>
<span class="n">prjct_version</span> <span class="o">=</span> <span class="n">prjct_versionmatch</span><span class="o">.</span><span class="n">group</span><span class="p">()</span>
<span class="n">PRJCT</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">PRJCT_TODO</span><span class="p">,</span> <span class="n">PRJCT_DONE</span> <span class="o">=</span> <span class="n">prjct</span><span class="o">.</span><span class="n">todo_export</span><span class="o">.</span><span class="n">to_html_dicts</span><span class="p">(</span><span class="n">prjct</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">load</span><span class="p">())</span>
<span class="n">PRJCT_PROJECTS</span> <span class="o">=</span> <span class="n">prjct</span><span class="o">.</span><span class="n">todo_export</span><span class="o">.</span><span class="n">project_list</span><span class="p">()</span>
<span class="n">PRJCT_DESC</span> <span class="o">=</span> <span class="n">prjct</span><span class="o">.</span><span class="n">descriptions</span><span class="o">.</span><span class="n">to_html_dict</span><span class="p">(</span><span class="n">prjct</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">load</span><span class="p">(),</span> <span class="n">MD_EXTENSIONS</span><span class="p">)</span>
<span class="n">PRJCT_VERSION</span> <span class="o">=</span> <span class="n">prjct_version</span>
<span class="n">PRJCT_URL</span> <span class="o">=</span> <span class="n">prjct</span><span class="o">.</span><span class="n">__url__</span>
<span class="n">PAGE_URL</span> <span class="o">=</span> <span class="s1">'</span><span class="si">{slug}</span><span class="s1">/'</span>
<span class="n">PAGE_SAVE_AS</span> <span class="o">=</span> <span class="s1">'</span><span class="si">{slug}</span><span class="s1">/index.html'</span>
<span class="n">TAGS_URL</span> <span class="o">=</span> <span class="s1">'tags/'</span>
<span class="n">TAGS_SAVE_AS</span> <span class="o">=</span> <span class="s1">'tags/index.html'</span>
<span class="n">TAG_URL</span> <span class="o">=</span> <span class="s1">'tags/</span><span class="si">{slug}</span><span class="s1">/'</span>
<span class="n">TAG_SAVE_AS</span> <span class="o">=</span> <span class="s1">'tags/</span><span class="si">{slug}</span><span class="s1">/index.html'</span>
<span class="n">CATEGORIES_URL</span> <span class="o">=</span> <span class="s1">'categories/'</span>
<span class="n">CATEGORIES_SAVE_AS</span> <span class="o">=</span> <span class="s1">'categories/index.html'</span>
<span class="n">CATEGORY_URL</span> <span class="o">=</span> <span class="s1">'categories/</span><span class="si">{slug}</span><span class="s1">/'</span>
<span class="n">CATEGORY_SAVE_AS</span> <span class="o">=</span> <span class="s1">'categories/</span><span class="si">{slug}</span><span class="s1">/index.html'</span>
<span class="n">AUTHORS_URL</span> <span class="o">=</span> <span class="s1">'authors/'</span>
<span class="n">AUTHORS_SAVE_AS</span> <span class="o">=</span> <span class="s1">'authors/index.html'</span>
<span class="n">AUTHOR_URL</span> <span class="o">=</span> <span class="s1">'authors/</span><span class="si">{slug}</span><span class="s1">/'</span>
<span class="n">AUTHOR_SAVE_AS</span> <span class="o">=</span> <span class="s1">'authors/</span><span class="si">{slug}</span><span class="s1">/index.html'</span>
<span class="c1"># {date:%b} gives short month in words (i.e. 'Apr')</span>
<span class="n">ARTICLE_URL</span> <span class="o">=</span> <span class="s1">'posts/{date:%Y}/{date:%-m}/{date:</span><span class="si">%-d</span><span class="s1">}/</span><span class="si">{slug}</span><span class="s1">/'</span>
<span class="n">ARTICLE_SAVE_AS</span> <span class="o">=</span> <span class="s1">'posts/{date:%Y}/{date:%-m}/{date:</span><span class="si">%-d</span><span class="s1">}/</span><span class="si">{slug}</span><span class="s1">/index.html'</span>
<span class="n">DAY_ARCHIVE_URL</span> <span class="o">=</span> <span class="s1">'posts/{date:%Y}/{date:%-m}/{date:</span><span class="si">%-d</span><span class="s1">}/'</span>
<span class="n">DAY_ARCHIVE_SAVE_AS</span> <span class="o">=</span> <span class="s1">'posts/{date:%Y}/{date:%-m}/{date:</span><span class="si">%-d</span><span class="s1">}/index.html'</span>
<span class="n">MONTH_ARCHIVE_URL</span> <span class="o">=</span> <span class="s1">'posts/{date:%Y}/{date:%-m}/'</span>
<span class="n">MONTH_ARCHIVE_SAVE_AS</span> <span class="o">=</span> <span class="s1">'posts/{date:%Y}/{date:%-m}/index.html'</span>
<span class="n">YEAR_ARCHIVE_URL</span> <span class="o">=</span> <span class="s1">'posts/{date:%Y}/'</span>
<span class="n">YEAR_ARCHIVE_SAVE_AS</span> <span class="o">=</span> <span class="s1">'posts/{date:%Y}/index.html'</span>
<span class="n">ARCHIVES_URL</span> <span class="o">=</span> <span class="s1">'posts/'</span>
<span class="n">ARCHIVES_SAVE_AS</span> <span class="o">=</span> <span class="s1">'posts/index.html'</span>
<span class="n">PRJCT_URL</span> <span class="o">=</span> <span class="s1">'prjct/'</span>
<span class="n">PRJCT_SAVE_AS</span> <span class="o">=</span> <span class="s1">'prjct/index.html'</span>
<span class="n">DIRECT_TEMPLATES</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'index'</span><span class="p">,</span> <span class="s1">'categories'</span><span class="p">,</span> <span class="s1">'authors'</span><span class="p">,</span> <span class="s1">'archives'</span><span class="p">,</span>
<span class="s1">'search'</span><span class="p">,</span> <span class="s1">'tags'</span><span class="p">,</span> <span class="s1">'404'</span><span class="p">,</span> <span class="s1">'prjct'</span><span class="p">]</span>
<span class="n">PAGINATED_DIRECT_TEMPLATES</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'index'</span><span class="p">,</span> <span class="s1">'archives'</span><span class="p">]</span>
</code></pre></div>
<h2>Example 2 — Burst Energy</h2>
<!-- Link to Demonstration version -->
<p>This is formerly a production site. I’d generate the site locally and then send
a zip file containing the site to the hosting provider.</p>
<p>I used <a href="http://www.pyinvoke.org/">invoke</a> to automate the site generation. This
automation would also generate graphs (before running Pelican), generate a
javascript file needed elsewhere (after running Pelican), and ensure the
resulting zip file was in a consistent format.</p>
<h3><code>tasks.py</code> — Invoke configuration file</h3>
<div class="highlight"><pre><span></span><code><span class="c1"># Altered fabfile to use with Invoke</span>
<span class="kn">from</span> <span class="nn">invoke</span> <span class="kn">import</span> <span class="n">run</span><span class="p">,</span> <span class="n">task</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">zipfile</span>
<span class="kn">from</span> <span class="nn">build_js</span> <span class="kn">import</span> <span class="n">build_js</span>
<span class="kn">from</span> <span class="nn">floating_rate_vs_rro_graph</span> <span class="kn">import</span> <span class="n">make_graph</span>
<span class="c1"># Local path configuration (can be absolute or relative to fabfile)</span>
<span class="n">env_deploy_path</span> <span class="o">=</span> <span class="s1">'output'</span>
<span class="n">env_zipfile</span> <span class="o">=</span> <span class="s1">'burstenergy.zip'</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">clean</span><span class="p">():</span>
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isdir</span><span class="p">(</span><span class="n">env_deploy_path</span><span class="p">):</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'rm -rf '</span> <span class="o">+</span> <span class="n">env_deploy_path</span><span class="p">)</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'mkdir '</span> <span class="o">+</span> <span class="n">env_deploy_path</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">():</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'pelican -s pelicanconf.py'</span><span class="p">)</span>
<span class="n">build_js</span><span class="p">()</span>
<span class="n">make_graph</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">rebuild</span><span class="p">():</span>
<span class="n">clean</span><span class="p">()</span>
<span class="n">build</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">regenerate</span><span class="p">():</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'start pelican -r -s pelicanconf.py'</span><span class="p">)</span>
<span class="n">build_js</span><span class="p">()</span>
<span class="n">make_graph</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">serve</span><span class="p">():</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'cd '</span> <span class="o">+</span> <span class="n">env_deploy_path</span> <span class="o">+</span> <span class="s1">' && start python -m http.server'</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">reserve</span><span class="p">():</span>
<span class="n">build</span><span class="p">()</span>
<span class="n">build_js</span><span class="p">()</span>
<span class="n">make_graph</span><span class="p">()</span>
<span class="n">serve</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">preview</span><span class="p">():</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'pelican -s publishconf.py'</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">build_zip</span><span class="p">():</span>
<span class="n">zipf</span> <span class="o">=</span> <span class="n">zipfile</span><span class="o">.</span><span class="n">ZipFile</span><span class="p">(</span><span class="n">env_zipfile</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">,</span> <span class="n">zipfile</span><span class="o">.</span><span class="n">ZIP_DEFLATED</span><span class="p">)</span>
<span class="k">for</span> <span class="n">root</span><span class="p">,</span> <span class="n">dirs</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">env_deploy_path</span><span class="p">):</span>
<span class="k">for</span> <span class="n">file</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
<span class="n">absfile</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">file</span><span class="p">)</span>
<span class="n">zfile</span> <span class="o">=</span> <span class="n">absfile</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">env_deploy_path</span><span class="p">)</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">sep</span><span class="p">):]</span> <span class="c1"># relative path</span>
<span class="n">zipf</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">absfile</span><span class="p">,</span> <span class="n">zfile</span><span class="p">)</span>
<span class="n">zipf</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">zip</span><span class="p">():</span>
<span class="n">clean</span><span class="p">()</span>
<span class="n">build</span><span class="p">()</span>
<span class="n">build_zip</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">develop</span><span class="p">():</span>
<span class="n">clean</span><span class="p">()</span>
<span class="n">regenerate</span><span class="p">()</span>
<span class="n">serve</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">build_dieppe</span><span class="p">():</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'pelican -s pelicanconf-dieppe.py'</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">less</span><span class="p">():</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'lessc theme</span><span class="se">\\</span><span class="s1">burst-energy</span><span class="se">\\</span><span class="s1">less</span><span class="se">\\</span><span class="s1">bootstrap.burst-energy.less > '</span> <span class="o">+</span>
<span class="n">env_deploy_path</span> <span class="o">+</span> <span class="s1">'</span><span class="se">\\</span><span class="s1">css</span><span class="se">\\</span><span class="s1">style.css'</span><span class="p">)</span>
<span class="c1"># lessc theme\burst-energy\less\bootstrap.burst-energy.less > output\css\style.css</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">take_screenshot</span><span class="p">():</span>
<span class="kn">from</span> <span class="nn">ghost</span> <span class="kn">import</span> <span class="n">Ghost</span>
<span class="n">url</span> <span class="o">=</span> <span class="s2">"http://localhost:8000"</span>
<span class="n">gh</span> <span class="o">=</span> <span class="n">Ghost</span><span class="p">()</span>
<span class="c1"># We create a new page</span>
<span class="n">page</span><span class="p">,</span> <span class="n">page_name</span> <span class="o">=</span> <span class="n">gh</span><span class="o">.</span><span class="n">create_page</span><span class="p">()</span>
<span class="c1"># We load the main page of ebay</span>
<span class="n">page_resource</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">wait_onload_event</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="c1"># Save the image of the screen</span>
<span class="n">page</span><span class="o">.</span><span class="n">capture_to</span><span class="p">(</span><span class="s2">"burst-energy.png"</span><span class="p">)</span>
</code></pre></div>
<h3>build_js.py</h3>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="c1"># -*- coding: utf-8 -*- #</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">unicode_literals</span>
<span class="sd">'''</span>
<span class="sd">This builds the JavaScript files that are used by the MySecure content.</span>
<span class="sd">Build the site first, then this will put the JS files in the output directory.</span>
<span class="sd">'''</span>
<span class="k">def</span> <span class="nf">build_js</span><span class="p">():</span>
<span class="kn">from</span> <span class="nn">pelicanconf</span> <span class="kn">import</span> <span class="n">OUTPUT_PATH</span>
<span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">codecs</span>
<span class="s1">'configuration'</span>
<span class="s1">'form file'</span>
<span class="n">FORM_FILE</span> <span class="o">=</span> <span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">realpath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">sep</span> <span class="o">+</span>
<span class="n">OUTPUT_PATH</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">sep</span><span class="p">)</span>
<span class="n">FORM_FILE</span> <span class="o">+=</span> <span class="s1">'mysecure'</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">sep</span> <span class="o">+</span> <span class="s1">'signup.html'</span>
<span class="s1">'output directory'</span>
<span class="n">JS_PATH</span> <span class="o">=</span> <span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">realpath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">sep</span> <span class="o">+</span>
<span class="n">OUTPUT_PATH</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">sep</span> <span class="o">+</span> <span class="s1">'js'</span><span class="p">)</span>
<span class="s1">'Placeholder text'</span>
<span class="n">placeholder</span> <span class="o">=</span> <span class="s1">'PLACEHOLDER'</span>
<span class="s2">"test existence of 'form file'"</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">FORM_FILE</span><span class="p">,</span> <span class="s2">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">FORM_FILE_HANDLE</span><span class="p">:</span>
<span class="n">soup</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">FORM_FILE_HANDLE</span><span class="p">,</span> <span class="s2">"lxml"</span><span class="p">)</span>
<span class="s2">"create `title.js`"</span>
<span class="s2">"document title"</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">JS_PATH</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">sep</span> <span class="o">+</span> <span class="s1">'title.js'</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">)</span> <span class="k">as</span> <span class="n">TITLE_JS</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"var titleblock = ''"</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">TITLE_JS</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"titleblock += '</span><span class="si">{}</span><span class="s2">'"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">soup</span><span class="o">.</span><span class="n">title</span><span class="p">),</span> <span class="n">file</span><span class="o">=</span><span class="n">TITLE_JS</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">""</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">TITLE_JS</span><span class="p">)</span>
<span class="c1"># ToDo: Select the title dynamically,</span>
<span class="c1"># based on the file it's called from"</span>
<span class="s2">"meta -- viewport"</span>
<span class="n">viewport</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s1">'meta'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'name'</span><span class="p">:</span> <span class="s1">'viewport'</span><span class="p">})</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"titleblock += '</span><span class="si">{}</span><span class="s2">'"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">viewport</span><span class="p">),</span> <span class="n">file</span><span class="o">=</span><span class="n">TITLE_JS</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">""</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">TITLE_JS</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"document.write(titleblock);"</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">TITLE_JS</span><span class="p">)</span>
<span class="s2">"create `header.js` and `footerinfo.js`"</span>
<span class="n">on_header</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">my_re2</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s1">'<!--?body-->'</span><span class="p">)</span>
<span class="n">my_re4</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="n">placeholder</span><span class="p">)</span>
<span class="n">on_header</span> <span class="o">=</span> <span class="kc">True</span>
<span class="k">with</span> <span class="n">codecs</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">JS_PATH</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">sep</span> <span class="o">+</span> <span class="s1">'header.js'</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">,</span> <span class="s2">"utf-8"</span><span class="p">)</span> \
<span class="k">as</span> <span class="n">HEADER_JS</span><span class="p">:</span>
<span class="k">with</span> <span class="n">codecs</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">JS_PATH</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">sep</span> <span class="o">+</span> <span class="s1">'footerinfo.js'</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">,</span> <span class="s2">"utf-8"</span><span class="p">)</span> \
<span class="k">as</span> <span class="n">FOOTER_JS</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"var headerblock = ''"</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">HEADER_JS</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"var footerblock = ''"</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">FOOTER_JS</span><span class="p">)</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">str</span><span class="p">(</span><span class="n">soup</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s1">'body'</span><span class="p">))</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'</span><span class="se">\n</span><span class="s1">'</span><span class="p">):</span>
<span class="n">line2</span> <span class="o">=</span> <span class="n">my_re2</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="s1">''</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
<span class="k">if</span> <span class="n">my_re4</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">line2</span><span class="p">):</span>
<span class="n">on_header</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">line2</span> <span class="o">=</span> <span class="s1">''</span>
<span class="k">if</span> <span class="n">on_header</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"headerblock += '</span><span class="si">{}</span><span class="s2">'"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">line2</span><span class="p">),</span> <span class="n">file</span><span class="o">=</span><span class="n">HEADER_JS</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"footerblock += '</span><span class="si">{}</span><span class="s2">'"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">line2</span><span class="p">),</span> <span class="n">file</span><span class="o">=</span><span class="n">FOOTER_JS</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"document.write(headerblock)"</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">HEADER_JS</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"document.write(footerblock)"</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">FOOTER_JS</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">build_js</span><span class="p">()</span>
</code></pre></div>
<h3><code>floating_rate_vs_rro_graph.py</code> — to generate graphs</h3>
<div class="highlight"><pre><span></span><code><span class="c1"># This generated the graph show the comparision between the RRO and our</span>
<span class="c1"># floating rate.</span>
<span class="c1"># This graph is displayed on https://www.burstenergy.ca/rates/floating/</span>
<span class="n">Months</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'2014-01'</span><span class="p">,</span> <span class="s1">'2014-02'</span><span class="p">,</span> <span class="s1">'2014-03'</span><span class="p">,</span> <span class="s1">'2014-04'</span><span class="p">,</span> <span class="s1">'2014-05'</span><span class="p">,</span> <span class="s1">'2014-06'</span><span class="p">,</span>
<span class="s1">'2014-07'</span><span class="p">,</span> <span class="s1">'2014-08'</span><span class="p">,</span> <span class="s1">'2014-09'</span><span class="p">,</span> <span class="s1">'2014-10'</span><span class="p">,</span> <span class="s1">'2014-11'</span><span class="p">,</span> <span class="s1">'2014-12'</span><span class="p">,</span>
<span class="s1">'2015-01'</span><span class="p">,</span> <span class="s1">'2015-02'</span><span class="p">,</span> <span class="s1">'2015-03'</span><span class="p">,</span> <span class="s1">'2015-04'</span><span class="p">,</span> <span class="s1">'2015-05'</span><span class="p">,</span> <span class="s1">'2015-06'</span><span class="p">,</span>
<span class="s1">'2015-07'</span><span class="p">,</span> <span class="s1">'2015-08'</span><span class="p">,</span> <span class="s1">'2015-09'</span><span class="p">,</span> <span class="s1">'2015-10'</span><span class="p">,</span> <span class="s1">'2015-11'</span><span class="p">,</span> <span class="s1">'2015-12'</span><span class="p">,</span>
<span class="s1">'2016-01'</span><span class="p">,</span> <span class="s1">'2016-02'</span><span class="p">,</span> <span class="s1">'2016-03'</span><span class="p">,</span> <span class="s1">'2016-04'</span><span class="p">]</span>
<span class="c1"># both floating and RRO for Edmonton</span>
<span class="n">RRO</span> <span class="o">=</span> <span class="p">[</span> <span class="mf">8.689</span><span class="p">,</span> <span class="mf">7.471</span><span class="p">,</span> <span class="mf">6.991</span><span class="p">,</span> <span class="mf">6.986</span><span class="p">,</span> <span class="mf">8.922</span><span class="p">,</span> <span class="mf">5.995</span><span class="p">,</span>
<span class="mf">7.198</span><span class="p">,</span> <span class="mf">8.021</span><span class="p">,</span> <span class="mf">7.954</span><span class="p">,</span> <span class="mf">8.737</span><span class="p">,</span> <span class="mf">7.127</span><span class="p">,</span> <span class="mf">7.545</span><span class="p">,</span>
<span class="mf">7.302</span><span class="p">,</span> <span class="mf">6.583</span><span class="p">,</span> <span class="mf">5.431</span><span class="p">,</span> <span class="mf">5.832</span><span class="p">,</span> <span class="mf">4.337</span><span class="p">,</span> <span class="mf">4.089</span><span class="p">,</span>
<span class="mf">6.140</span><span class="p">,</span> <span class="mf">5.813</span><span class="p">,</span> <span class="mf">5.387</span><span class="p">,</span> <span class="mf">5.498</span><span class="p">,</span> <span class="mf">5.212</span><span class="p">,</span> <span class="mf">5.489</span><span class="p">,</span>
<span class="mf">5.304</span><span class="p">,</span> <span class="mf">4.753</span><span class="p">,</span> <span class="mf">4.521</span><span class="p">,</span> <span class="mf">3.65</span><span class="p">]</span>
<span class="n">Floating</span> <span class="o">=</span> <span class="p">[</span> <span class="mf">6.149</span><span class="p">,</span> <span class="mf">11.730</span><span class="p">,</span> <span class="mf">5.903</span><span class="p">,</span> <span class="mf">4.438</span><span class="p">,</span> <span class="mf">7.328</span><span class="p">,</span> <span class="mf">5.909</span><span class="p">,</span>
<span class="mf">15.801</span><span class="p">,</span> <span class="mf">6.239</span><span class="p">,</span> <span class="mf">3.710</span><span class="p">,</span> <span class="mf">4.011</span><span class="p">,</span> <span class="mf">5.378</span><span class="p">,</span> <span class="mf">4.049</span><span class="p">,</span>
<span class="mf">5.001</span><span class="p">,</span> <span class="mf">4.831</span><span class="p">,</span> <span class="mf">3.294</span><span class="p">,</span> <span class="mf">3.282</span><span class="p">,</span> <span class="mf">7.461</span><span class="p">,</span> <span class="mf">13.218</span><span class="p">,</span>
<span class="mf">3.692</span><span class="p">,</span> <span class="mf">4.952</span><span class="p">,</span> <span class="mf">3.327</span><span class="p">,</span> <span class="mf">3.412</span><span class="p">,</span> <span class="mf">3.403</span><span class="p">,</span> <span class="mf">3.333</span><span class="p">,</span>
<span class="mf">3.441</span><span class="p">,</span> <span class="mf">2.907</span><span class="p">,</span> <span class="mf">2.617</span><span class="p">,</span> <span class="mf">2.503</span><span class="p">]</span>
<span class="c1"># NGX Spot Prices</span>
<span class="n">NGX</span> <span class="o">=</span> <span class="p">[</span><span class="mf">3.8412</span><span class="p">,</span> <span class="mf">5.4560</span><span class="p">,</span> <span class="mf">5.1446</span><span class="p">,</span> <span class="mf">4.4361</span><span class="p">,</span> <span class="mf">4.4374</span><span class="p">,</span> <span class="mf">4.3885</span><span class="p">,</span>
<span class="mf">4.2005</span><span class="p">,</span> <span class="mf">3.8446</span><span class="p">,</span> <span class="mf">3.8608</span><span class="p">,</span> <span class="mf">3.7947</span><span class="p">,</span> <span class="mf">3.7084</span><span class="p">,</span> <span class="mf">3.6218</span><span class="p">,</span>
<span class="mf">3.0539</span><span class="p">,</span> <span class="mf">2.7301</span><span class="p">,</span> <span class="mf">2.7503</span><span class="p">,</span> <span class="mf">2.5161</span><span class="p">,</span> <span class="mf">2.5477</span><span class="p">,</span> <span class="mf">2.5729</span><span class="p">,</span>
<span class="mf">2.5976</span><span class="p">,</span> <span class="mf">2.7783</span><span class="p">,</span> <span class="mf">2.7699</span><span class="p">,</span> <span class="mf">2.6217</span><span class="p">,</span> <span class="mf">2.4256</span><span class="p">,</span> <span class="mf">2.3347</span><span class="p">,</span>
<span class="mf">2.3324</span><span class="p">,</span> <span class="mf">2.0379</span><span class="p">,</span> <span class="mf">1.5752</span><span class="p">,</span> <span class="mf">1.3116</span><span class="p">]</span>
<span class="n">NG_margin</span> <span class="o">=</span> <span class="mf">0.90</span>
<span class="k">def</span> <span class="nf">make_graph</span><span class="p">(</span><span class="n">Months_Plotted</span><span class="o">=</span><span class="mi">12</span><span class="p">):</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="kn">from</span> <span class="nn">statistics</span> <span class="kn">import</span> <span class="n">mean</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span>
<span class="kn">import</span> <span class="nn">matplotlib.dates</span> <span class="k">as</span> <span class="nn">mdates</span>
<span class="kn">from</span> <span class="nn">pylab</span> <span class="kn">import</span> <span class="n">savefig</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
<span class="c1"># for nicer colours, importing is enough</span>
<span class="kn">import</span> <span class="nn">seaborn</span> <span class="k">as</span> <span class="nn">sns</span>
<span class="w"> </span><span class="sd">"""Convert *Months* to numbers"""</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">Months</span><span class="p">)):</span>
<span class="n">year</span><span class="p">,</span> <span class="n">month</span> <span class="o">=</span> <span class="nb">map</span><span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="n">Months</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'-'</span><span class="p">))</span>
<span class="n">Months</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">mdates</span><span class="o">.</span><span class="n">date2num</span><span class="p">(</span><span class="n">datetime</span><span class="p">(</span><span class="n">year</span><span class="p">,</span> <span class="n">month</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span>
<span class="w"> </span><span class="sd">"""drop last month on RRO if we have more data than for the floating"""</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">RRO</span><span class="p">)</span> <span class="o">></span> <span class="nb">len</span><span class="p">(</span><span class="n">Floating</span><span class="p">):</span>
<span class="n">my_RRO</span> <span class="o">=</span> <span class="n">RRO</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">my_Months</span> <span class="o">=</span> <span class="n">Months</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">my_RRO</span> <span class="o">=</span> <span class="n">RRO</span>
<span class="n">my_Months</span> <span class="o">=</span> <span class="n">Months</span>
<span class="n">my_Floating</span> <span class="o">=</span> <span class="n">Floating</span>
<span class="n">my_NG</span> <span class="o">=</span> <span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="n">NG_margin</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">NGX</span><span class="p">]</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">my_Months</span><span class="p">)</span> <span class="o">></span> <span class="n">Months_Plotted</span><span class="p">:</span>
<span class="n">my_Floating</span> <span class="o">=</span> <span class="n">my_Floating</span><span class="p">[</span><span class="o">-</span><span class="n">Months_Plotted</span><span class="p">:]</span>
<span class="n">my_RRO</span> <span class="o">=</span> <span class="n">my_RRO</span><span class="p">[</span><span class="o">-</span><span class="n">Months_Plotted</span><span class="p">:]</span>
<span class="n">my_Months</span> <span class="o">=</span> <span class="n">my_Months</span><span class="p">[</span><span class="o">-</span><span class="n">Months_Plotted</span><span class="p">:]</span>
<span class="n">my_NG</span> <span class="o">=</span> <span class="n">my_NG</span><span class="p">[</span><span class="o">-</span><span class="n">Months_Plotted</span><span class="p">:]</span>
<span class="c1"># averages</span>
<span class="n">average_RRO</span> <span class="o">=</span> <span class="p">[</span><span class="n">mean</span><span class="p">(</span><span class="n">my_RRO</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">my_Months</span><span class="p">]</span>
<span class="n">average_Floating</span> <span class="o">=</span> <span class="p">[</span><span class="n">mean</span><span class="p">(</span><span class="n">my_Floating</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">my_Months</span><span class="p">]</span>
<span class="n">average_NG</span> <span class="o">=</span> <span class="p">[</span><span class="n">mean</span><span class="p">(</span><span class="n">my_NG</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">my_Months</span><span class="p">]</span>
<span class="w"> </span><span class="sd">"""Now let's plot!"""</span>
<span class="c1"># make the final image 800px wide</span>
<span class="n">fig</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">figure</span><span class="p">(</span><span class="n">dpi</span><span class="o">=</span><span class="mi">72</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">800</span><span class="o">/</span><span class="mi">72</span><span class="o">/</span><span class="mf">1.19</span><span class="p">,</span> <span class="p">(</span><span class="mi">800</span><span class="o">*</span><span class="mi">1</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span><span class="o">/</span><span class="mi">72</span><span class="o">/</span><span class="mf">1.19</span><span class="p">))</span>
<span class="n">graph</span> <span class="o">=</span> <span class="n">fig</span><span class="o">.</span><span class="n">add_subplot</span><span class="p">(</span><span class="mi">111</span><span class="p">)</span>
<span class="c1"># Plot the two lines</span>
<span class="n">line_RRO</span> <span class="o">=</span> <span class="n">graph</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">my_Months</span><span class="p">,</span> <span class="n">my_RRO</span><span class="p">,</span> <span class="n">sns</span><span class="o">.</span><span class="n">xkcd_rgb</span><span class="p">[</span><span class="s2">"pale red"</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s2">"RRO"</span><span class="p">)</span>
<span class="n">line_Burst</span> <span class="o">=</span> <span class="n">graph</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">my_Months</span><span class="p">,</span> <span class="n">my_Floating</span><span class="p">,</span> <span class="n">sns</span><span class="o">.</span><span class="n">xkcd_rgb</span><span class="p">[</span><span class="s2">"medium green"</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s2">"Burst Energy"</span><span class="p">)</span>
<span class="c1"># x axis -- list the months on the 1st</span>
<span class="n">graph</span><span class="o">.</span><span class="n">set_xticks</span><span class="p">(</span><span class="n">my_Months</span><span class="p">)</span>
<span class="n">graph</span><span class="o">.</span><span class="n">set_xticklabels</span><span class="p">([</span><span class="n">mdates</span><span class="o">.</span><span class="n">num2date</span><span class="p">(</span><span class="n">date</span><span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%b '%y"</span><span class="p">)</span> <span class="k">for</span> <span class="n">date</span> <span class="ow">in</span> <span class="n">my_Months</span><span class="p">])</span>
<span class="c1"># y axis -- min is 0, label</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylim</span><span class="p">(</span><span class="n">ymin</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s2">"</span><span class="se">\u00A2</span><span class="s2"> / kWh, for Edmonton"</span><span class="p">)</span>
<span class="c1"># add legend</span>
<span class="n">graph</span><span class="o">.</span><span class="n">legend</span><span class="p">()</span>
<span class="c1"># current file location (this script file)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">realpath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span>
<span class="c1"># drop script file name</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">parents</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="c1"># location of where we want to save the graph</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">p</span> <span class="o">/</span> <span class="s2">"floating-rate-vs-rro.png"</span>
<span class="n">savefig</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">p</span><span class="p">),</span> <span class="n">bbox_inches</span><span class="o">=</span><span class="s1">'tight'</span><span class="p">)</span>
<span class="c1"># plt.show()</span>
<span class="w"> </span><span class="sd">"""Now let's plot (Natural Gas!"""</span>
<span class="c1"># make the final image 800px wide</span>
<span class="n">fig</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">figure</span><span class="p">(</span><span class="n">dpi</span><span class="o">=</span><span class="mi">72</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">800</span><span class="o">/</span><span class="mi">72</span><span class="o">/</span><span class="mf">1.19</span><span class="p">,</span> <span class="p">(</span><span class="mi">800</span><span class="o">*</span><span class="mi">1</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span><span class="o">/</span><span class="mi">72</span><span class="o">/</span><span class="mf">1.19</span><span class="p">))</span>
<span class="n">graph</span> <span class="o">=</span> <span class="n">fig</span><span class="o">.</span><span class="n">add_subplot</span><span class="p">(</span><span class="mi">111</span><span class="p">)</span>
<span class="c1"># Plot the two lines</span>
<span class="n">line_Burst</span> <span class="o">=</span> <span class="n">graph</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">my_Months</span><span class="p">,</span> <span class="n">my_NG</span><span class="p">,</span> <span class="n">sns</span><span class="o">.</span><span class="n">xkcd_rgb</span><span class="p">[</span><span class="s2">"medium green"</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s2">"Burst Energy"</span><span class="p">)</span>
<span class="c1"># x axis -- list the months on the 1st</span>
<span class="n">graph</span><span class="o">.</span><span class="n">set_xticks</span><span class="p">(</span><span class="n">my_Months</span><span class="p">)</span>
<span class="n">graph</span><span class="o">.</span><span class="n">set_xticklabels</span><span class="p">([</span><span class="n">mdates</span><span class="o">.</span><span class="n">num2date</span><span class="p">(</span><span class="n">date</span><span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%b '%y"</span><span class="p">)</span> <span class="k">for</span> <span class="n">date</span> <span class="ow">in</span> <span class="n">my_Months</span><span class="p">])</span>
<span class="c1"># y axis -- min is 0, label</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylim</span><span class="p">(</span><span class="n">ymin</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">ymax</span><span class="o">=</span><span class="mi">6</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s2">"$ / GJ, for Alberta"</span><span class="p">)</span>
<span class="c1"># add legend</span>
<span class="n">graph</span><span class="o">.</span><span class="n">legend</span><span class="p">()</span>
<span class="c1"># current file location (this script file)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">realpath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span>
<span class="c1"># drop script file name</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">parents</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="c1"># location of where we want to save the graph</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">p</span> <span class="o">/</span> <span class="s2">"natural-gas.png"</span>
<span class="n">savefig</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">p</span><span class="p">),</span> <span class="n">bbox_inches</span><span class="o">=</span><span class="s1">'tight'</span><span class="p">)</span>
<span class="c1"># plt.show()</span>
<span class="k">def</span> <span class="nf">averages</span><span class="p">(</span><span class="n">Months_Plotted</span><span class="o">=</span><span class="mi">12</span><span class="p">):</span>
<span class="kn">import</span> <span class="nn">statistics</span>
<span class="w"> </span><span class="sd">"""drop last month on RRO if we have more data than for the floating"""</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">RRO</span><span class="p">)</span> <span class="o">></span> <span class="nb">len</span><span class="p">(</span><span class="n">Floating</span><span class="p">):</span>
<span class="n">my_RRO</span> <span class="o">=</span> <span class="n">RRO</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">my_Months</span> <span class="o">=</span> <span class="n">Months</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">my_RRO</span> <span class="o">=</span> <span class="n">RRO</span>
<span class="n">my_Months</span> <span class="o">=</span> <span class="n">Months</span>
<span class="n">my_Floating</span> <span class="o">=</span> <span class="n">Floating</span>
<span class="n">my_NG</span> <span class="o">=</span> <span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="n">NG_margin</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">NGX</span><span class="p">]</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">my_Months</span><span class="p">)</span> <span class="o">></span> <span class="n">Months_Plotted</span><span class="p">:</span>
<span class="n">my_Floating</span> <span class="o">=</span> <span class="n">my_Floating</span><span class="p">[</span><span class="o">-</span><span class="n">Months_Plotted</span><span class="p">:]</span>
<span class="n">my_RRO</span> <span class="o">=</span> <span class="n">my_RRO</span><span class="p">[</span><span class="o">-</span><span class="n">Months_Plotted</span><span class="p">:]</span>
<span class="n">my_Months</span> <span class="o">=</span> <span class="n">my_Months</span><span class="p">[</span><span class="o">-</span><span class="n">Months_Plotted</span><span class="p">:]</span>
<span class="n">my_NG</span> <span class="o">=</span> <span class="n">my_NG</span><span class="p">[</span><span class="o">-</span><span class="n">Months_Plotted</span><span class="p">:]</span>
<span class="n">ave_RRO</span> <span class="o">=</span> <span class="n">statistics</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">my_RRO</span><span class="p">)</span>
<span class="n">ave_Floating</span> <span class="o">=</span> <span class="n">statistics</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">my_Floating</span><span class="p">)</span>
<span class="n">ave_NG</span> <span class="o">=</span> <span class="n">statistics</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">my_NG</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"For the last </span><span class="si">{}</span><span class="s2"> months, the average is:"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">Months_Plotted</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">For the RRO, </span><span class="si">{:.3f}</span><span class="s2"> ¢/kWh"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="n">ave_RRO</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">For the Floating rate, </span><span class="si">{:.3f}</span><span class="s2"> ¢/kWh"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="n">ave_Floating</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">For natural gas, $</span><span class="si">{:.2f}</span><span class="s2"> /GJ"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="n">ave_NG</span><span class="p">))</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">make_graph</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
<span class="n">averages</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span>
<span class="n">averages</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
</code></pre></div>
<h3><code>pelicanconf.py</code> — Pelican Configuration</h3>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># -*- coding: utf-8 -*- #</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">unicode_literals</span>
<span class="n">AUTHOR</span> <span class="o">=</span> <span class="s1">'Burst Energy'</span>
<span class="n">SITENAME</span> <span class="o">=</span> <span class="s1">'Burst Energy'</span>
<span class="n">SITEURL</span> <span class="o">=</span> <span class="s1">'https://www.burstenergy.ca'</span> <span class="c1"># used by the sitemap generator</span>
<span class="n">WEB_URL</span> <span class="o">=</span> <span class="s1">''</span> <span class="c1"># "SITEURL" as used by the templates (so for the published web pages)</span>
<span class="n">CANONICAL_SITEURL</span> <span class="o">=</span> <span class="n">SITEURL</span>
<span class="n">PATH</span> <span class="o">=</span> <span class="s1">'content'</span>
<span class="n">OUTPUT_PATH</span> <span class="o">=</span> <span class="s1">'output'</span>
<span class="n">TIMEZONE</span> <span class="o">=</span> <span class="s1">'America/Edmonton'</span>
<span class="n">DEFAULT_LANG</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'en'</span>
<span class="n">DEFAULT_DATE</span> <span class="o">=</span> <span class="s1">'fs'</span>
<span class="c1"># Feed generation is usually not desired when developing</span>
<span class="n">FEED_ALL_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">CATEGORY_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">TRANSLATION_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">AUTHOR_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">AUTHOR_FEED_RSS</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># Theme</span>
<span class="n">THEME</span> <span class="o">=</span> <span class="s1">'theme/burst-energy'</span>
<span class="n">CUSTOM_CSS</span> <span class="o">=</span> <span class="s1">'css/burst-energy.css'</span>
<span class="n">BANNER_SUBTITLE</span> <span class="o">=</span> <span class="s1">'Simple Electricity'</span>
<span class="n">HIDE_SIDEBAR</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">BANNER</span> <span class="o">=</span> <span class="n">BANNER_ALL_PAGES</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">TYPOGRIFY</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">COPY_DATE</span> <span class="o">=</span> <span class="s1">'2014-15'</span>
<span class="n">BOOTSTRAP_NAVBAR_INVERSE</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">SITELOGO</span> <span class="o">=</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'images/burst-energy-logo-sol-white-230.png'</span>
<span class="c1"># SITELOGO_WIDTH = '110px'</span>
<span class="n">SITELOGO_HEIGHT</span> <span class="o">=</span> <span class="s1">'25px'</span>
<span class="n">FOOTER_LOGO</span> <span class="o">=</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'images/burst-energy-logo-sol-230.png'</span>
<span class="n">FOOTER_LOGO_WIDTH</span> <span class="o">=</span> <span class="s1">'220px'</span>
<span class="n">HIDE_SITENAME</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">BOOTSTRAP_THEME</span> <span class="o">=</span> <span class="s1">'burst-energy'</span>
<span class="n">THEME_COLOR</span> <span class="o">=</span> <span class="s2">"#2C3E50"</span>
<span class="n">FAVICON</span> <span class="o">=</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'images/favicon.png'</span>
<span class="n">FAVICON_IE</span> <span class="o">=</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'favicon.ico'</span>
<span class="n">TOUCHICON</span> <span class="o">=</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'images/apple-touch-icon.png'</span>
<span class="c1"># PYGMENTS_STYLE =</span>
<span class="n">GOOGLE_ANALYTICS_UNIVERSAL</span> <span class="o">=</span> <span class="s1">'UA-49197109-1'</span>
<span class="n">GOOGLE_ANALYTICS_UNIVERSAL_PROPERTY</span> <span class="o">=</span> <span class="s1">'burstenergy.ca'</span>
<span class="n">DISPLAY_PAGES_ON_MENU</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">DISPLAY_CATEGORIES_ON_MENU</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">DISPLAY_ARCHIVES_ON_MENU</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">MENU_ITEMS_ONE</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'News'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/news/'</span><span class="p">,</span> <span class="kc">None</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Our Rates'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/rates/'</span><span class="p">,</span> <span class="kc">None</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Electricity 101'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/electricity-101/'</span><span class="p">,</span> <span class="kc">None</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">MENU_ITEMS_TWO</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'Sign Up'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/mysecure/signup.html'</span><span class="p">,</span> <span class="s1">'glyphicon glyphicon-ok-circle'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'My Account'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/mysecure/portal.html'</span><span class="p">,</span> <span class="s1">'fa fa-sign-in'</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">INDEX_SAVE_AS</span> <span class="o">=</span> <span class="s2">"news/index.html"</span>
<span class="n">DISPLAY_ARTICLE_INFO_ON_INDEX</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># Link Structure</span>
<span class="n">PAGE_URL</span> <span class="o">=</span> <span class="s1">'</span><span class="si">{slug}</span><span class="s1">/'</span>
<span class="n">PAGE_SAVE_AS</span> <span class="o">=</span> <span class="s1">'</span><span class="si">{slug}</span><span class="s1">/index.html'</span>
<span class="n">ARTICLE_URL</span> <span class="o">=</span> <span class="s1">'news/{date:%Y}/</span><span class="si">{slug}</span><span class="s1">/'</span>
<span class="n">ARTICLE_SAVE_AS</span> <span class="o">=</span> <span class="s1">'news/{date:%Y}/</span><span class="si">{slug}</span><span class="s1">/index.html'</span>
<span class="n">YEAR_ARCHIVE_SAVE_AS</span> <span class="o">=</span> <span class="s1">'news/{date:%Y}/index.html'</span>
<span class="n">CATEGORIES_SAVE_AS</span> <span class="o">=</span> <span class="s1">'category/index.html'</span>
<span class="n">AUTHORS_SAVE_AS</span> <span class="o">=</span> <span class="s1">'author/index.html'</span>
<span class="n">STATIC_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'images'</span><span class="p">,</span>
<span class="s1">'js'</span><span class="p">,</span>
<span class="s1">'favicon.ico'</span><span class="p">,</span>
<span class="s1">'browserconfig.xml'</span><span class="p">,</span>
<span class="s1">'robots.txt'</span><span class="p">,</span> <span class="p">]</span>
<span class="c1"># Contact Info</span>
<span class="n">CONTACT_WEB_EMAIL</span> <span class="o">=</span> <span class="s2">"webteam@burstenergy.ca"</span>
<span class="n">CONTACT_PHONE</span> <span class="o">=</span> <span class="s2">"(780) 665-9918"</span>
<span class="n">C_RETAIL_FAX</span> <span class="o">=</span> <span class="s2">"(403) 265-7290"</span>
<span class="n">C_CORPORATE_EMAIL</span> <span class="o">=</span> <span class="s2">"william@burstenergy.ca"</span>
<span class="n">FOOTER_ADDRESS</span> <span class="o">=</span> <span class="s2">"Suite 200, 1316 9<sup>th</sup> Avenue SE<br/>Calgary, Alberta T2G 0T3"</span>
<span class="n">SOCIAL</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'facebook'</span><span class="p">,</span> <span class="s1">'https://www.facebook.com/BurstEnergyCA'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'twitter'</span><span class="p">,</span> <span class="s1">'http://twitter.com/BurstEnergy'</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">FOOTER_LINKS</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'About Us'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/about/'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'FAQ'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/faq/'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Glossary'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/gloss/'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Quick Links'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/quicklinks/'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Privacy'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/privacy/'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Legal'</span><span class="p">,</span> <span class="n">WEB_URL</span> <span class="o">+</span> <span class="s1">'/legal/'</span><span class="p">),</span>
<span class="p">)</span>
<span class="c1"># Plugins</span>
<span class="n">PLUGIN_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'S:</span><span class="se">\\</span><span class="s1">Documents</span><span class="se">\\</span><span class="s1">GitHub</span><span class="se">\\</span><span class="s1">pelican-plugins'</span><span class="p">,</span> <span class="s1">'C:</span><span class="se">\\</span><span class="s1">Users</span><span class="se">\\</span><span class="s1">User</span><span class="se">\\</span><span class="s1">Documents</span><span class="se">\\</span><span class="s1">GitHub</span><span class="se">\\</span><span class="s1">pelican-plugins'</span><span class="p">]</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'pelican_alias'</span><span class="p">,</span>
<span class="s1">'assets'</span><span class="p">,</span>
<span class="s1">'extended_sitemap'</span><span class="p">,</span>
<span class="s1">'neighbors'</span><span class="p">]</span>
<span class="n">USE_ASSETS</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">ASSETS_CSS</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">ASSETS_JS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">SITEMAP</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"format"</span><span class="p">:</span> <span class="s2">"xml"</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">EXTENDED_SITEMAP_PLUGIN</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'priorities'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'index'</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">,</span>
<span class="s1">'articles'</span><span class="p">:</span> <span class="mf">0.5</span><span class="p">,</span>
<span class="s1">'pages'</span><span class="p">:</span> <span class="mf">0.8</span><span class="p">,</span>
<span class="s1">'others'</span><span class="p">:</span> <span class="mf">0.4</span>
<span class="p">},</span>
<span class="s1">'changefrequencies'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'index'</span><span class="p">:</span> <span class="s1">'daily'</span><span class="p">,</span>
<span class="s1">'articles'</span><span class="p">:</span> <span class="s1">'weekly'</span><span class="p">,</span>
<span class="s1">'pages'</span><span class="p">:</span> <span class="s1">'weekly'</span><span class="p">,</span>
<span class="s1">'others'</span><span class="p">:</span> <span class="s1">'monthly'</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">DEFAULT_PAGINATION</span> <span class="o">=</span> <span class="mi">10</span>
<span class="c1"># Uncomment following line if you want document-relative URLs when developing</span>
<span class="c1"># RELATIVE_URLS = True</span>
<span class="c1"># Nav bar links are set expliciately above</span>
<span class="c1"># pages set to hidden don't show up in sitemap</span>
<span class="c1"># sitemap also uses the 'date' as last modified</span>
</code></pre></div>
<h2>Minchin.ca</h2>
<p>This is my personal website at <a href="http://minchin.ca">http://minchin.ca</a>. I’ve edited Bootstrap
extensively (colours, fonts, vertical menu). The theme has since been posted to
PyPI as <a href="https://pypi.python.org/pypi/seafoam/">seafoam</a> and is installable via
<code>pip</code>. The <a href="https://github.com/MinchinWeb/minchinweb.github.io/tree/pelican">code for the
site</a>, and the
site ifself, are both hosted on GitHub Pages.</p>
<h3><code>pelicanconf.py</code> — Pelican Configuration</h3>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># -*- coding: utf-8 -*- #</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">unicode_literals</span>
<span class="n">AUTHOR</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'Wm. Minchin'</span>
<span class="n">SITENAME</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'Minchin.ca'</span>
<span class="n">SITEURL</span> <span class="o">=</span> <span class="s1">'http://minchin.ca'</span>
<span class="n">SITE_ROOT_URL</span> <span class="o">=</span> <span class="n">SITEURL</span>
<span class="n">TIMEZONE</span> <span class="o">=</span> <span class="s1">'America/Edmonton'</span>
<span class="n">DEFAULT_LANG</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'en'</span>
<span class="c1"># Uncomment following line if you want document-relative URLs when developing</span>
<span class="n">RELATIVE_URLS</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># Feed generation is usually not desired when developing</span>
<span class="n">FEED_ALL_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">CATEGORY_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">TRANSLATION_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">DEFAULT_PAGINATION</span> <span class="o">=</span> <span class="kc">False</span>
<span class="c1"># static paths will be copied under the same name</span>
<span class="n">STATIC_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'images'</span><span class="p">,</span>
<span class="s1">'..\extras'</span><span class="p">,</span>
<span class="s1">'css'</span><span class="p">,</span>
<span class="s1">'projects\design'</span><span class="p">,</span>
<span class="s1">'..\.gitattributes'</span><span class="p">,</span>
<span class="s1">'..\.gitignore'</span><span class="p">,</span>
<span class="s1">'..\README.txt'</span><span class="p">,</span> <span class="p">]</span>
<span class="c1"># A list of files to copy from the source to the destination</span>
<span class="n">EXTRA_PATH_METADATA</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'../.gitattributes'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'.gitattributes'</span><span class="p">},</span>
<span class="s1">'../.gitignore'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'.gitignore'</span><span class="p">},</span>
<span class="s1">'../README.txt'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'README.txt'</span><span class="p">},</span>
<span class="c1"># CNAME file tells GitHub Pages to display this site at minchin.ca</span>
<span class="s1">'../extras/CNAME'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'CNAME'</span><span class="p">},</span>
<span class="s1">'../extras/minchin.ico'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'favicon.ico'</span><span class="p">},</span>
<span class="s1">'../extras/MTS_1v1.xlsm'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'MTS_1v1.xlsm'</span><span class="p">},</span>
<span class="s1">'../extras/TRB_Minchin.ca.XSL'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'TRB_Minchin.ca.XSL'</span><span class="p">},</span>
<span class="s1">'../extras/googlecbc66a9bfde8606b.html'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'googlecbc66a9bfde8606b.html'</span><span class="p">},</span>
<span class="s1">'../extras/.nojekyll'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'.nojekyll'</span><span class="p">},</span>
<span class="c1"># prism is used by the blog for code highlighting</span>
<span class="s1">'../extras/prism.js'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'js/prism.js'</span><span class="p">},</span>
<span class="s1">'../extras/prism.css'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'css/prism.css'</span><span class="p">},</span>
<span class="p">}</span>
<span class="c1"># Custom settings</span>
<span class="n">MARKUP</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'rst'</span><span class="p">,</span>
<span class="s1">'md'</span><span class="p">,</span>
<span class="s1">'markdown'</span><span class="p">,</span>
<span class="s1">'mkd'</span><span class="p">,</span>
<span class="s1">'mdown'</span><span class="p">,))</span> <span class="c1"># don't include htm and html files</span>
<span class="n">READERS</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'html'</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
<span class="s1">'htm'</span><span class="p">:</span> <span class="kc">None</span><span class="p">}</span>
<span class="n">PATH</span> <span class="o">=</span> <span class="s1">'content'</span>
<span class="n">OUTPUT_PATH</span> <span class="o">=</span> <span class="s1">'../minchinweb.github.io-master/'</span>
<span class="c1"># Add Blog to sidebar</span>
<span class="n">MENUITEMS</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'Blog'</span><span class="p">,</span> <span class="s1">'http://blog.minchin.ca/'</span><span class="p">,</span> <span class="s1">'fa fa-pencil'</span><span class="p">),</span> <span class="p">)</span>
<span class="n">DISPLAY_PAGES_ON_MENU</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># disable Tags, etc</span>
<span class="n">TAGS_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">TAG_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">CATEGORY_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">CATEGORY_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">CATEGORIES_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">CATEGORIES_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">ARTICLE_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">ARTICLE_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">AUTHORS_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">AUTHORS_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">ARCHIVES_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">ARCHIVES_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">PAGE_URL</span> <span class="o">=</span> <span class="s2">"</span><span class="si">{slug}</span><span class="s2">/"</span>
<span class="n">PAGE_SAVE_AS</span> <span class="o">=</span> <span class="s2">"</span><span class="si">{slug}</span><span class="s2">/index.html"</span>
<span class="c1"># Theme Related</span>
<span class="n">TYPOGRIFY</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">THEME</span> <span class="o">=</span> <span class="s1">'themes/pelican-minchin-ca'</span>
<span class="n">SITELOGO</span> <span class="o">=</span> <span class="s1">'images/MinchindotCA-200.png'</span>
<span class="n">SITELOGO_SIZE</span> <span class="o">=</span> <span class="s1">'100%'</span>
<span class="n">PYGMENTS_STYLE</span> <span class="o">=</span> <span class="s1">'friendly'</span>
<span class="n">DISPLAY_BREADCRUMBS</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">FAVICON</span> <span class="o">=</span> <span class="s1">'favicon.ico'</span>
<span class="n">BOOTSTRAP_THEME</span> <span class="o">=</span> <span class="s1">'minchin-ca'</span>
<span class="n">USE_OPEN_GRAPH</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">CUSTOM_CSS</span> <span class="o">=</span> <span class="s1">'css/minchin-ca.css'</span>
<span class="n">DOCUTIL_CSS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">GOOGLE_ANALYTICS_UNIVERSAL</span> <span class="o">=</span> <span class="s1">'UA-384291-3'</span>
<span class="n">GOOGLE_ANALYTICS_UNIVERSAL_PROPERTY</span> <span class="o">=</span> <span class="s1">'minchin.ca'</span>
<span class="c1"># Plugins</span>
<span class="n">PLUGIN_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'../pelican-plugins'</span><span class="p">,</span> <span class="p">]</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'assets'</span><span class="p">,</span> <span class="p">]</span>
<span class="n">ASSET_CSS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">ASSET_JS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">SITEMAP</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"format"</span><span class="p">:</span> <span class="s2">"xml"</span><span class="p">,</span>
<span class="p">}</span>
<span class="c1"># # Make things disappear</span>
<span class="n">DISPLAY_CATEGORIES_ON_MENU</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">HIDE_SITENAME</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">HIDE_SIDEBAR</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">FEED_ALL_ATOM</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">FEED_ALL_RSS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">GITHUB_USER</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">ADDTHIS_PROFILE</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">DISQUS_SITENAME</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">PDF_PROCESSOR</span> <span class="o">=</span> <span class="kc">False</span>
</code></pre></div>
<h2>Minchin Genealogy</h2>
<p>I host a copy of my genealogy on my personal website. The generate site
contains over 11,000 pages (and so demonstrates that Pelican can handle very
large sites). This site uses the same theme as <em>Minchin.ca</em>. I’ve used
automation (in this case, a Python script) to automate source data generation
and final site upload. This is hosted on GitHub pages as a <em>project page</em>. This
site is set up to allow comments via email.</p>
<h3><code>gen_upload.py</code> — Build Automation Script</h3>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/python</span>
<span class="c1"># -*- coding: utf-8 -*-</span>
<span class="sd">'''Genealogy Uploader</span>
<span class="sd">v.3.2.3 - WM - January 7, 2016</span>
<span class="sd">This script serves to semi-automate the building and uploading of my</span>
<span class="sd">genealogy website. It is intended to be semi-interactive and run from the</span>
<span class="sd">command line.'''</span>
<span class="kn">import</span> <span class="nn">codecs</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">date</span><span class="p">,</span> <span class="n">datetime</span>
<span class="kn">import</span> <span class="nn">fileinput</span>
<span class="kn">import</span> <span class="nn">multiprocessing</span> <span class="k">as</span> <span class="nn">mp</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">textwrap</span>
<span class="kn">import</span> <span class="nn">uuid</span>
<span class="kn">import</span> <span class="nn">webbrowser</span>
<span class="kn">import</span> <span class="nn">zipfile</span>
<span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span>
<span class="kn">import</span> <span class="nn">colorama</span>
<span class="kn">from</span> <span class="nn">colorama</span> <span class="kn">import</span> <span class="n">Fore</span><span class="p">,</span> <span class="n">Style</span>
<span class="kn">from</span> <span class="nn">invoke</span> <span class="kn">import</span> <span class="n">run</span><span class="p">,</span> <span class="n">task</span>
<span class="kn">from</span> <span class="nn">joblib</span> <span class="kn">import</span> <span class="n">Parallel</span><span class="p">,</span> <span class="n">delayed</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">winshell</span>
<span class="kn">import</span> <span class="nn">minchin.text</span>
<span class="n">__version__</span> <span class="o">=</span> <span class="s1">'3.2.3'</span>
<span class="n">colorama</span><span class="o">.</span><span class="n">init</span><span class="p">()</span>
<span class="n">COPYRIGHT_START_YEAR</span> <span class="o">=</span> <span class="mi">1987</span>
<span class="n">ADAM_LINK</span> <span class="o">=</span> <span class="s2">"http://gigatrees.com"</span>
<span class="n">ADAM_FOOTER</span> <span class="o">=</span> <span class="s2">"<p><strong>Are we related?</strong> Are you a long lost cousin? Spotted an error here? This website remains a work-in-progress and I would love to hear from you. Drop me a line at minchinweb [at] gmail [dot] com.</p>"</span>
<span class="n">INDENT</span> <span class="o">=</span> <span class="s2">" "</span><span class="o">*</span><span class="mi">4</span>
<span class="n">GITHUB_FOLDER</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s2">"S:\Documents\GitHub\genealogy-gh-pages"</span><span class="p">)</span>
<span class="n">PHOTO_FOLER</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s2">"S:\Documents\Genealogy"</span><span class="p">)</span>
<span class="n">DOWNLOAD_FOLDER</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s2">"S:\Downloads\Chrome"</span><span class="p">)</span>
<span class="n">URL_ROOT</span> <span class="o">=</span> <span class="s2">"http://minchin.ca/genealogy"</span>
<span class="n">REPO_URL</span> <span class="o">=</span> <span class="s2">"https://github.com/MinchinWeb/genealogy.git"</span>
<span class="n">ADAM_PREFIX</span> <span class="o">=</span> <span class="s1">'william-minchin-gigatree-offline-'</span>
<span class="n">TODAY_STR</span> <span class="o">=</span> <span class="s1">''</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span><span class="o">.</span><span class="n">year</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]</span> <span class="o">+</span> <span class="nb">str</span><span class="o">.</span><span class="n">zfill</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span><span class="o">.</span><span class="n">month</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="nb">str</span><span class="o">.</span><span class="n">zfill</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span><span class="o">.</span><span class="n">day</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">GEDCOM_EXPECTED</span> <span class="o">=</span> <span class="s1">'William '</span> <span class="o">+</span> <span class="n">TODAY_STR</span> <span class="o">+</span> <span class="s1">'.ged'</span>
<span class="n">USER_FOLDER</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">expanduser</span><span class="p">(</span><span class="s1">'~'</span><span class="p">))</span>
<span class="n">MY_GEDCOM</span> <span class="o">=</span> <span class="n">USER_FOLDER</span> <span class="o">/</span> <span class="s1">'Desktop'</span> <span class="o">/</span> <span class="n">GEDCOM_EXPECTED</span>
<span class="n">start_time</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
<span class="n">step_no</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># step counter</span>
<span class="c1"># folder where the script is saved</span>
<span class="n">HERE_FOLDER</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">realpath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)))</span>
<span class="n">WORKING_FOLDER</span> <span class="o">=</span> <span class="n">HERE_FOLDER</span> <span class="c1"># current working directory</span>
<span class="n">CONTENT_FOLDER</span> <span class="o">=</span> <span class="n">HERE_FOLDER</span> <span class="o">/</span> <span class="s1">'content'</span> <span class="o">/</span> <span class="s1">'pages'</span>
<span class="n">adam_zip</span> <span class="o">=</span> <span class="s1">''</span> <span class="c1"># set later</span>
<span class="n">tracking_filename</span> <span class="o">=</span> <span class="s1">''</span> <span class="c1"># set later</span>
<span class="k">def</span> <span class="nf">addimage</span><span class="p">(</span><span class="n">image</span><span class="p">):</span>
<span class="w"> </span><span class="sd">'''Take the file listed in image, finds in my genealogy photo directory, and</span>
<span class="sd"> adds it to the GitHub folder.'''</span>
<span class="w"> </span><span class="sd">'''TO-DO: implement this!!'''</span>
<span class="k">pass</span>
<span class="c1"># multiple replacement</span>
<span class="c1"># from http://stackoverflow.com/questions/6116978/python-replace-multiple-strings</span>
<span class="c1">#</span>
<span class="c1"># Usage:</span>
<span class="c1"># >>> replacements = (u"café", u"tea"), (u"tea", u"café"), (u"like", u"love")</span>
<span class="c1"># >>> print multiple_replace(u"Do you like café? No, I prefer tea.", *replacements)</span>
<span class="c1"># Do you love tea? No, I prefer café.</span>
<span class="k">def</span> <span class="nf">multiple_replacer</span><span class="p">(</span><span class="o">*</span><span class="n">key_values</span><span class="p">):</span>
<span class="n">replace_dict</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">key_values</span><span class="p">)</span>
<span class="n">replacement_function</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">match</span><span class="p">:</span> <span class="n">replace_dict</span><span class="p">[</span><span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">)]</span>
<span class="n">pattern</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s2">"|"</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">re</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">k</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">key_values</span><span class="p">]),</span> <span class="n">re</span><span class="o">.</span><span class="n">M</span> <span class="o">|</span> <span class="n">re</span><span class="o">.</span><span class="n">I</span><span class="p">)</span>
<span class="k">return</span> <span class="k">lambda</span> <span class="n">string</span><span class="p">:</span> <span class="n">pattern</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="n">replacement_function</span><span class="p">,</span> <span class="n">string</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">multiple_replace</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="o">*</span><span class="n">key_values</span><span class="p">):</span>
<span class="k">return</span> <span class="n">multiple_replacer</span><span class="p">(</span><span class="o">*</span><span class="n">key_values</span><span class="p">)(</span><span class="n">string</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_adam_version</span><span class="p">():</span>
<span class="n">soup_file</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span> <span class="o">/</span> <span class="s1">'names.html'</span><span class="p">),</span> <span class="s1">'r'</span><span class="p">)</span>
<span class="n">soup</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">soup_file</span><span class="p">,</span> <span class="s2">"lxml"</span><span class="p">)</span>
<span class="n">soup_file</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">return</span> <span class="n">soup</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="s2">"gt-version"</span><span class="p">)</span><span class="o">.</span><span class="n">get_text</span><span class="p">()</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">'utf-8'</span><span class="p">)</span> <span class="c1"># 'Built by Adam 1.35.0.0' or the like</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">export_gedcom</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Export from RootsMagic.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="k">global</span> <span class="n">start_time</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Export from RootsMagic."</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">call the file </span><span class="si">{}{}{}</span><span class="s2"> and save it to the desktop"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="o">*</span><span class="mi">2</span><span class="p">,</span> <span class="n">Style</span><span class="o">.</span><span class="n">BRIGHT</span><span class="p">,</span> <span class="n">GEDCOM_EXPECTED</span><span class="p">,</span> <span class="n">Style</span><span class="o">.</span><span class="n">RESET_ALL</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">do not include LDS information"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="o">*</span><span class="mi">2</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">no need to privatize individuals (at this step)"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="o">*</span><span class="mi">2</span><span class="p">))</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">query_yes_quit</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">Next?"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">),</span> <span class="n">default</span><span class="o">=</span><span class="s2">"yes"</span><span class="p">):</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">start_time</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">MY_GEDCOM</span><span class="p">)</span><span class="o">.</span><span class="n">st_ctime</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">Your file doesn't seem to exist. Exiting..."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">))</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">clean_gedcom</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Cleaning up GEDCOM.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Cleaning up GEDCOM."</span><span class="p">)</span>
<span class="c1"># replace image paths</span>
<span class="n">gedcom_file</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">MY_GEDCOM</span><span class="p">),</span> <span class="s1">'r'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span> <span class="c1"># add failsafe is the fail doesn't exist yet or is still being written to</span>
<span class="n">subject</span> <span class="o">=</span> <span class="n">gedcom_file</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">gedcom_file</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">pattern</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">'S:</span><span class="se">\\</span><span class="s1">Documents</span><span class="se">\\</span><span class="s1">Genealogy</span><span class="se">\\</span><span class="s1">([0-9]+[\.[a-z]+]*\.? )*'</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">IGNORECASE</span><span class="p">)</span> <span class="c1"># path start</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">pattern</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="s1">'images/'</span><span class="p">,</span> <span class="n">subject</span><span class="p">)</span>
<span class="n">pattern2</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">'(images.*)</span><span class="se">\\</span><span class="s1">'</span><span class="p">)</span> <span class="c1"># reverse slashes in rest of path</span>
<span class="n">result2</span> <span class="o">=</span> <span class="n">pattern2</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s1">'\1/'</span><span class="p">,</span> <span class="n">result</span><span class="p">)</span>
<span class="n">result3</span> <span class="o">=</span> <span class="n">pattern2</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s1">'\1/'</span><span class="p">,</span> <span class="n">result2</span><span class="p">)</span>
<span class="n">f_out</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">MY_GEDCOM</span><span class="p">),</span> <span class="s1">'w'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span>
<span class="n">f_out</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">result3</span><span class="p">)</span>
<span class="n">f_out</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">upload_gedcom</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Upload GEDCOM to Gigatree.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". The file is now ready to upload to Gigatrees."</span><span class="p">)</span>
<span class="n">webbrowser</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s2">"http://gigatrees.com/toolbox/gigatree"</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">log-in (using Facebook)"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="o">*</span><span class="mi">2</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">now click 'generate report'"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="o">*</span><span class="mi">2</span><span class="p">))</span>
<span class="c1"># check to see if we're logged in</span>
<span class="c1"># log in, if needed</span>
<span class="c1"># discard old GEDCOM</span>
<span class="c1"># upload new GEDCOM</span>
<span class="c1"># run generator</span>
<span class="c1"># download new output</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">check_images</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Check which images have already been uploaded.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Checking images."</span><span class="p">)</span>
<span class="n">gedcom_file</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">MY_GEDCOM</span><span class="p">),</span> <span class="s1">'r'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span>
<span class="n">subject</span> <span class="o">=</span> <span class="n">gedcom_file</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">gedcom_file</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">missing_matches</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">all_matches</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">matches</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">wrapper</span> <span class="o">=</span> <span class="n">textwrap</span><span class="o">.</span><span class="n">TextWrapper</span><span class="p">(</span><span class="n">width</span><span class="o">=</span><span class="mi">79</span><span class="p">,</span> <span class="n">initial_indent</span><span class="o">=</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">subsequent_indent</span><span class="o">=</span><span class="n">INDENT</span><span class="o">*</span><span class="mi">2</span><span class="p">)</span>
<span class="n">pattern_bad</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s2">"missing "</span><span class="p">)</span>
<span class="k">for</span> <span class="n">match</span> <span class="ow">in</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">'(images/.+\.(jpg|jpeg|png|gif|pdf))'</span><span class="p">,</span> <span class="n">subject</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">IGNORECASE</span><span class="p">):</span>
<span class="n">all_matches</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">match</span><span class="p">)</span>
<span class="n">all_matches</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">all_matches</span><span class="p">))</span> <span class="c1"># remove duplicates and sort</span>
<span class="k">for</span> <span class="n">match</span> <span class="ow">in</span> <span class="n">all_matches</span><span class="p">:</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">head</span><span class="p">(</span><span class="n">URL_ROOT</span> <span class="o">+</span> <span class="s2">"/"</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">match</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span> <span class="n">allow_redirects</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">r</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="n">requests</span><span class="o">.</span><span class="n">codes</span><span class="o">.</span><span class="n">ok</span><span class="p">:</span>
<span class="n">mytext</span> <span class="o">=</span> <span class="n">wrapper</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="s2">"missing </span><span class="si">{}</span><span class="s2"> -> </span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">status_code</span><span class="p">),</span> <span class="n">match</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">pattern_bad</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="n">Fore</span><span class="o">.</span><span class="n">RED</span> <span class="o">+</span> <span class="n">Style</span><span class="o">.</span><span class="n">BRIGHT</span> <span class="o">+</span> <span class="s2">"missing "</span> <span class="o">+</span> <span class="n">Style</span><span class="o">.</span><span class="n">RESET_ALL</span><span class="p">,</span> <span class="n">mytext</span><span class="p">))</span>
<span class="n">missing_matches</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">match</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">matches</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">missing_matches</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}{}</span><span class="s2"> images matching. No missing images."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">matches</span><span class="p">)))</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="si">{}{}</span><span class="s2"> images matching."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">matches</span><span class="p">)))</span>
<span class="n">q_add_images</span> <span class="o">=</span> <span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">query_yes_no_all</span><span class="p">(</span><span class="s2">"</span><span class="si">{}{}</span><span class="s2"> missing images. Add them?"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">missing_matches</span><span class="p">))),</span> <span class="n">default</span><span class="o">=</span><span class="s2">"no"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">q_add_images</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span> <span class="c1"># all</span>
<span class="k">for</span> <span class="n">image</span> <span class="ow">in</span> <span class="n">missing_matches</span><span class="p">:</span>
<span class="n">addimage</span><span class="p">(</span><span class="n">image</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">q_add_images</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span> <span class="c1"># yes</span>
<span class="k">for</span> <span class="n">image</span> <span class="ow">in</span> <span class="n">missing_matches</span><span class="p">:</span>
<span class="k">if</span> <span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">querry_yes_no</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">Add </span><span class="si">{}</span><span class="s2">?"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="o">*</span><span class="mi">2</span><span class="p">,</span> <span class="n">image</span><span class="p">),</span> <span class="n">default</span><span class="o">=</span><span class="s2">"yes"</span><span class="p">):</span>
<span class="n">addimage</span><span class="p">(</span><span class="n">image</span><span class="p">)</span>
<span class="c1"># TO-DO: implement this!</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span> <span class="c1"># no</span>
<span class="k">pass</span>
<span class="c1"># write missing images to a file</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'missing-images.txt'</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"Genealogy Uploader, v.</span><span class="si">{}</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">__version__</span><span class="p">)))</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'</span><span class="si">{}</span><span class="se">\n\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">MY_GEDCOM</span><span class="p">))</span>
<span class="k">for</span> <span class="n">missing</span> <span class="ow">in</span> <span class="n">missing_matches</span><span class="p">:</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'</span><span class="si">{}</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">missing</span><span class="p">))</span>
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">delete_old_output</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Delete old Pelican output.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Deleting old Pelican output."</span><span class="p">)</span>
<span class="n">to_delete</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">html_files</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">GITHUB_FOLDER</span><span class="p">))</span>
<span class="n">all_files</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">GITHUB_FOLDER</span><span class="p">))</span>
<span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">all_files</span><span class="p">:</span>
<span class="k">if</span> <span class="n">filename</span> <span class="o">==</span> <span class="p">(</span><span class="s1">'.git'</span><span class="p">):</span>
<span class="k">pass</span> <span class="c1"># don't drop the GIT repo</span>
<span class="k">elif</span> <span class="n">filename</span> <span class="o">==</span> <span class="p">(</span><span class="s1">'images'</span><span class="p">):</span>
<span class="k">pass</span> <span class="c1"># don't drop the image folder</span>
<span class="k">elif</span> <span class="n">filename</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s1">'.html'</span><span class="p">):</span>
<span class="n">html_files</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">to_delete</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1"># delete HTML files</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'del *.html -y'</span><span class="p">)</span>
<span class="n">bar</span> <span class="o">=</span> <span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">progressbar</span><span class="p">(</span><span class="n">maximum</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">to_delete</span><span class="p">)</span> <span class="o">+</span> <span class="n">html_files</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">=</span> <span class="n">html_files</span>
<span class="n">bar</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">counter</span><span class="p">)</span>
<span class="c1"># delete everything else</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">to_delete</span><span class="p">:</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">delete_file</span><span class="p">(</span><span class="n">my_file</span><span class="p">,</span> <span class="n">no_confirm</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">allow_undo</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">silent</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">bar</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">counter</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="si">{}{}</span><span class="s2"> files deleted."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="o">*</span><span class="mi">2</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">to_delete</span><span class="p">)</span> <span class="o">+</span> <span class="n">html_files</span><span class="p">)))</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">delete_old_adam</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Delete old Gigatrees output.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Deleting old Gigatree output."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'del *.* /q'</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">get_new_adam</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Get new Gigatree output.'''</span>
<span class="c1"># TO-DO: allow override of 'start time'</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="k">global</span> <span class="n">adam_zip</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Get new Gigatree output."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">DOWNLOAD_FOLDER</span><span class="p">))</span>
<span class="n">gedcom_time</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">MY_GEDCOM</span><span class="p">))</span><span class="o">.</span><span class="n">st_ctime</span><span class="p">)</span>
<span class="n">count_loops</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">all_files</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">DOWNLOAD_FOLDER</span><span class="p">))</span>
<span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">all_files</span><span class="p">:</span>
<span class="k">if</span> <span class="n">filename</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">ADAM_PREFIX</span><span class="p">)</span> <span class="ow">and</span> <span class="n">filename</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".zip"</span><span class="p">):</span>
<span class="k">if</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">st_ctime</span><span class="p">)</span> <span class="o">></span> <span class="n">gedcom_time</span><span class="p">:</span>
<span class="n">adam_zip</span> <span class="o">=</span> <span class="n">filename</span>
<span class="k">if</span> <span class="n">adam_zip</span> <span class="o">!=</span> <span class="s1">''</span> <span class="ow">and</span> <span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">adam_zip</span><span class="p">)</span><span class="o">.</span><span class="n">st_size</span> <span class="o">></span> <span class="mi">1000</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">count_loops</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">count_loops</span> <span class="o">></span> <span class="mi">60</span><span class="p">:</span>
<span class="k">if</span> <span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">query_yes_quit</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">We've waited 30 minutes. Keep waiting?"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">),</span> <span class="n">default</span><span class="o">=</span><span class="s2">"yes"</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">False</span><span class="p">:</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">count_loops</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">wait</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">copy_file</span><span class="p">(</span><span class="n">adam_zip</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">step_unzip</span><span class="p">():</span>
<span class="c1"># Test 1: 6:48.948 for 9,999 files</span>
<span class="c1"># Test 2: 2:05.188204 for 11,1698 files</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">start_time_local</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Unzip new Gigatree output (Zipfile)."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">zf</span> <span class="o">=</span> <span class="n">zipfile</span><span class="o">.</span><span class="n">ZipFile</span><span class="p">(</span><span class="n">adam_zip</span><span class="p">)</span>
<span class="n">zf</span><span class="o">.</span><span class="n">extractall</span><span class="p">()</span>
<span class="n">zf</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time_local</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">step_unzip_faster</span><span class="p">():</span>
<span class="c1"># see http://dmarkey.com/wordpress/2011/10/15/python-zipfile-speedup-tips/</span>
<span class="c1"># Test 1: 4:44.459 for 9,999 files</span>
<span class="c1"># this doesn't appear to work on Python 3.5.1, says it's not a zip file</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">start_time_local</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Unzip new Gigatree output (Zipfile faster)."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">zf</span> <span class="o">=</span> <span class="n">zipfile</span><span class="o">.</span><span class="n">ZipFile</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="n">adam_zip</span><span class="p">,</span> <span class="s1">'r'</span><span class="p">))</span>
<span class="n">zf</span><span class="o">.</span><span class="n">extractall</span><span class="p">()</span>
<span class="n">zf</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time_local</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">step_unzip_czip</span><span class="p">():</span>
<span class="c1"># Test 1: 4:46.109 for 9,999 files</span>
<span class="c1"># Test 2: xx for 11,1698 files</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">start_time_local</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Unzip new Gigatree output (czip)."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">zf</span> <span class="o">=</span> <span class="n">czipfile</span><span class="o">.</span><span class="n">ZipFile</span><span class="p">(</span><span class="n">adam_zip</span><span class="p">)</span>
<span class="n">zf</span><span class="o">.</span><span class="n">extractall</span><span class="p">()</span>
<span class="n">zf</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time_local</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">step_unzip_7zip</span><span class="p">():</span>
<span class="c1"># Test 1: 5:09.974 for 9,999 files</span>
<span class="c1"># Test 2: 1:43.229726 for 11,1698 files</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">start_time_local</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Unzip new Gigatree output (7-zip)."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'"C:</span><span class="se">\\</span><span class="s1">Program Files</span><span class="se">\\</span><span class="s1">7-Zip</span><span class="se">\\</span><span class="s1">7z.exe" e </span><span class="si">{}</span><span class="s1"> > nul'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">adam_zip</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time_local</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">step_unzip_infozip</span><span class="p">():</span>
<span class="c1"># Test 2: 1:51.550671 for 11,1698 files</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">start_time_local</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Unzip new Gigatree output (unzip.exe)."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'C:</span><span class="se">\\</span><span class="s1">bin</span><span class="se">\\</span><span class="s1">unzip.exe </span><span class="si">{}</span><span class="s1"> > nul'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">adam_zip</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time_local</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">unzip_adam</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Unzip new Adam output.'''</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">step_unzip_7zip</span><span class="p">()</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">step_unzip</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">php_to_html</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Change any .php files to .html.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Rename all .php files."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'rename *.php *.html'</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">copy_js</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Copy Gigatree .js files.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Copy Gigatree .js files."</span><span class="p">)</span>
<span class="c1"># files are copied from the base CONTENT_FOLDER (where they are put by unzipping</span>
<span class="c1"># the adam.zip) to the CONTENT_FOLDER / js (where Pelican will find them)</span>
<span class="n">js_files</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'tab-list-handler.js'</span><span class="p">,</span>
<span class="s1">'tooltip-handler.js'</span><span class="p">,</span>
<span class="s1">'graph-handler.js'</span><span class="p">,</span>
<span class="s1">'gigatrees-map-min.js'</span><span class="p">,</span> <span class="p">)</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">js_files</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">delete_file</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span> <span class="o">/</span> <span class="s1">'js'</span> <span class="o">/</span> <span class="n">my_file</span><span class="p">),</span> <span class="n">no_confirm</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">allow_undo</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">silent</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">pass</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">copy_file</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span> <span class="o">/</span> <span class="n">my_file</span><span class="p">),</span> <span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span> <span class="o">/</span> <span class="s1">'js'</span> <span class="o">/</span> <span class="n">my_file</span><span class="p">),</span> <span class="n">no_confirm</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="c1"># not needed; the elements of the 'gigatrees.css' needed have been folded</span>
<span class="c1"># directly into the theme LESS files</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">copy_css</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Copy Gigatree .css files.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Copy Gigatree .css files."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">css_files</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'gigatrees.css'</span><span class="p">,</span> <span class="p">)</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">css_files</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">delete_file</span><span class="p">(</span><span class="s2">"../css/"</span> <span class="o">+</span> <span class="n">my_file</span><span class="p">,</span> <span class="n">no_confirm</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">allow_undo</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">silent</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">pass</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">copy_file</span><span class="p">(</span><span class="n">my_file</span><span class="p">,</span> <span class="s2">"../css/"</span> <span class="o">+</span> <span class="n">my_file</span><span class="p">,</span> <span class="n">no_confirm</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">copy_img</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Copy Gigatree image files.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Copy Gigatree image files."</span><span class="p">)</span>
<span class="c1"># files are copied from the base CONTENT_FOLDER (where they are put by unzipping</span>
<span class="c1"># the adam.zip) to the CONTENT_FOLDER / img (where Pelican will find them)</span>
<span class="n">img_files</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'arrowd.png'</span><span class="p">,</span>
<span class="s1">'arrowl.png'</span><span class="p">,</span>
<span class="s1">'arrowr.png'</span><span class="p">,</span>
<span class="s1">'arrowu.png'</span><span class="p">,</span>
<span class="s1">'bg-black.png'</span><span class="p">,</span>
<span class="s1">'bg-pattern.png'</span><span class="p">,</span>
<span class="s1">'mapicon_f.png'</span><span class="p">,</span>
<span class="s1">'mapicon_m.png'</span><span class="p">,</span>
<span class="s1">'mapicon_u.png'</span><span class="p">,</span>
<span class="s1">'mapmarker1.png'</span><span class="p">,</span>
<span class="s1">'mapmarker2.png'</span><span class="p">,</span>
<span class="s1">'mapmarker3.png'</span><span class="p">,</span>
<span class="s1">'mapmarker4.png'</span><span class="p">,</span>
<span class="s1">'mapmarker5.png'</span><span class="p">,</span>
<span class="s1">'avatar.jpg'</span><span class="p">,</span>
<span class="s1">'image.jpg'</span><span class="p">,</span>
<span class="s1">'pdf.jpg'</span><span class="p">,</span> <span class="p">)</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">img_files</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">delete_file</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span> <span class="o">/</span> <span class="s1">'img'</span> <span class="o">/</span> <span class="n">my_file</span><span class="p">),</span> <span class="n">no_confirm</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">allow_undo</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">silent</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">pass</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">copy_file</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span> <span class="o">/</span> <span class="n">my_file</span><span class="p">),</span> <span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span> <span class="o">/</span> <span class="s1">'img'</span> <span class="o">/</span> <span class="n">my_file</span><span class="p">),</span> <span class="n">no_confirm</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">replace_index</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Copy over index.md, 404.md.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Copy over index.md, 404.md."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"index.md"</span><span class="p">,</span> <span class="s2">"index.html"</span><span class="p">,</span> <span class="s2">"404.md"</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">delete_file</span><span class="p">(</span><span class="n">my_file</span><span class="p">,</span> <span class="n">no_confirm</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">allow_undo</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">silent</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"index.md"</span><span class="p">,</span> <span class="s2">"404.md"</span><span class="p">):</span>
<span class="n">winshell</span><span class="o">.</span><span class="n">copy_file</span><span class="p">(</span><span class="s2">"../../_unchanging_pages/</span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">my_file</span><span class="p">),</span> <span class="n">my_file</span><span class="p">,</span> <span class="n">no_confirm</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">set_pelican_variables</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Sets a couple of variables that Pelican uses while generating the site.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Setting up Pelican."</span><span class="p">)</span>
<span class="n">adam_version_text</span> <span class="o">=</span> <span class="n">get_adam_version</span><span class="p">()</span> <span class="c1"># 'Built by Adam 1.35.0.0 ' or the like</span>
<span class="n">adam_version_text</span> <span class="o">=</span> <span class="n">adam_version_text</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">'utf-8'</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="n">date_in_text</span> <span class="o">=</span> <span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%B </span><span class="si">%d</span><span class="s2">, %Y"</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">' 0'</span><span class="p">,</span> <span class="s1">' '</span><span class="p">)</span> <span class="c1"># 'January 7, 2014' or the like</span>
<span class="n">year_range</span> <span class="o">=</span> <span class="s2">"</span><span class="si">{}</span><span class="s2">-</span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">COPYRIGHT_START_YEAR</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">year</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'</span><span class="si">{}{}</span><span class="s1"> - </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">adam_version_text</span><span class="p">,</span> <span class="n">date_in_text</span><span class="p">))</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">WORKING_FOLDER</span> <span class="o">/</span> <span class="s1">'adamconf.py'</span><span class="p">),</span> <span class="s1">'w'</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'# Genealogy Uploader, v.</span><span class="si">{}</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">__version__</span><span class="p">)))</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'# </span><span class="si">{}</span><span class="se">\n\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">MY_GEDCOM</span><span class="p">)))</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'ADAM = True</span><span class="se">\n</span><span class="s1">'</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'ADAM_VERSION = "</span><span class="si">{}</span><span class="s1">"</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">adam_version_text</span><span class="p">))</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'ADAM_UPDATED = "</span><span class="si">{}</span><span class="s1">"</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">date_in_text</span><span class="p">))</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'ADAM_COPY_DATE = "</span><span class="si">{}</span><span class="s1">"</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">year_range</span><span class="p">))</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'ADAM_LINK = "</span><span class="si">{}</span><span class="s1">"</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ADAM_LINK</span><span class="p">))</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'ADAM_FOOTER = "</span><span class="si">{}</span><span class="s1">"</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ADAM_FOOTER</span><span class="p">))</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'ADAM_PUBLISH = True</span><span class="se">\n</span><span class="s1">'</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">replace_emails</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Hide emails in Sources.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Hiding Emails."</span><span class="p">)</span>
<span class="c1"># replace and hide emails; but some of these are over lines breaks,</span>
<span class="c1"># so we'll have to search and replace through the output</span>
<span class="c1"># We are actually only considering 'Sources', as that's where</span>
<span class="c1"># all the emails seem to be...</span>
<span class="n">replacements</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"someone@hotmail.com"</span><span class="p">,</span> <span class="s1">'[email redacted]'</span><span class="p">),</span> \
<span class="p">(</span><span class="s2">"someone@gmail.com"</span><span class="p">,</span> <span class="s1">'[email redacted]'</span><span class="p">),</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">all_files</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">all_html_files</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">all_files</span><span class="p">:</span>
<span class="c1">#if my_file.endswith(".html"):</span>
<span class="k">if</span> <span class="n">my_file</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">'sources-'</span><span class="p">):</span>
<span class="n">all_html_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">my_file</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">bar</span> <span class="o">=</span> <span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">progressbar</span><span class="p">(</span><span class="n">maximum</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">all_html_files</span><span class="p">))</span>
<span class="c1"># inline search and replace</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">all_html_files</span><span class="p">:</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">fileinput</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">my_file</span><span class="p">,</span> <span class="n">inplace</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="n">multiple_replace</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="o">*</span><span class="n">replacements</span><span class="p">))</span>
<span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">bar</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">counter</span><span class="p">)</span>
<span class="nb">print</span><span class="p">()</span> <span class="c1"># clear progress bar</span>
<span class="k">def</span> <span class="nf">html_fixes</span><span class="p">(</span><span class="n">my_file</span><span class="p">):</span>
<span class="w"> </span><span class="sd">'''</span>
<span class="sd"> Applies the various updates and changes I want done to the raw Gigatrees</span>
<span class="sd"> HTML.</span>
<span class="sd"> Assumes 'my_file' is in the CONTENT_FOLDER.</span>
<span class="sd"> Assumes 'my_file' is a string.</span>
<span class="sd"> '''</span>
<span class="k">with</span> <span class="n">codecs</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span> <span class="o">/</span> <span class="n">my_file</span><span class="p">),</span> <span class="s1">'r'</span><span class="p">,</span> <span class="s1">'utf-8'</span><span class="p">)</span> <span class="k">as</span> <span class="n">html_doc</span><span class="p">:</span>
<span class="n">my_html</span> <span class="o">=</span> <span class="n">html_doc</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">soup</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">my_html</span><span class="p">,</span> <span class="s2">"lxml"</span><span class="p">)</span>
<span class="c1"># change page title</span>
<span class="n">title_tag</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">html</span><span class="o">.</span><span class="n">head</span><span class="o">.</span><span class="n">title</span>
<span class="k">for</span> <span class="n">tag</span> <span class="ow">in</span> <span class="n">soup</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="s2">"gt-page-title"</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
<span class="n">title_tag</span><span class="o">.</span><span class="n">string</span><span class="o">.</span><span class="n">replace_with</span><span class="p">(</span><span class="n">tag</span><span class="o">.</span><span class="n">string</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span>
<span class="n">tag</span><span class="o">.</span><span class="n">decompose</span><span class="p">()</span>
<span class="c1"># dump all the meta tags in the head section</span>
<span class="k">for</span> <span class="n">tag</span> <span class="ow">in</span> <span class="n">soup</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">):</span>
<span class="n">tag</span><span class="o">.</span><span class="n">decompose</span><span class="p">()</span>
<span class="c1"># Remove links to CDN stuff I serve locally</span>
<span class="n">js_served_locally</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'jquery.min.js'</span><span class="p">,</span>
<span class="s1">'jquery-ui.min.js'</span><span class="p">,</span>
<span class="s1">'bootstrap.min.js'</span><span class="p">,</span>
<span class="s1">'globalize.min.js'</span><span class="p">,</span>
<span class="s1">'dx.chartjs.js'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">tag</span> <span class="ow">in</span> <span class="n">soup</span><span class="p">(</span><span class="s2">"script"</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">link</span> <span class="o">=</span> <span class="n">tag</span><span class="p">[</span><span class="s2">"src"</span><span class="p">]</span>
<span class="k">if</span> <span class="n">link</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="n">js_served_locally</span><span class="p">):</span>
<span class="n">tag</span><span class="o">.</span><span class="n">decompose</span><span class="p">()</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">pass</span>
<span class="c1"># fix pdf paths?</span>
<span class="c1"># other stuff</span>
<span class="k">for</span> <span class="n">tag</span> <span class="ow">in</span> <span class="n">soup</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="s2">"gt-page-title"</span><span class="p">):</span>
<span class="n">tag</span><span class="o">.</span><span class="n">decompose</span><span class="p">()</span>
<span class="k">for</span> <span class="n">tag</span> <span class="ow">in</span> <span class="n">soup</span><span class="p">(</span><span class="n">class_</span><span class="o">=</span><span class="s2">"gt-version"</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
<span class="n">tag</span><span class="o">.</span><span class="n">decompose</span><span class="p">()</span>
<span class="c1"># at meta tags, used for the breadcrumbs in the link.</span>
<span class="c1"># they need to be in the section, in the form</span>
<span class="c1">#</span>
<span class="c1"># </span>
<span class="c1"># <!-- other stuff... --></span>
<span class="c1"># <meta content="Locations" name="at"/></span>
<span class="c1"># <meta content="places.html" name="at_link"/> <!-- this is added to SITEURL --></span>
<span class="c1"># </span>
<span class="n">new_tags</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">if</span> <span class="n">my_file</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"names-"</span><span class="p">):</span>
<span class="n">new_tag_1</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">new_tag</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s2">"Surnames"</span><span class="p">)</span>
<span class="n">new_tag_2</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">new_tag</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s2">"names.html"</span><span class="p">)</span>
<span class="n">new_tags</span> <span class="o">=</span> <span class="kc">True</span>
<span class="k">elif</span> <span class="n">my_file</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"places-"</span><span class="p">):</span>
<span class="n">new_tag_1</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">new_tag</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s2">"Locations"</span><span class="p">)</span>
<span class="n">new_tag_2</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">new_tag</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s2">"places.html"</span><span class="p">)</span>
<span class="n">new_tags</span> <span class="o">=</span> <span class="kc">True</span>
<span class="k">elif</span> <span class="n">my_file</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"sources-"</span><span class="p">):</span>
<span class="n">new_tag_1</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">new_tag</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s2">"Sources"</span><span class="p">)</span>
<span class="n">new_tag_2</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">new_tag</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s2">"sources.html"</span><span class="p">)</span>
<span class="n">new_tags</span> <span class="o">=</span> <span class="kc">True</span>
<span class="k">elif</span> <span class="n">my_file</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"timeline"</span><span class="p">)</span> <span class="ow">and</span> <span class="n">my_file</span> <span class="o">!=</span> <span class="s2">"timeline.html"</span><span class="p">:</span>
<span class="n">new_tag_1</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">new_tag</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s2">"Timelines"</span><span class="p">)</span>
<span class="n">new_tag_2</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">new_tag</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s2">"timeline.html"</span><span class="p">)</span>
<span class="n">new_tags</span> <span class="o">=</span> <span class="kc">True</span>
<span class="k">if</span> <span class="n">new_tags</span><span class="p">:</span>
<span class="n">new_tag_1</span><span class="o">.</span><span class="n">attrs</span><span class="p">[</span><span class="s1">'name'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'at'</span>
<span class="n">new_tag_2</span><span class="o">.</span><span class="n">attrs</span><span class="p">[</span><span class="s1">'name'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'at_link'</span>
<span class="n">soup</span><span class="o">.</span><span class="n">html</span><span class="o">.</span><span class="n">head</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">new_tag_1</span><span class="p">)</span>
<span class="n">soup</span><span class="o">.</span><span class="n">html</span><span class="o">.</span><span class="n">head</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">new_tag_2</span><span class="p">)</span>
<span class="c1"># write fixed version of file to disk</span>
<span class="k">with</span> <span class="n">codecs</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span> <span class="o">/</span> <span class="n">my_file</span><span class="p">),</span> <span class="s1">'w'</span><span class="p">,</span> <span class="s1">'utf-8'</span><span class="p">)</span> <span class="k">as</span> <span class="n">html_doc</span><span class="p">:</span>
<span class="n">html_doc</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">soup</span><span class="p">))</span>
<span class="c1">#html_doc.write(soup.prettify())</span>
<span class="k">def</span> <span class="nf">html_fixes_2</span><span class="p">(</span><span class="n">my_file</span><span class="p">,</span> <span class="n">my_bar</span><span class="p">,</span> <span class="n">my_counter</span><span class="p">):</span>
<span class="w"> </span><span class="sd">'''</span>
<span class="sd"> Applies the HTML changes and then updates the counter.</span>
<span class="sd"> Assumes 'my_bar' is of type minchin.text.progressbar</span>
<span class="sd"> Assumes 'my_counter' is of type multiprocessing.Value</span>
<span class="sd"> '''</span>
<span class="n">html_fixes</span><span class="p">(</span><span class="n">my_file</span><span class="p">)</span>
<span class="c1"># get the lock so that different threads can't update it at the same time</span>
<span class="k">with</span> <span class="n">my_counter</span><span class="o">.</span><span class="n">get_lock</span><span class="p">():</span>
<span class="n">my_counter</span><span class="o">.</span><span class="n">value</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">my_bar</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">my_counter</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="sd">'''</span>
<span class="sd">For 11,146 files (2 core, 4 thread machine, program run time):</span>
<span class="sd"> - no html cleaning (so just overhead):</span>
<span class="sd"> 13:50 (partial delete, 7-Zip), 24:52, 21:56, 25:59 (Zipfile)</span>
<span class="sd"> - single thread: 18:49.190113</span>
<span class="sd"> - multi-threaded (n_jobs=9): 4:59.657586</span>
<span class="sd">'''</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">clean_adam_html_single_thread</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Remove nasty and extra HTML.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">start_time_local</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Remove nasty and extra HTML (single threaded)."</span><span class="p">)</span>
<span class="c1">#os.chdir(str(CONTENT_FOLDER))</span>
<span class="n">all_files</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">all_html_files</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">all_files</span><span class="p">:</span>
<span class="k">if</span> <span class="n">my_file</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".html"</span><span class="p">):</span>
<span class="n">all_html_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">my_file</span><span class="p">)</span>
<span class="c1"># this gives us a C-type integer, useful for accessing from multiple threads</span>
<span class="n">counter</span> <span class="o">=</span> <span class="n">mp</span><span class="o">.</span><span class="n">Value</span><span class="p">(</span><span class="s1">'i'</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">bar</span> <span class="o">=</span> <span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">progressbar</span><span class="p">(</span><span class="n">maximum</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">all_html_files</span><span class="p">))</span>
<span class="n">bar</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">counter</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">all_html_files</span><span class="p">:</span>
<span class="n">html_fixes_2</span><span class="p">(</span><span class="n">my_file</span><span class="p">,</span> <span class="n">bar</span><span class="p">,</span> <span class="n">counter</span><span class="p">)</span>
<span class="nb">print</span><span class="p">()</span> <span class="c1"># clear progress bar</span>
<span class="nb">print</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time_local</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">clean_adam_html_multithreaded</span><span class="p">(</span><span class="n">my_n_jobs</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">'''Remove nasty and extra HTML (multi-threaded).'''</span>
<span class="w"> </span><span class="sd">'''</span>
<span class="sd"> Benchmarks</span>
<span class="sd"> n_jobs time</span>
<span class="sd"> 9 6:20</span>
<span class="sd"> 6 6:18 (machine has 6 cores)</span>
<span class="sd"> 4 7:16</span>
<span class="sd"> '''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="c1">#start_time_local = datetime.now()</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Remove nasty and extra HTML (multi-threaded)."</span><span class="p">)</span>
<span class="n">all_files</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">CONTENT_FOLDER</span><span class="p">))</span>
<span class="n">all_html_files</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">all_files</span><span class="p">:</span>
<span class="k">if</span> <span class="n">my_file</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".html"</span><span class="p">):</span>
<span class="n">all_html_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">my_file</span><span class="p">)</span>
<span class="k">if</span> <span class="n">my_n_jobs</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">my_n_jobs</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">cpu_count</span><span class="p">())</span>
<span class="n">Parallel</span><span class="p">(</span><span class="n">n_jobs</span><span class="o">=</span><span class="n">my_n_jobs</span><span class="p">,</span> <span class="n">verbose</span><span class="o">=</span><span class="mi">5</span><span class="p">)(</span><span class="n">delayed</span><span class="p">(</span><span class="n">html_fixes</span><span class="p">)(</span><span class="n">my_file</span><span class="p">)</span> <span class="k">for</span> <span class="n">my_file</span> <span class="ow">in</span> <span class="n">all_html_files</span><span class="p">)</span>
<span class="c1">#print(INDENT, datetime.now() - start_time_local)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">pelican</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Run Pelican.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Run Pelican (site generator)."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">WORKING_FOLDER</span><span class="p">))</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'pelican -s publishconf.py'</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">pelican_local</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Run Pelican (in local, developmental mode).'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Run Pelican (site generator) in local mode."</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">WORKING_FOLDER</span><span class="p">))</span>
<span class="n">run</span><span class="p">(</span><span class="s1">'pelican -s pelicanconf.py'</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">create_tracking</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Create deploy tracking file.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="k">global</span> <span class="n">tracking_filename</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Create deploy tracking file."</span><span class="p">)</span>
<span class="c1"># create a 'random' number using UUID</span>
<span class="c1"># note that the last set of digits will correspond to the workstation</span>
<span class="n">myUUID</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">uuid</span><span class="o">.</span><span class="n">uuid1</span><span class="p">())</span>
<span class="n">tracking_filename</span> <span class="o">=</span> <span class="n">myUUID</span> <span class="o">+</span> <span class="s2">".txt"</span>
<span class="n">target</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">GITHUB_FOLDER</span> <span class="o">/</span> <span class="n">tracking_filename</span><span class="p">),</span> <span class="s1">'w'</span><span class="p">)</span>
<span class="n">target</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">myUUID</span> <span class="o">+</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="n">target</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"Adam upload by Python script.</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="n">target</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">GEDCOM_EXPECTED</span> <span class="o">+</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="n">target</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">git</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Git commit and push.'''</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Git -> commit and push."</span><span class="p">)</span>
<span class="n">commit_msg</span> <span class="o">=</span> <span class="s2">"Gigatrees generated upload from </span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">GEDCOM_EXPECTED</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">GITHUB_FOLDER</span><span class="p">))</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="s1">'</span><span class="si">{}{}</span><span class="s1">> git add -A</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">Fore</span><span class="o">.</span><span class="n">YELLOW</span><span class="p">,</span> <span class="n">Style</span><span class="o">.</span><span class="n">RESET_ALL</span><span class="p">))</span>
<span class="n">r1</span> <span class="o">=</span> <span class="n">run</span><span class="p">(</span><span class="s1">'git add -A'</span><span class="p">,</span> <span class="n">hide</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">r1</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="s1">'</span><span class="si">{}{}</span><span class="s1">> git commit -m "</span><span class="si">{}</span><span class="s1">"</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">Fore</span><span class="o">.</span><span class="n">YELLOW</span><span class="p">,</span> <span class="n">commit_msg</span><span class="p">,</span> <span class="n">Style</span><span class="o">.</span><span class="n">RESET_ALL</span><span class="p">))</span>
<span class="n">r2</span> <span class="o">=</span> <span class="n">run</span><span class="p">(</span><span class="s1">'git commit -m Gigatrees_upload'</span><span class="p">,</span> <span class="n">hide</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">r2</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="s1">'</span><span class="si">{}{}</span><span class="s1">> git push origin</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">Fore</span><span class="o">.</span><span class="n">YELLOW</span><span class="p">,</span> <span class="n">Style</span><span class="o">.</span><span class="n">RESET_ALL</span><span class="p">))</span>
<span class="n">r3</span> <span class="o">=</span> <span class="n">run</span><span class="p">(</span><span class="s1">'git push origin'</span><span class="p">,</span> <span class="n">hide</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">r3</span><span class="o">.</span><span class="n">stdout</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">r3</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">live</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Tell us when we're live.'''</span>
<span class="c1"># TO-DO: find tracking file based on creation/modified date</span>
<span class="k">global</span> <span class="n">step_no</span>
<span class="n">step_no</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">step_no</span><span class="p">)</span><span class="o">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s2">". Wait to go live."</span><span class="p">)</span>
<span class="k">if</span> <span class="n">tracking_filename</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'</span><span class="si">{}</span><span class="s1">No tracking file set.'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">INDENT</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">head</span><span class="p">(</span><span class="n">URL_ROOT</span> <span class="o">+</span> <span class="s2">"/"</span> <span class="o">+</span> <span class="n">tracking_filename</span><span class="p">,</span> <span class="n">allow_redirects</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">if</span> <span class="n">r</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="n">requests</span><span class="o">.</span><span class="n">codes</span><span class="o">.</span><span class="n">ok</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">wait</span><span class="p">(</span><span class="mi">180</span><span class="p">)</span>
<span class="nd">@task</span>
<span class="k">def</span> <span class="nf">all_steps</span><span class="p">():</span>
<span class="w"> </span><span class="sd">'''Everything!'''</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">title</span><span class="p">(</span><span class="s2">"Genealogy Uploader, v.</span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">__version__</span><span class="p">)))</span>
<span class="nb">print</span><span class="p">()</span>
<span class="n">export_gedcom</span><span class="p">()</span> <span class="c1"># works 151230</span>
<span class="n">clean_gedcom</span><span class="p">()</span> <span class="c1"># works</span>
<span class="n">upload_gedcom</span><span class="p">()</span> <span class="c1"># works</span>
<span class="c1">#check_images() # works</span>
<span class="n">delete_old_output</span><span class="p">()</span> <span class="c1"># works</span>
<span class="n">delete_old_adam</span><span class="p">()</span> <span class="c1"># works ~2 min</span>
<span class="n">get_new_adam</span><span class="p">()</span> <span class="c1">#</span>
<span class="n">unzip_adam</span><span class="p">()</span> <span class="c1"># pretty sure works ~5 min</span>
<span class="c1">#php_to_html() # works, brakes if there are no PHP files</span>
<span class="n">copy_js</span><span class="p">()</span> <span class="c1"># works</span>
<span class="c1">#copy_css()</span>
<span class="n">copy_img</span><span class="p">()</span>
<span class="n">replace_index</span><span class="p">()</span> <span class="c1"># works</span>
<span class="n">set_pelican_variables</span><span class="p">()</span> <span class="c1"># works</span>
<span class="n">clean_adam_html_multithreaded</span><span class="p">()</span>
<span class="n">replace_emails</span><span class="p">()</span> <span class="c1"># doesn't crash</span>
<span class="n">create_tracking</span><span class="p">()</span> <span class="c1"># works ~10 sec</span>
<span class="n">pelican</span><span class="p">()</span> <span class="c1"># works (assuming Pelican works)</span>
<span class="n">git</span><span class="p">()</span> <span class="c1">#</span>
<span class="n">live</span><span class="p">()</span> <span class="c1">#</span>
<span class="n">minchin</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">clock_on_right</span><span class="p">(</span><span class="n">Fore</span><span class="o">.</span><span class="n">GREEN</span> <span class="o">+</span> <span class="n">Style</span><span class="o">.</span><span class="n">BRIGHT</span> <span class="o">+</span> <span class="s2">"Update is Live!"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">Style</span><span class="o">.</span><span class="n">RESET_ALL</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">INDENT</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time</span><span class="p">)</span>
<span class="nd">@task</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">does_nothing</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'this does nothing'</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">all_steps</span><span class="p">()</span>
</code></pre></div>
<h3><code>pelicanconf.py</code> — Pelican Configuration</h3>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># -*- coding: utf-8 -*- #</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">curdir</span><span class="p">)</span>
<span class="c1"># Adam configuration options</span>
<span class="n">ADAM_PUBLISH</span> <span class="o">=</span> <span class="kc">False</span>
<span class="kn">from</span> <span class="nn">adamconf</span> <span class="kn">import</span> <span class="o">*</span>
<span class="c1"># ADAM = True is used by the theme tempates to display 'Genealogy only' things</span>
<span class="n">AUTHOR</span> <span class="o">=</span> <span class="s1">'D. Minchin & Wm. Minchin'</span>
<span class="n">SITENAME</span> <span class="o">=</span> <span class="s1">'Minchin.ca'</span>
<span class="k">if</span> <span class="n">ADAM_PUBLISH</span><span class="p">:</span>
<span class="n">SITEURL</span> <span class="o">=</span> <span class="s1">'http://minchin.ca/genealogy'</span>
<span class="n">RELATIVE_URLS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">SITEURL</span> <span class="o">=</span> <span class="s1">'.'</span>
<span class="c1"># Uncomment following line if you want document-relative URLs when developing</span>
<span class="n">RELATIVE_URLS</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">SITE_ROOT_URL</span> <span class="o">=</span> <span class="s1">'http://minchin.ca'</span>
<span class="n">TIMEZONE</span> <span class="o">=</span> <span class="s1">'America/Edmonton'</span>
<span class="n">DEFAULT_LANG</span> <span class="o">=</span> <span class="s1">'en'</span>
<span class="c1"># Feed generation is usually not desired when developing</span>
<span class="n">FEED_ALL_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">CATEGORY_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">TRANSLATION_FEED_ATOM</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">DEFAULT_PAGINATION</span> <span class="o">=</span> <span class="kc">False</span>
<span class="c1"># static paths will be copied under the same name</span>
<span class="c1"># these are relative to the base CONTENT folder</span>
<span class="n">STATIC_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'images'</span><span class="p">,</span>
<span class="s1">'../extras'</span><span class="p">,</span>
<span class="s1">'css'</span><span class="p">,</span>
<span class="s1">'design'</span><span class="p">,</span>
<span class="s1">'js'</span><span class="p">,</span>
<span class="s1">'pages/img'</span><span class="p">,</span>
<span class="s1">'../.gitattributes'</span><span class="p">,</span>
<span class="s1">'../.gitignore'</span><span class="p">,</span>
<span class="s1">'../README.txt'</span><span class="p">,</span>
<span class="p">]</span>
<span class="c1"># A list of files to copy from the source to the destination</span>
<span class="n">EXTRA_PATH_METADATA</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'../.gitattributes'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'.gitattributes'</span><span class="p">},</span>
<span class="s1">'../.gitignore'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'.gitignore'</span><span class="p">},</span>
<span class="s1">'../README.txt'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'README.txt'</span><span class="p">},</span>
<span class="s1">'../extras/minchin.ico'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'favicon.ico'</span><span class="p">},</span>
<span class="s1">'../extras/.nojekyll'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'.nojekyll'</span><span class="p">},</span>
<span class="s1">'js/tab-list-handler.js'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'tab-list-handler.js'</span><span class="p">},</span>
<span class="s1">'js/tooltip-handler.js'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'tooltip-handler.js'</span><span class="p">},</span>
<span class="s1">'js/graph-handler.js'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'graph-handler.js'</span><span class="p">},</span>
<span class="s1">'js/gigatrees-map-min.js'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'gigatrees-map-min.js'</span><span class="p">},</span>
<span class="s1">'pages/img/arrowd.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'arrowd.png'</span><span class="p">},</span>
<span class="s1">'pages/img/arrowl.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'arrowl.png'</span><span class="p">},</span>
<span class="s1">'pages/img/arrowr.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'arrowr.png'</span><span class="p">},</span>
<span class="s1">'pages/img/arrowu.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'arrowu.png'</span><span class="p">},</span>
<span class="s1">'pages/img/bg-black.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'bg-black.png'</span><span class="p">},</span>
<span class="s1">'pages/img/bg-pattern.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'bg-pattern.png'</span><span class="p">},</span>
<span class="s1">'pages/img/mapicon_f.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'mapicon_f.png'</span><span class="p">},</span>
<span class="s1">'pages/img/mapicon_m.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'mapicon_m.png'</span><span class="p">},</span>
<span class="s1">'pages/img/mapicon_u.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'mapicon_u.png'</span><span class="p">},</span>
<span class="s1">'pages/img/mapmarker1.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'mapmarker1.png'</span><span class="p">},</span>
<span class="s1">'pages/img/mapmarker2.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'mapmarker2.png'</span><span class="p">},</span>
<span class="s1">'pages/img/mapmarker3.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'mapmarker3.png'</span><span class="p">},</span>
<span class="s1">'pages/img/mapmarker4.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'mapmarker4.png'</span><span class="p">},</span>
<span class="s1">'pages/img/mapmarker5.png'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'mapmarker5.png'</span><span class="p">},</span>
<span class="s1">'pages/img/avatar.jpg'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'avatar.jpg'</span><span class="p">},</span>
<span class="s1">'pages/img/image.jpg'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'image.jpg'</span><span class="p">},</span>
<span class="s1">'pages/img/pdf.jpg'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'path'</span><span class="p">:</span> <span class="s1">'pdf.jpg'</span><span class="p">},</span>
<span class="p">}</span>
<span class="c1"># Custom settings</span>
<span class="n">FILENAME_METADATA</span> <span class="o">=</span> <span class="s1">'(?P<slug>[\w-]*)'</slug></span> <span class="c1"># so anything before the file extension becomes the slug</span>
<span class="c1">## Please note that the metadata available inside your files takes precedence</span>
<span class="c1"># over the metadata extracted from the filename.</span>
<span class="n">MARKUP</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'rst'</span><span class="p">,</span>
<span class="s1">'md'</span><span class="p">,</span>
<span class="s1">'markdown'</span><span class="p">,</span>
<span class="s1">'mkd'</span><span class="p">,</span>
<span class="s1">'mdown'</span><span class="p">,</span>
<span class="s1">'html'</span><span class="p">,</span>
<span class="s1">'htm'</span><span class="p">))</span>
<span class="n">PATH</span> <span class="o">=</span> <span class="s1">'content'</span>
<span class="n">OUTPUT_PATH</span> <span class="o">=</span> <span class="s1">'../genealogy-gh-pages/'</span>
<span class="c1"># Add Blog to sidebar</span>
<span class="n">MENUITEMS</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'Blog'</span><span class="p">,</span> <span class="s1">'http://blog.minchin.ca/'</span><span class="p">,</span> <span class="s1">'fa fa-pencil'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Genealogy'</span><span class="p">,</span> <span class="n">SITEURL</span><span class="p">,</span> <span class="s1">'glyphicon glyphicon-tree-deciduous'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'My Projects'</span><span class="p">,</span> <span class="s1">'http://minchin.ca/projects/'</span><span class="p">,</span> <span class="s1">'fa fa-flask'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Search'</span><span class="p">,</span> <span class="s1">'http://minchin.ca/search/'</span><span class="p">,</span> <span class="s1">'fa fa-search'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'About'</span><span class="p">,</span> <span class="s1">'http://minchin.ca/about/'</span><span class="p">,</span> <span class="s1">'fa fa-info-circle'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Contact Me'</span><span class="p">,</span> <span class="s1">'http://minchin.ca/contact/'</span><span class="p">,</span> <span class="s1">'fa fa-envelope'</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">MENUITEMS_2_AT</span> <span class="o">=</span> <span class="s1">'Genealogy'</span>
<span class="n">MENUITEMS_2_AT_LINK</span> <span class="o">=</span> <span class="s1">''</span> <span class="c1"># this is added to SITEURL</span>
<span class="n">MENUITEMS_2</span> <span class="o">=</span> <span class="p">((</span><span class="s1">'Surnames'</span><span class="p">,</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'/names.html'</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Updates'</span><span class="p">,</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'/updates.html'</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Sources'</span><span class="p">,</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'/sources.html'</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Distribution Map'</span><span class="p">,</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'/map.html'</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Timelines'</span><span class="p">,</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'/timeline.html'</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
<span class="c1">#('Immigrants', SITEURL + '/immigrants.html', False), # doens't exist in current builds</span>
<span class="c1">#('Nobility', SITEURL + '/titles.html', False), # doens't exist in current builds</span>
<span class="p">(</span><span class="s1">'Locations'</span><span class="p">,</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'/places.html'</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Bonkers Report'</span><span class="p">,</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'/bonkers-report.html'</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'Photos'</span><span class="p">,</span> <span class="n">SITEURL</span> <span class="o">+</span> <span class="s1">'/photos.html'</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
<span class="c1">#('External Links', SITEURL + '/links.html', False), # doens't exist in current builds</span>
<span class="c1">#('Statistics', SITEURL + '/stats.html', False), # stats graphs aren't working right now</span>
<span class="p">)</span>
<span class="n">DISPLAY_PAGES_ON_MENU</span> <span class="o">=</span> <span class="kc">False</span>
<span class="c1"># disable Tags, etc</span>
<span class="n">TAGS_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">TAG_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">CATEGORY_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">CATEGORY_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">CATEGORIES_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">CATEGORIES_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">ARTICLE_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">ARTICLE_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">AUTHORS_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">AUTHORS_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">ARCHIVES_URL</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">ARCHIVES_SAVE_AS</span> <span class="o">=</span> <span class="s1">''</span>
<span class="n">PAGE_URL</span> <span class="o">=</span> <span class="s2">"</span><span class="si">{slug}</span><span class="s2">.html"</span>
<span class="n">PAGE_SAVE_AS</span> <span class="o">=</span> <span class="s2">"</span><span class="si">{slug}</span><span class="s2">.html"</span>
<span class="c1"># Theme Related</span>
<span class="n">TYPOGRIFY</span> <span class="o">=</span> <span class="kc">False</span> <span class="c1"># turn off for HIDDEN names...</span>
<span class="n">THEME</span> <span class="o">=</span> <span class="s1">'../minchinweb.github.io-pelican/themes/pelican-minchin-ca'</span>
<span class="n">SITELOGO</span> <span class="o">=</span> <span class="s1">'images/MinchindotCA-200.png'</span>
<span class="n">SITELOGO_SIZE</span> <span class="o">=</span> <span class="s1">'100%'</span>
<span class="n">PYGMENTS_STYLE</span> <span class="o">=</span> <span class="s1">'friendly'</span>
<span class="n">DISPLAY_BREADCRUMBS</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">FAVICON</span> <span class="o">=</span> <span class="s1">'favicon.ico'</span>
<span class="n">BOOTSTRAP_THEME</span> <span class="o">=</span> <span class="s1">'minchin-ca'</span>
<span class="n">USE_OPEN_GRAPH</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">CUSTOM_CSS</span> <span class="o">=</span> <span class="s1">'css/minchin-ca.css'</span>
<span class="n">DOCUTIL_CSS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">CUSTOM_JS_LIST</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'js/jquery-ui.min.js'</span><span class="p">,</span>
<span class="s1">'js/globalize.min.js'</span><span class="p">,</span>
<span class="s1">'js/dx.chartjs.js'</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">GOOGLE_ANALYTICS_UNIVERSAL</span> <span class="o">=</span> <span class="s1">'UA-384291-3'</span>
<span class="n">GOOGLE_ANALYTICS_UNIVERSAL_PROPERTY</span> <span class="o">=</span> <span class="s1">'minchin.ca'</span>
<span class="c1"># Plugins</span>
<span class="n">PLUGIN_PATHS</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'../pelican-plugins'</span><span class="p">,)</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'assets'</span><span class="p">,</span> <span class="p">]</span>
<span class="n">ASSET_CSS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">ASSET_JS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">SITEMAP</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"format"</span><span class="p">:</span> <span class="s2">"xml"</span><span class="p">,</span>
<span class="p">}</span>
<span class="c1"># # Make things disappear</span>
<span class="n">DISPLAY_CATEGORIES_ON_MENU</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">HIDE_SITENAME</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">HIDE_SIDEBAR</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">FEED_ALL_ATOM</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">FEED_ALL_RSS</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">GITHUB_USER</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">ADDTHIS_PROFILE</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">DISQUS_SITENAME</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">PDF_PROCESSOR</span> <span class="o">=</span> <span class="kc">False</span>
</code></pre></div>
<h3><code>adamconf.py</code> — Additional Settings File</h3>
<p>This settings file is generated by the build script (<code>gen_upload.py</code>) and then
imported into the Pelican configuration file (<code>pelicanconf.py</code>) to provide
these setting to Pelican.</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Genealogy Uploader, v.3.2.3</span>
<span class="c1"># C:\Users\William\Desktop\William 160108.ged</span>
<span class="n">ADAM</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">ADAM_VERSION</span> <span class="o">=</span> <span class="s2">"Built by Gigatrees (3.0.14)"</span>
<span class="n">ADAM_UPDATED</span> <span class="o">=</span> <span class="s2">"January 8, 2016"</span>
<span class="n">ADAM_COPY_DATE</span> <span class="o">=</span> <span class="s2">"1987-2016"</span>
<span class="n">ADAM_LINK</span> <span class="o">=</span> <span class="s2">"http://gigatrees.com"</span>
<span class="n">ADAM_FOOTER</span> <span class="o">=</span> <span class="s2">"<p><strong>Are we related?</strong> Are you a long lost cousin? Spotted an error here? This website remains a work-in-progress and I would love to hear from you. Drop me a line at minchinweb [at] gmail [dot] com.</p>"</span>
<span class="n">ADAM_PUBLISH</span> <span class="o">=</span> <span class="kc">True</span>
</code></pre></div></body></html>Image Process Plugin 1.1.3 for Pelican Released2017-05-27T16:00:00-06:002017-05-27T16:00:00-06:00Wm. Minchintag:blog.minchin.ca,2017-05-27:/2017/05/image-processing-for-pelican-113-released.html<p><em>Image Process</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>Image Process</em> let you automate the processing of images based on their
class attribute. Use this plugin to minimize the overall page weight and
to save you a trip to Gimp or Photoshop each time you include an image
in your post.</p>
<p><em>Image Process</em> also makes it easy to create responsive images using the
new <span class="caps">HTML5</span> <code>srcset</code> attribute and <code><picture></code> tag. It does this by
generating multiple derivative images from one or more sources.</p>
<html><head></head><body><p><em>Image Process</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>Image Process</em> let you automate the processing of images based on their
class attribute. Use this plugin to minimize the overall page weight and
to save you a trip to Gimp or Photoshop each time you include an image
in your post.</p>
<p><em>Image Process</em> also makes it easy to create responsive images using the
new <span class="caps">HTML5</span> <code>srcset</code> attribute and <code><picture></picture></code> tag. It does this by
generating multiple derivative images from one or more sources.</p>
<p><em>Image Process</em> will not overwrite your original images.</p>
<p><em>Image Process</em> is used by
<a href="https://github.com/MinchinWeb/seafoam">this blog’s theme</a> to resize the source
images so they are the correct size for thumbnails on the main index page and
the larger size they are displayed at on top of the articles.</p>
<h2>This Release</h2>
<p>This post is up because version 1.1.3 of the plugin has been released and
posted <a href="https://pypi.org/project/minchin.pelican.plugins.image-process/">PyPI</a>.
This version was released because version 1.1.2, as posted to PyPI, was broken;
among other issues I couldn’t generated my blog!</p>
<p>To upgrade simply use <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.image_process<span class="w"> </span>--upgrade
</code></pre></div>
<p>The bulk of the <span class="caps">README</span> is included below as I haven’t posted it to the blog
before. This plugin is a rather powerful, but as such is more involved to
configure than many other Pelican plugins.</p>
<p>A special thanks to the team at <a href="https://github.com/whiskyechobravo/image_process">Whisky Echo
Bravo</a> who developed the code
of the plugin.</p>
<h2>Installation</h2>
<p>The easiest way to install <em>Image Process</em> is through the use of pip.
This will also install the required dependencies automatically.</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.image_process
</code></pre></div>
<p>Then, in your <code>pelicanconf.py</code> file, add <code>Image Process</code> to your list of plugins:</p>
<div class="highlight"><pre><span></span><code><span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># ...</span>
<span class="s1">'minchin.pelican.plugins.image_process'</span><span class="p">,</span>
<span class="c1"># ...</span>
<span class="p">]</span>
</code></pre></div>
<p>You will also need to configure your desired transformations (see <em>Usage</em>
below) and add the appropriate class to images you want processed.</p>
<h2>Requirements</h2>
<p><em>Image Process</em> requires Beautiful Soup, Pillow, Six, and Pelican. All these
can be manually installed with pip:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pillow<span class="w"> </span>beautifulsoup4<span class="w"> </span>six<span class="w"> </span>pelican
</code></pre></div>
<p>If you encounter errors while processing <span class="caps">JPEG</span> files (on Linux), you may need to
install the <span class="caps">JPEG</span> development library:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>uninstall<span class="w"> </span>pillow
apt-get<span class="w"> </span>install<span class="w"> </span>libjpeg-dev
pip<span class="w"> </span>install<span class="w"> </span>pillow
</code></pre></div>
<h2>Usage</h2>
<p><em>Image Process</em> scans your content for <code><img/></code> tags with special classes. It
then maps the classes to a set of image processing instructions, computes new
images and modifies <span class="caps">HTML</span> code according to the instructions.</p>
<h3>Define Transformations</h3>
<p>The first step in using this module is to define some image transformations in
your Pelican configuration file. Transformations are defined in the
<code>IMAGE_PROCESS</code> dictionary, mapping a transformation name to a list of
operations. There are three kinds of transformations: image replacement,
responsive image and picture set.</p>
<h4>Image replacement</h4>
<p>The simplest image processing will replace the original image by a new,
transformed image computed from the original. You may use this, for example, to
ensure that all images are of the same size, or to compute a thumbnail from a
larger image:</p>
<div class="highlight"><pre><span></span><code><span class="n">IMAGE_PROCESS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'article-image'</span><span class="p">:</span> <span class="p">[</span><span class="s2">"scale_in 300 300 True"</span><span class="p">],</span>
<span class="s1">'thumb'</span><span class="p">:</span> <span class="p">[</span><span class="s2">"crop 0 0 50</span><span class="si">% 50%</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"scale_out 150 150 True"</span><span class="p">,</span> <span class="s2">"crop 0 0 150 150"</span><span class="p">],</span>
<span class="p">}</span>
</code></pre></div>
<p>These transformations tell <em>Image process</em> to transform the image referred by
the <code>src</code> attribute of an <code><img/></code> according to the list of operations specified
and replace the <code>src</code> attribute by the <span class="caps">URL</span> of the transformed image.</p>
<p>For consistency with the other type of transformations described below, there
is an alternative syntax for the processing instructions:</p>
<div class="highlight"><pre><span></span><code><span class="n">IMAGE_PROCESS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'thumb'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'type'</span><span class="p">:</span> <span class="s1">'image'</span><span class="p">,</span>
<span class="s1">'ops'</span><span class="p">:</span> <span class="p">[</span><span class="s2">"crop 0 0 50</span><span class="si">% 50%</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"scale_out 150 150 True"</span><span class="p">,</span> <span class="s2">"crop 0 0 150 150"</span><span class="p">],</span>
<span class="p">}</span>
<span class="s1">'article-image'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'type'</span><span class="p">:</span> <span class="s1">'image'</span><span class="p">,</span>
<span class="s1">'ops'</span><span class="p">:</span> <span class="p">[</span><span class="s2">"scale_in 300 300 True"</span><span class="p">],</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>To apply image replacement to the images in your articles, you must add them
the special class <code>image-process-</code> followed by the name of the transformation
you wish to apply. For example, let's pretend you have defined the
transformation described above. If you write your content in <span class="caps">HTML</span> or in
Markdown, do something like this:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">img</span> <span class="na">class</span><span class="o">=</span><span class="s">"image-process-article-image"</span> <span class="na">src</span><span class="o">=</span><span class="s">"/images/pelican.jpg"</span><span class="p">/></span>
</code></pre></div>
<p>In reStructuredText, use the <code>:class:</code> attribute of the <code>image</code> or the
<code>figure</code> directive:</p>
<div class="highlight"><pre><span></span><code><span class="p">..</span> <span class="ow">image</span><span class="p">::</span> /images/pelican.png
<span class="nc">:class:</span> image-process-article-image
<span class="p">..</span> <span class="ow">figure</span><span class="p">::</span> /images/pelican.png
<span class="nc">:class:</span> image-process-article-image
</code></pre></div>
<div class="admonition note">
The reStructuredText reader will convert underscores (`_`) to dashes (`-`) in
class names. To make sure everything runs smoothly, do not use underscores in
your transformation names.
</div>
<h4>Responsive Images</h4>
<p>You can use <em>Image process</em> to automatically generate a set of images that will
be selected for display by browsers according to the viewport width or
according to the device resolution. To accomplish this, <em>Image process</em> will
add a <code>srcset</code> attribute (and maybe a <code>media</code> and a <code>sizes</code> attribute) to the
<code><img/></code> tag.</p>
<p>Note that the <code>srcset</code> syntax is currently not supported by all browsers.
However, browsers who do not support the <code>srcset</code> attribute will fall back to a
default image specified by the still-present <code>src</code> attribute. See <a href="http://caniuse.com/#feat=srcset">Can I
Use</a> for the current status on <code>srcset</code>
support.</p>
<p><span class="caps">HTML5</span> supports two types of responsive image set. The first one is
device-pixel-ratio-based, selecting higher resolution images for higher
resolution devices; the second one is viewport-based, selecting images
according to the viewport width. If you want to know more about <span class="caps">HTML5</span>
responsive images, I recommend <a href="http://www.smashingmagazine.com/2014/05/14/responsive-images-done-right-guide-picture-srcset/">this
article</a>
for a gentle introduction to the <code>srcset</code> and <code><picture></picture></code> syntaxes.</p>
<p>To tell <em>Image process</em> to generate a responsive image, add a
<code>responsive-image</code> transformation to your your <code>IMAGE_PROCESS</code> dictionary, with
the following syntax:</p>
<div class="highlight"><pre><span></span><code><span class="n">IMAGE_PROCESS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'crisp'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'type'</span><span class="p">:</span> <span class="s1">'responsive-image'</span><span class="p">,</span>
<span class="s1">'srcset'</span><span class="p">:</span> <span class="p">[(</span><span class="s1">'1x'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"scale_in 800 600 True"</span><span class="p">]),</span>
<span class="p">(</span><span class="s1">'2x'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"scale_in 1600 1200 True"</span><span class="p">]),</span>
<span class="p">(</span><span class="s1">'4x'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"scale_in 3200 2400 True"</span><span class="p">]),</span>
<span class="p">],</span>
<span class="s1">'default'</span><span class="p">:</span> <span class="s1">'1x'</span><span class="p">,</span>
<span class="p">},</span>
<span class="s1">'large-photo'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'type'</span><span class="p">:</span> <span class="s1">'responsive-image'</span><span class="p">,</span>
<span class="s1">'sizes'</span><span class="p">:</span> <span class="s1">'(min-width: 1200px) 800px, (min-width: 992px) 650px, </span><span class="se">\</span>
<span class="s1"> (min-width: 768px) 718px, 100vw'</span><span class="p">,</span>
<span class="s1">'srcset'</span><span class="p">:</span> <span class="p">[(</span><span class="s1">'600w'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"scale_in 600 450 True"</span><span class="p">]),</span>
<span class="p">(</span><span class="s1">'800w'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"scale_in 800 600 True"</span><span class="p">]),</span>
<span class="p">(</span><span class="s1">'1600w'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"scale_in 1600 1200 True"</span><span class="p">]),</span>
<span class="p">],</span>
<span class="s1">'default'</span><span class="p">:</span> <span class="s1">'800w'</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
</code></pre></div>
<p>The <code>crisp</code> transformation is an example of a transformation enabling
device-pixel-ratio-based selection. The <code>srcset</code> is a list of tuple, each tuple
containing the image description (<code>'1x'</code>, <code>'2x'</code>, etc.) and the list of
operations to generate the derivative image from the original image (the
original image is the value of the <code>src</code> attribute of the <code><img/></code>). Image
descriptions are hints about the resolution of the associated image and must
have the suffix <code>x</code>. The <code>default</code> names the image to use to replace the <code>src</code>
attribute of the image. This is the image that will be displayed by browsers
that do not support the <code>srcset</code> syntax.</p>
<p>The <code>large-photo</code> transformation is an example of a transformation enabling
viewport-based selection. The <code>sizes</code> contains a rule to compute the width of
the displayed image from the width of the viewport. Once the browser knows the
image width, it will select an image source from the <code>srcset</code>. The <code>srcset</code> is
a list of tuple, each tuple containing the image description (<code>'600w'</code>,
<code>'800w'</code>, etc.) and the list of operations to generate the derivative image
from the original image (the original image is the value of the <code>src</code> attribute
of the <code><img/></code>). Image descriptions are hints about the width in pixels of the
associated image and must have the suffix <code>w</code>. The <code>default</code> names the image to
use to replace the <code>src</code> attribute of the image. This is the image that will be
displayed by browsers that do not support the <code>srcset</code> syntax.</p>
<p>In the two examples above, the <code>default</code> is a string referring to one of the
images in the <code>srcset</code>. However, the <code>default</code> value could also be a list of
operations to generate a different derivative image.</p>
<p>To make the images in your article responsive, you must add them the special
class <code>image-process-</code> followed by the name of the transformation you wish to
apply, exactly like you would do for the image replacement case, described
above. So, if you write your content in <span class="caps">HTML</span> or in Markdown, do something like this:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">img</span> <span class="na">class</span><span class="o">=</span><span class="s">"image-process-large-photo"</span> <span class="na">src</span><span class="o">=</span><span class="s">"/images/pelican.jpg"</span> <span class="p">/></span>
</code></pre></div>
<p>In reStructuredText, use the <code>:class:</code> attribute of the <code>image</code> of the
<code>figure</code> directive:</p>
<div class="highlight"><pre><span></span><code><span class="p">..</span> <span class="ow">image</span><span class="p">::</span> /images/pelican.png
<span class="nc">:class:</span> image-process-large-photo
<span class="p">..</span> <span class="ow">figure</span><span class="p">::</span> /images/pelican.png
<span class="nc">:class:</span> image-process-large-photo
</code></pre></div>
<h4>Picture Sets</h4>
<p><em>Image process</em> can be use to generate the images used by a <code><picture></picture></code> tag.
The <code><picture></picture></code> syntax allows for more flexibility in providing a choice of
image to the browser. Again, if you want to know more about <span class="caps">HTML5</span> responsive
images, see <a href="http://www.smashingmagazine.com/2014/05/14/responsive-images-done-right-guide-picture-srcset/">this
article</a>
for a gentle introduction to the <code>srcset</code> and <code><picture></picture></code> syntaxes.</p>
<p>To tell <em>Image process</em> to generate the images for a <code><picture></picture></code>, add a
<code>picture</code> entry to your <code>IMAGE_PROCESS</code> dictionary with the following syntax:</p>
<div class="highlight"><pre><span></span><code><span class="n">IMAGE_PROCESS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'example-pict'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'type'</span><span class="p">:</span> <span class="s1">'picture'</span><span class="p">,</span>
<span class="s1">'sources'</span><span class="p">:</span> <span class="p">[{</span><span class="s1">'name'</span><span class="p">:</span> <span class="s1">'default'</span><span class="p">,</span>
<span class="s1">'media'</span><span class="p">:</span> <span class="s1">'(min-width: 640px)'</span><span class="p">,</span>
<span class="s1">'srcset'</span><span class="p">:</span> <span class="p">[(</span><span class="s1">'640w'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"scale_in 640 480 True"</span><span class="p">]),</span>
<span class="p">(</span><span class="s1">'1024w'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"scale_in 1024 683 True"</span><span class="p">]),</span>
<span class="p">(</span><span class="s1">'1600w'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"scale_in 1600 1200 True"</span><span class="p">]),</span>
<span class="p">],</span>
<span class="s1">'sizes'</span><span class="p">:</span> <span class="s1">'100vw'</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">{</span><span class="s1">'name'</span><span class="p">:</span> <span class="s1">'source-1'</span><span class="p">,</span>
<span class="s1">'srcset'</span><span class="p">:</span> <span class="p">[(</span><span class="s1">'1x'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"crop 100 100 200 200"</span><span class="p">]),</span>
<span class="p">(</span><span class="s1">'2x'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"crop 100 100 300 300"</span><span class="p">]),</span>
<span class="p">]</span>
<span class="p">}</span>
<span class="p">],</span>
<span class="s1">'default'</span><span class="p">:</span> <span class="p">(</span><span class="s1">'default'</span><span class="p">,</span> <span class="s1">'640w'</span><span class="p">),</span>
<span class="p">},</span>
<span class="p">}</span>
</code></pre></div>
<p>Each of the <code>sources</code> entry is very similar to the <code>responsive image</code> describe
above. Here, each source must have a <code>name</code>, which will be used to find the <span class="caps">URL</span>
of the original image for this source in your article. The source may also have
a <code>media</code>, which contains a rule used by the browser to select the active
source. The <code>default</code> names the image to use to replace the <code>src</code> attribute of
the <code><img/></code> inside the <code><picture></picture></code>. This is the image that will be displayed by
browsers that do not support the <code><picture></picture></code> syntax. In this example, it will
use the image <code>640w</code> from the source <code>default</code>. A list of operations could have
been specified instead of <code>640w</code>.</p>
<p>To generate a responsive <code><picture></picture></code> for the images in your articles, you must
add to your article a pseudo <code><picture></picture></code> tag that looks like this:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">picture</span><span class="p">></span>
<span class="p"><</span><span class="nt">source</span> <span class="na">class</span><span class="o">=</span><span class="s">"source-1"</span> <span class="na">src</span><span class="o">=</span><span class="s">"/images/pelican-closeup.jpg"</span><span class="p">><!--</span--><span class="nt">source</span><span class="p">></span>
<span class="p"><</span><span class="nt">img</span> <span class="na">class</span><span class="o">=</span><span class="s">"image-process-example-pict"</span> <span class="na">src</span><span class="o">=</span><span class="s">"/images/pelican.jpg"</span> <span class="p">/></span>
<span class="p"><!--</span--><span class="nt">picture</span><span class="p">></span>
</span></span></code></pre></div>
<p>Each <code><source/></code> tag maps a source name (the <code>class</code> attribute) to a file (the
<code>src</code> attribute). The <code><img/></code> must have the special class <code>image-process-</code>
followed by the name of the transformation you wish to apply. The file
referenced by the <code>src</code> attribute of the <code><img/>></code> will be used as the special
<code>default</code> source in your transformation definition.</p>
<p>The pseudo <code><picture></picture></code> tag above can be used in articles written in <span class="caps">HTML</span>,
Markdown or restructuredText. In reStructuredText, however, you can also use
the <code>figure</code> directive to generate a <code><picture></picture></code>. The figure image file will be
used as the special <code>default</code> source; other sources must be added in the the
legend section of the <code>figure</code> as <code>image</code> directives. The figure class must be
<code>image-process-</code> followed by the name of the transformation you wish to apply,
while the other images must have two classes: <code>image-process</code> and the name of
the source they provide an image for:</p>
<div class="highlight"><pre><span></span><code><span class="p">..</span> <span class="ow">figure</span><span class="p">::</span> /images/pelican.png
<span class="nc">:class:</span> image-process-large-photo
Test picture
<span class="p"> ..</span> <span class="ow">image</span><span class="p">::</span> /images/pelican-closeup.jpg
<span class="nc">:class:</span> image-process source-1
</code></pre></div>
<p>The images in the legend section that are used as source for the <code><picture></picture></code>
will be removed from the image legend, so that they do not appear in your final article.</p>
<h3>Transformations</h3>
<p>Available operations for transformations are:</p>
<dl>
<dt>crop <em>top</em> <em>left</em> <em>right</em> <em>bottom</em></dt>
<dd>
<p>Crop the image to the box (<em>left</em>, <em>top</em>)-(<em>right</em>, <em>bottom</em>). Values can
be absolute (a number) or relative to the size of the image (a number
followed by a percent sign <code>%</code>).</p>
</dd>
<dt>flip_horizontal</dt>
<dd>
<p>Flip the image horizontally.</p>
</dd>
<dt>flip_vertical</dt>
<dd>
<p>Flip the image vertically.</p>
</dd>
<dt>grayscale</dt>
<dd>
<p>Convert the image to grayscale.</p>
</dd>
<dt>resize <em>width</em> <em>height</em></dt>
<dd>
<p>Resize the image. This operation does <em>not</em> preserve the image aspect
ratio. Values can be absolute (a number) or relative to the size of the
image (a number followed by a percent sign <code>%</code>).</p>
</dd>
<dt>rotate degree</dt>
<dd>
<p>Rotate the image.</p>
</dd>
<dt>scale_in <em>width</em> <em>height</em> <em>upscale</em></dt>
<dd>
<p>Resize the image. This operation preserves the image aspect ratio and the
resulting image will be no larger than <em>width</em> x <em>height</em>. Values can be
absolute (a number) or relative to the size of the image (a number followed
by a percent sign <code>%</code>). If <em>upscale</em> is False, smaller images will not be enlarged.</p>
</dd>
<dt>scale_out <em>width</em> <em>height</em> <em>upscale</em></dt>
<dd>
<p>Resize the image. This operation preserves the image aspect ratio and the
resulting image will be no smaller than <em>width</em> x <em>height</em>. Values can be
absolute (a number) or relative to the size of the image (a number followed
by a percent sign <code>%</code>). If <em>upscale</em> is False, smaller images will not be enlarged.</p>
</dd>
<dt>blur</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.BLUR</code> filter to the image.</p>
</dd>
<dt>contour</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.CONTOUR</code> filter to the image.</p>
</dd>
<dt>detail</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.DETAIL</code> filter to the image.</p>
</dd>
<dt>edge_enhance</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.EDGE_ENHANCE</code> filter to the image.</p>
</dd>
<dt>edge_enhance_more</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.EDGE_ENHANCE_MORE</code> filter to the image.</p>
</dd>
<dt>emboss</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.EMBOSS</code> filter to the image.</p>
</dd>
<dt>find_edges</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.FIND_EDGES</code> filter to the image.</p>
</dd>
<dt>smooth</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.SMOOTH filter</code> to the image.</p>
</dd>
<dt>smooth_more</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.SMOOTH_MORE</code> filter to the image.</p>
</dd>
<dt>sharpen</dt>
<dd>
<p>Apply the <code>pillow.ImageFilter.SHARPEN</code> filter to the image.</p>
</dd>
</dl>
<p>You can also define your own operations; the only requirement is that your
operation should be a callable object expecting a <code>pillow.Image</code> as its first
parameter and it should return the transformed image:</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">crop_face</span><span class="p">(</span><span class="n">image</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""Detect face in image and crop around it."""</span>
<span class="c1"># TODO: Fancy algorithm.</span>
<span class="k">return</span> <span class="n">image</span>
<span class="n">IMAGE_PROCESS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'face-thumbnail'</span><span class="p">:</span> <span class="p">[</span><span class="n">crop_face</span><span class="p">,</span> <span class="s2">"scale_out 150 150 True"</span><span class="p">]</span>
<span class="p">}</span>
</code></pre></div>
<h3>Additional Settings</h3>
<h4>Destination Directory</h4>
<p>By default, the new images will be stored in a directory named
<code>derivative/<transformation_name></transformation_name></code> in the output folder at the same location as
the original image. For example if the original image is located in the
<code>content/images</code> folder. The computed images will be stored in the
<code>output/images/derivative/<transformation_name></transformation_name></code>. All the transformations are
done in the output directory in order to avoid confusion with the source files
or if we test multiple transformations. You can replace <code>derivative</code> by
something else using the <code>IMAGE_PROCESS_DIR</code> setting in your Pelican
configuration file:</p>
<div class="highlight"><pre><span></span><code><span class="n">IMAGE_PROCESS_DIR</span> <span class="o">=</span> <span class="s1">'derivees'</span>
</code></pre></div>
<h4>Force Image Processing</h4>
<p>If the transformed image already exists and is newer than the original image,
the plugin assumes that it should not recompute it again. You can force the
plugin to recompute all images by setting <code>IMAGE_PROCESS_FORCE</code> to <code>True</code> in
your Pelican configuration file.</p>
<div class="highlight"><pre><span></span><code><span class="n">IMAGE_PROCESS_FORCE</span> <span class="o">=</span> <span class="kc">True</span>
</code></pre></div>
<h4>Selecting a <span class="caps">HTML</span> Parser</h4>
<p>You may select the <span class="caps">HTML</span> parser which is used. The default is the builtin
<code>html.parser</code> but you may also select <code>html5lib</code> or <code>lxml</code> by setting
<code>IMAGE_PROCESS_PARSER</code> in your pelican configuration file , e.g.:</p>
<div class="highlight"><pre><span></span><code><span class="n">IMAGE_PROCESS_PARSER</span> <span class="o">=</span> <span class="s2">"html5lib"</span>
</code></pre></div>
<p>For details, refer to the <a href="https://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-a-parser">BeautifulSoup documentation on
parsers</a>.</p>
<h4>File Encoding</h4>
<p>You may select a different file encoding to be used by BeautifulSoup as it
opens your files. The default is <code>uft-8</code>.</p>
<div class="highlight"><pre><span></span><code><span class="n">IMAGE_PROCESS_ENCODING</span> <span class="o">=</span> <span class="s2">"uft-8"</span>
</code></pre></div>
<h2>Known Issues</h2>
<ul>
<li>Pillow, when resizing animated <span class="caps">GIF</span> files, does not return an animated file</li>
<li>the <code>setup.py</code> file for this project does not run on Python 2.7. However,
wheels of this project are "universal" and so can be generated by Python
3 and subsequently installed by Python 2.7.</li>
<li>test require access to the <code>pelican.tests</code> module, which isn't included in
the pelican distribution on PyPI.</li>
<li>version 1.1.2, as uploaded to PyPI, is broken; use a different version.
(see <a href="https://github.com/MinchinWeb/minchin.pelican.plugins.image_process/issues/2">issue
#2</a>
for details)</li>
</ul>
<h2>Credits</h2>
<p>Pelican image in test data by Jon Sullivan. Source:
<a href="http://www.pdphoto.org/PictureDetail.php?mat=&pg=5726">http://www.pdphoto.org/PictureDetail.php?mat=&pg=5726</a></p>
<p>Original Plugin developed by the team at <a href="https://github.com/whiskyechobravo/image_process">Whisky Echo
Bravo</a>.</p></body></html>Pelican Trove Classifiers Added to PyPI2017-04-18T10:04:00-06:002017-04-18T10:04:00-06:00Wm. Minchintag:blog.minchin.ca,2017-04-18:/2017/04/pelican-trove-classifiers.html<a href="https://pypi.python.org/pypi">PyPI</a> is the “package index” for Python and a
wonderful tool for finding and sharing Python libraries.<html><head></head><body><p><a href="https://pypi.python.org/pypi">PyPI</a> is the “package index” for Python and a
wonderful tool for finding and sharing Python libraries.</p>
<p>On the index, there is a system of <em>trove classifiers</em> (see the <a href="https://pypi.python.org/pypi?%3Aaction=list_classifiers">full list
here</a>) that serve to
allow package authors to tag their projects to help user find them. Some groups
classifiers cover the Python version targeted, the “development status” (alpha,
beta, stable, etc.), the license, the framework used, as so on.</p>
<p>Recently, trove classifiers for Pelican were added! This is great because it
will allow for better discover-ability of Pelican themes and plugins. So I’ve
re-released my collection of Pelican plugins and themes with the new
classifiers, so the following releases:</p>
<ul>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.jinja_filters">minchin.pelican.jinja_filters</a> v1.0.4</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.cname">minchin.pelican.plugins.cname</a> v1.0.4</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.image_process">minchin.pelican.plugins.image_process</a> v1.1.2 (this release is broken, <a href="https://blog.minchin.ca/2017/05/image-processing-for-pelican-113-released.html">version 1.1.3 has been released</a>)</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.nojekyll">minchin.pelican.plugins.nojekyll</a> v1.0.2</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.optimize_images">minchin.pelican.plugins.optimize_images</a> v1.1.1</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.post_stats">minchin.pelican.plugins.post_stats</a> v1.1.1</li>
<li><a href="https://github.com/MinchinWeb/minchin.pelican.plugins.summary">minchin.pelican.plugins.summary</a> v1.1.1</li>
<li><a href="https://github.com/MinchinWeb/seafoam">seafoam</a> v2.1.4</li>
</ul>
<p>PyPI also has the ability to show you <a href="https://pypi.python.org/pypi?:action=browse&c=635">everything tagged with
Pelican</a>.</p></body></html>Post Stats Plugin 1.1.0 for Pelican Released2017-02-26T20:52:00-07:002017-02-26T20:52:00-07:00Wm. Minchintag:blog.minchin.ca,2017-02-26:/2017/02/post-stats-for-pelican-110-released.html<p><em>Post Stats</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>Post Stats</em> calculates various statistics about a post and store them in
an article.stats dictionary:</p>
<html><head></head><body><p><em>Post Stats</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>,
a static site generator written in Python.</p>
<p><em>Post Stats</em> calculates various statistics about a post and store them in
an article.stats dictionary:</p>
<ul>
<li><code>wc</code>: how many words (i.e. word count)</li>
<li><code>read_mins</code>: how many minutes would it take to read this article, based
on <a href="http://en.wikipedia.org/wiki/Words_per_minute#Reading_and_comprehension">250 wpm</a></li>
<li><code>word_counts</code>: frequency count of all the words in the article; can be
used for tag/word clouds</li>
<li><code>fi</code>: Flesch-kincaid Index/ Reading Ease
(<a href="http://en.wikipedia.org/wiki/Flesch%E2%80%93Kincaid_readability_tests>">more info</a>)</li>
<li><code>fk</code>: Flesch-kincaid Grade Level</li>
</ul>
<h2>Installation</h2>
<p>The easiest way to install <em>Post Stats</em> is through the use of pip. This
will also install the required dependencies automatically.</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.post_stats
</code></pre></div>
<p>Then, in your <code>pelicanconf.py</code> file, add <code>Post Stats</code> to your list of plugins:</p>
<div class="highlight"><pre><span></span><code><span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># ...</span>
<span class="s1">'minchin.pelican.plugins.post_stats'</span><span class="p">,</span>
<span class="c1"># ...</span>
<span class="p">]</span>
</code></pre></div>
<p>You may also need to configure your template to make use of the statistics generated.</p>
<h2>Requirements</h2>
<p><em>Post Stats</em> depends on (and is really only useful with) Pelican. The plugin
also requires Beautiful Soup 4 to process your content. If the plugin is
installed from pip, these will automatically be installed. These can also be
manually installed with pip:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pelican
pip<span class="w"> </span>install<span class="w"> </span>beautifulsoup4
</code></pre></div>
<h2>Configuration and Usage</h2>
<p>This plugin calculates various statistics about a post and store them in an
article.stats dictionary.</p>
<p>Example:</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="s1">'wc'</span><span class="p">:</span> <span class="mi">2760</span><span class="p">,</span>
<span class="s1">'fi'</span><span class="p">:</span> <span class="s1">'65.94'</span><span class="p">,</span>
<span class="s1">'fk'</span><span class="p">:</span> <span class="s1">'7.65'</span><span class="p">,</span>
<span class="s1">'word_counts'</span><span class="p">:</span> <span class="n">Counter</span><span class="p">({</span><span class="sa">u</span><span class="s1">'to'</span><span class="p">:</span> <span class="mi">98</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'a'</span><span class="p">:</span> <span class="mi">90</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'the'</span><span class="p">:</span> <span class="mi">83</span><span class="p">,</span> <span class="o">...</span><span class="p">}),</span>
<span class="s1">'read_mins'</span><span class="p">:</span> <span class="mi">12</span>
<span class="p">}</span>
</code></pre></div>
<p>This allows you to output these values in your templates, like this, for example:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">p</span> <span class="na">title</span><span class="o">=</span><span class="s">"~</span><span class="cp">{{</span> <span class="nv">article.stats</span><span class="o">[</span><span class="s1">'wc'</span><span class="o">]</span> <span class="cp">}}</span><span class="s"> words"</span><span class="p">></span>~<span class="cp">{{</span> <span class="nv">article.stats</span><span class="o">[</span><span class="s1">'read_mins'</span><span class="o">]</span> <span class="cp">}}</span> min read<span class="p"><!--</span--><span class="nt">p</span><span class="p">></span>
<span class="p"><</span><span class="nt">ul</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span><span class="p">></span>Flesch-kincaid Index/ Reading Ease: <span class="cp">{{</span> <span class="nv">article.stats</span><span class="o">[</span><span class="s1">'fi'</span><span class="o">]</span> <span class="cp">}}</span><span class="p"><!--</span--><span class="nt">li</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span><span class="p">></span>Flesch-kincaid Grade Level: <span class="cp">{{</span> <span class="nv">article.stats</span><span class="o">[</span><span class="s1">'fk'</span><span class="o">]</span> <span class="cp">}}</span><span class="p"><!--</span--><span class="nt">li</span><span class="p">></span>
<span class="p"><!--</span--><span class="nt">ul</span><span class="p">></span>
</span></span></span></span></code></pre></div>
<p>The <code>word_counts</code> variable is a Python <code>Counter</code> dictionary and looks
something like this, with each unique word and it’s frequency:</p>
<div class="highlight"><pre><span></span><code><span class="n">Counter</span><span class="p">({</span><span class="sa">u</span><span class="s1">'to'</span><span class="p">:</span> <span class="mi">98</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'a'</span><span class="p">:</span> <span class="mi">90</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'the'</span><span class="p">:</span> <span class="mi">83</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'of'</span><span class="p">:</span> <span class="mi">50</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'karma'</span><span class="p">:</span> <span class="mi">50</span><span class="p">,</span> <span class="o">.....</span>
</code></pre></div>
<p>and can be used to create a tag/word cloud for a post.</p>
<p>There are no user-configurable settings.</p>
<h2>Known Issues</h2>
<p>An issue, as such, is that there is no formal test suite. Testing is currently
limited to my in-use observations. I also run a basic check upon uploaded the
package to PyPI that it can be downloaded and loaded into Python.</p>
<p>The package is tested in Python 3.6; compatibility with other version of Python
is unknown, but there should be nothing particular keeping it from working with
other “modern” versions of Python.</p>
<h2>Credits</h2>
<p><a href="http://duncanlock.net/blog/2013/06/23/post-statistics-plugin-for-pelican/">Original plugin</a>
by Duncan Lock (<a href="https://github.com/dflock">@dflock</a>) and
posted to the
<a href="https://github.com/getpelican/pelican-plugins">Pelican-Plugins repo</a>.</p>
<h2>License</h2>
<p>The plugin code is assumed to be under the AGPLv3 license (this is the
license of the Pelican-Plugins repo).</p></body></html>Optimize Images Plugin 1.1.0 for Pelican Released2017-02-26T20:00:00-07:002017-02-26T20:00:00-07:00Wm. Minchintag:blog.minchin.ca,2017-02-26:/2017/02/optimize-images-for-pelican-110-released.html<p><em>Optimize Images</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a
static site generator written in Python.</p>
<p><em>Optimize Images</em> applies lossless compression on <span class="caps">JPEG</span> and <span class="caps">PNG</span> images, with no
effect on image quality. It uses <a href="http://jpegclub.org/jpegtran/">jpegtran</a> and
<a href="http://optipng.sourceforge.net/">OptiPNG</a>.</p>
<html><head></head><body><p><em>Optimize Images</em> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a
static site generator written in Python.</p>
<p><em>Optimize Images</em> applies lossless compression on <span class="caps">JPEG</span> and <span class="caps">PNG</span> images, with no
effect on image quality. It uses <a href="http://jpegclub.org/jpegtran/">jpegtran</a> and
<a href="http://optipng.sourceforge.net/">OptiPNG</a>.</p>
<h2>Installation</h2>
<p>The easiest way to install <em>Optimize Images</em> is through the use of pip. This
will also install the required Python dependencies automatically (currently
none beyond Pelican itself).</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.optimize_images
</code></pre></div>
<p>It is assumed both jpegtran and OptiPNG are installed and available on the
system path.</p>
<p>Then, in your <code>pelicanconf.py</code> file, add <code>Optimize Images</code> to your list of plugins:</p>
<div class="highlight"><pre><span></span><code><span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># ...</span>
<span class="s1">'minchin.pelican.plugins.optimize_images'</span><span class="p">,</span>
<span class="c1"># ...</span>
<span class="p">]</span>
</code></pre></div>
<h2>Requirements</h2>
<p><em>Optimize Images</em> depends on (and is really only useful with) Pelican. This can
be manually installed with pip:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pelican
</code></pre></div>
<p>It is assumed both jpegtran and OptiPNG are installed on system path. On
Windows, installers are available at each respective website. On Ubuntu systems
(including Travis-<span class="caps">CI</span>), the two can be installed via <em>apt-get</em>.</p>
<div class="highlight"><pre><span></span><code>apt-get<span class="w"> </span>install<span class="w"> </span>optipng<span class="w"> </span>libjpeg-progs
</code></pre></div>
<h2>Configuration and Usage</h2>
<p>The plugin will activate and optimize images upon <em>finalized</em> signal of Pelican.</p>
<p>The plugin has no user settings.</p>
<h2>Known Issues</h2>
<p>Image manipulation like this can take some time to run. You may consider only
adding this plugin to your <code>publishconf.py</code> (rather than your base
<code>pelicanconf.py</code>), which will then only run this image optimization in
preparation for site publication.</p>
<p>An issue, as such, is that there is no formal test suite. Testing is currently
limited to my in-use observations. I also run a basic check upon uploaded the
package to PyPI that it can be downloaded and loaded into Python.</p>
<p>The package is tested in Python 3.6; compatibility with other version of Python
is unknown, but there should be nothing particular keeping it from working with
other “modern” versions of Python.</p>
<h2>Credits</h2>
<p>Original plugin from the <a href="https://github.com/getpelican/pelican-plugins">Pelican-Plugins
repo</a>.</p>
<h2>License</h2>
<p>The plugin code is assumed to be under the AGPLv3 license (this is the
license of the Pelican-Plugins repo).</p></body></html>Summary Plugin 1.1.0 for Pelican Released2017-02-05T15:29:00-07:002017-02-21T14:51:00-07:00Wm. Minchintag:blog.minchin.ca,2017-02-05:/2017/02/summary-for-pelican-110-released.html<p><strong>Summary</strong> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a static
site generator written in Python.</p>
<p><strong>Summary</strong> allows easy, variable length summaries directly embedded into the
body of your articles.</p>
<html><head></head><body><p><strong>Summary</strong> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a static
site generator written in Python.</p>
<p><strong>Summary</strong> allows easy, variable length summaries directly embedded into the
body of your articles.</p>
<h2>Installation</h2>
<p>The easiest way to install <strong>Summary</strong> is through the use of pip. This will
also install the required dependencies automatically (currently none beyond Pelican).</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.summary
</code></pre></div>
<p>Then, in your <code>pelicanconf.py</code> file, add <strong>Summary</strong> to your list of plugins:</p>
<div class="highlight"><pre><span></span><code><span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># ...</span>
<span class="s1">'minchin.pelican.plugins.summary'</span><span class="p">,</span>
<span class="c1"># ...</span>
<span class="p">]</span>
</code></pre></div>
<p>You may also need to configure the summary start and end markers (see
Configuration below).</p>
<h2>Configuration and Usage</h2>
<p>This plugin introduces two new settings: <code>SUMMARY_BEGIN_MARKER</code> and
<code>SUMMARY_END_MARKER</code>: strings which can be placed directly into an article to
mark the beginning and end of a summary. When found, the standard
<code>SUMMARY_MAX_LENGTH</code> setting will be ignored. The markers themselves will also
be removed from your articles before they are published. The default values are
<code><!-- PELICAN_BEGIN_SUMMARY --></code> and <code><!-- PELICAN_END_SUMMARY --></code>.</p>
<p>If no beginning or end marker is found, and if <code>SUMMARY_USE_FIRST_PARAGRAPH</code> is
enabled in the settings, the summary will be the first paragraph of the post.</p>
<p>The plugin also sets a <code>has_summary</code> attribute on every article. It is True for
articles with an explicitly-defined summary, and False otherwise. (It is also
False for an article truncated by <code>SUMMARY_MAX_LENGTH</code>.) Your templates can use
this e.g. to add a link to the full text at the end of the summary.</p>
<h2>Known Issues</h2>
<p><del>An issue, as such, is that there is no formal test suite. Testing is
currently limited to my in-use observations. I also run a basic check upon
uploaded the package to PyPI that it can be downloaded and loaded into
Python.</del></p>
<p><del>The package is tested in Python 3.5; compatibility with other version of
Python is unknown.</del></p>
<p>Tests are actually included and can be run from the root directory:</p>
<div class="highlight"><pre><span></span><code>python<span class="w"> </span>minchin/pelican/plugins/summary/test_summary.py
</code></pre></div>
<h2>Changes</h2>
<p>This version is basically just repackaging the plugin and making it available
on pip.</p>
<h2>Code</h2>
<p>The code for this project is available on <a href="https://github.com/MinchinWeb/minchin.pelican.plugins.summary">GitHub</a>. Contributions are welcome!</p>
<h2>Credits</h2>
<p>Original plugin from the <a href="https://github.com/getpelican/pelican-plugins">Pelican-Plugins repo</a>.</p>
<h2>License</h2>
<p>The plugin code is assumed to be under the AGPLv3 license (this is the license
of the Pelican-Plugins repo).</p></body></html>Seafoam 2.0.4 Pelican Theme Released2017-01-11T13:33:00-07:002017-01-11T13:33:00-07:00Wm. Minchintag:blog.minchin.ca,2017-01-11:/2017/01/seafoam-2-released.html<p>Version 2.0.4 of <em>Seafoam</em> has been released.</p>
<p>Seafoam is the Pelican theme I use for this site, but published to PyPI in the
hope that it will be useful to others.</p>
<p>This release changes the name of the project to <em>seafoam</em> from the unwieldy
<em><a href="{filename}20160912-minchin-dot-ca-pelican-theme-version-110-released.md">minchin.pelican.themes.minchindotca</a></em>.
It also add support for the <a href="https://bernhard.scheirle.de/posts/2014/March/29/static-comments-via-email/">Pelican Comment
System</a>,
removes the need to define Jinja2 filters in your configuration file, updates
to Font Awesome v4.7, and several design fixes as well. Code is updated to
work with Pelican v3.7 and Jinja v2.9.</p>
<html><head></head><body><p>Version 2.0.4 of <em>Seafoam</em> has been released.</p>
<p>Seafoam is the Pelican theme I use for this site, but published to PyPI in the
hope that it will be useful to others.</p>
<p>This release changes the name of the project to <em>seafoam</em> from the unwieldy
<em><a href="https://blog.minchin.ca/2016/09/minchin-dot-ca-pelican-theme-version-110-released.html">minchin.pelican.themes.minchindotca</a></em>.
It also add support for the <a href="https://bernhard.scheirle.de/posts/2014/March/29/static-comments-via-email/">Pelican Comment
System</a>,
removes the need to define Jinja2 filters in your configuration file, updates
to Font Awesome v4.7, and several design fixes as well. Code is updated to
work with Pelican v3.7 and Jinja v2.9.</p>
<h2>Quickstart</h2>
<p>To use this theme, first install it via <code>pip</code>: (as a side, this is the only
Pelican theme I know of that is installable from PyPI.)</p>
<div class="highlight"><pre><span></span><code>pip install seafoam
</code></pre></div>
<p>Next, update your <code>pelicanconf.py</code> to use the theme, with its default settings:</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">seafoam</span>
<span class="n">THEME</span> <span class="o">=</span> <span class="n">seafoam</span><span class="o">.</span><span class="n">get_path</span><span class="p">()</span>
<span class="n">BOOTSTRAP_THEME</span> <span class="o">=</span> <span class="s1">'seafoam'</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'minchin.pelican.jinja_filters'</span><span class="p">,</span>
<span class="s1">'minchin.pelican.plugins.image_process'</span><span class="p">,</span>
<span class="c1"># others, as desired...</span>
<span class="p">]</span>
<span class="n">IMAGE_PROCESS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'article-feature'</span><span class="p">:</span> <span class="p">[</span><span class="s2">"scale_in 848 848 True"</span><span class="p">],</span>
<span class="s1">'index-feature'</span><span class="p">:</span> <span class="p">[</span><span class="s2">"scale_in 263 263 True"</span><span class="p">],</span>
<span class="p">}</span>
</code></pre></div>
<p>And then regenerate your site.</p>
<p>The theme depends on the pathlib library, that was introduced in Python 3.4, so
the theme won’t work on earlier versions of Python. Installing the pathlib2
library should provide the necessary functionality for the theme to work, but
this is currently untested by me.</p>
<h2>Settings</h2>
<p>The full settings are not particularly well documented (by me anyway), although
the the
<a href="https://github.com/getpelican/pelican-themes/tree/master/pelican-bootstrap3">ReadMe</a>
for the base theme goes over most of the settings.</p>
<h2 id="changelog">Changelog</h2>
<p>I’ve built a release script that automates pushing releases to PyPI, and so it
makes it very easy to make a release. In fact, writing these release blog posts
is probably the most involved part of cutting a release! Therefore, this blog
post is to serve for five releases: 2.0.0, 2.0.1, 2.0.2, 2.0.3, and 2.0.4:</p>
<h3>Version 2.0.0 — January 9, 2017</h3>
<ul>
<li><em><span class="caps">BREAKING</span></em>: rename from <code>minchin.pelican.themes.minchindotca</code>
to <code>seafoam</code></li>
<li><em>feature</em>: add support for Pelican Blog System</li>
<li><em>feature</em>: add Seafoam logo</li>
<li><em>feature</em>: switch to <code>minchin.pelican.jinja_filters</code> to provide
the required Jinja filters, rather than requiring them to be manually
added to the user’s configuration file</li>
<li><em>feature</em>: upgrade to FontAwesome 4.7.0</li>
<li><em>feature</em>: upgrade to jQuery 3.1.0</li>
<li><em>feature</em>: add support for reading time via
<a href="https://github.com/getpelican/pelican-plugins/tree/master/post_stats">post stats</a> plugin</li>
<li><em>feature</em>: add support for <a href="https://github.com/getpelican/pelican-plugins/tree/master/pelican_comment_system">pelican comment system</a></li>
<li><em>bug</em>: restyle comments with bootstrap’s <code>media</code> class (much cleaner template
code) (see <a href="https://github.com/MinchinWeb/seafoam/issues/6">issue 6</a>)</li>
<li><em>bug</em>: switch template variable from <code>PAGES</code> to <code>pages</code> to support Pelican
v3.7 (see <a href="https://github.com/MinchinWeb/seafoam/issues/5">issue 5</a>)</li>
<li><em>bug</em>: don’t print section for next and previous posts in a category if the
article is the only one in that category</li>
</ul>
<h3>Version 2.0.1 — January 10, 2017</h3>
<ul>
<li><em>bug</em>: pluralization of “1 comment” now correct (see <a href="https://github.com/MinchinWeb/seafoam/issues/8">issue
8</a>)</li>
<li><em>bug</em>: fix pagination code to work with Jinja2 v2.9.0 (see <a href="https://github.com/MinchinWeb/seafoam/issues/9">issue
9</a>)</li>
</ul>
<h3>Version 2.0.2 — January 11, 2017</h3>
<ul>
<li><em>bug</em>: fix link color on panel-primary</li>
<li><em>bug</em>: improve layout of generated <span class="caps">HTML</span></li>
</ul>
<h3>Version 2.0.3 — January 11, 2017</h3>
<ul>
<li><em>bug</em>: fix link color in body area of panel-primary (fixes regression
from version 2.0.2)</li>
</ul>
<h3>Version 2.0.4 — January 11, 2017</h3>
<ul>
<li><em>bug</em>: fix archive page template code to work with Jinja2 v2.9.0 (see <a href="https://github.com/MinchinWeb/seafoam/issues/10">issue
10</a>)</li>
</ul></body></html>Blogger Comments Exported2016-12-29T18:33:00-07:002016-12-29T18:33:00-07:00Wm. Minchintag:blog.minchin.ca,2016-12-29:/2016/12/blogger-comments-exported.htmlI am in the middle of moving my blog over from Blogger to self-hosting it and
generating it with Pelican. One of the struggles was what to do with comments.
Something like Disqus could work, but the philosophy of externally hosting
comments doesn’t seem to jibe very well with the philosophy of a static
website, like this one. In the end, I discovered Bernhard Scheirle’s <a class="reference external" href="https://bernhard.scheirle.de/posts/2014/March/29/static-comments-via-email/">Pelican
Comment System</a>!
New comments are submitted via a <em>mailto:</em> link (which generates an email to
me), and then each comment is stored on the backend as a separate file. The
only problem left was how to import my existing comments from Blogger.<html><head></head><body><p>I am in the middle of moving my blog over from Blogger to self-hosting it and
generating it with Pelican. One of the struggles was what to do with comments.
Something like Disqus could work, but the philosophy of externally hosting
comments doesn’t seem to jibe very well with the philosophy of a static
website, like this one. In the end, I discovered Bernhard Scheirle’s <a class="reference external" href="https://bernhard.scheirle.de/posts/2014/March/29/static-comments-via-email/">Pelican
Comment System</a>!
New comments are submitted via a <em>mailto:</em> link (which generates an email to
me), and then each comment is stored on the backend as a separate file. The
only problem left was how to import my existing comments from Blogger.</p>
<p>Blogger is good in that it will give you an export of everything, but the bad
news is it’s one giant <span class="caps">XML</span> file. <span class="caps">XML</span> is great if you’re a computer, but a bit
of a pain if you’re a human. Add to that, I could not find the format
documented anyway. After much trial and error, I was able to pull out what I
needed. I’ll present the code I used to do it (Python 3.6) and then explain
what it does.</p>
<!-- The div below is needed so that the code highlighting works as expected. -->
<div class="highlight"><pre class="code python literal-block"><span class="ln"> 1 </span><span class="ch">#! python3.6</span><span class="w">
</span><span class="ln"> 2 </span><span class="w"></span><span class="sd">"""
</span><span class="ln"> 3 </span><span class="sd">Export Comments from BLogger XML
</span><span class="ln"> 4 </span><span class="sd">
</span><span class="ln"> 5 </span><span class="sd">Takes in a Blogger export XML file and spits out each comment in a seperate
</span><span class="ln"> 6 </span><span class="sd">file, such that can be used with the [Pelican Comment System]
</span><span class="ln"> 7 </span><span class="sd">(https://bernhard.scheirle.de/posts/2014/March/29/static-comments-via-email/).
</span><span class="ln"> 8 </span><span class="sd">
</span><span class="ln"> 9 </span><span class="sd">May be simple to extend to export posts as well.
</span><span class="ln"> 10 </span><span class="sd">
</span><span class="ln"> 11 </span><span class="sd">For a more detailed description, read my blog post at
</span><span class="ln"> 12 </span><span class="sd"> http://blog.minchin.ca/2016/12/blogger-comments-exported.html
</span><span class="ln"> 13 </span><span class="sd">
</span><span class="ln"> 14 </span><span class="sd">Author: Wm. Minchin -- minchinweb@gmail.com
</span><span class="ln"> 15 </span><span class="sd">License: MIT
</span><span class="ln"> 16 </span><span class="sd">Changes:
</span><span class="ln"> 17 </span><span class="sd">
</span><span class="ln"> 18 </span><span class="sd"> - 2016.12.29 -- initial release
</span><span class="ln"> 19 </span><span class="sd">"""</span><span class="w">
</span><span class="ln"> 20 </span><span class="w">
</span><span class="ln"> 21 </span><span class="w"></span><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span><span class="w">
</span><span class="ln"> 22 </span><span class="w">
</span><span class="ln"> 23 </span><span class="w"></span><span class="kn">import</span> <span class="nn">untangle</span><span class="w">
</span><span class="ln"> 24 </span><span class="w">
</span><span class="ln"> 25 </span><span class="w"></span><span class="c1">###############################################################################</span><span class="w">
</span><span class="ln"> 26 </span><span class="w"></span><span class="c1"># Constants #</span><span class="w">
</span><span class="ln"> 27 </span><span class="w"></span><span class="c1">###############################################################################</span><span class="w">
</span><span class="ln"> 28 </span><span class="w">
</span><span class="ln"> 29 </span><span class="w"></span><span class="n">BLOGGER_EXPORT</span> <span class="o">=</span> <span class="sa">r</span><span class="s1">'c:\tmp\blog.xml'</span><span class="w">
</span><span class="ln"> 30 </span><span class="w"></span><span class="n">COMMENTS_DIR</span> <span class="o">=</span> <span class="s1">'comments'</span><span class="w">
</span><span class="ln"> 31 </span><span class="w"></span><span class="n">COMMENT_EXT</span> <span class="o">=</span> <span class="s1">'.md'</span><span class="w">
</span><span class="ln"> 32 </span><span class="w"></span><span class="n">AUTHORS_FILENAME</span> <span class="o">=</span> <span class="s1">'authors.txt'</span><span class="w">
</span><span class="ln"> 33 </span><span class="w">
</span><span class="ln"> 34 </span><span class="w"></span><span class="c1">###############################################################################</span><span class="w">
</span><span class="ln"> 35 </span><span class="w"></span><span class="c1"># Main Code Body #</span><span class="w">
</span><span class="ln"> 36 </span><span class="w"></span><span class="c1">###############################################################################</span><span class="w">
</span><span class="ln"> 37 </span><span class="w">
</span><span class="ln"> 38 </span><span class="w"></span><span class="n">authors_and_pics</span> <span class="o">=</span> <span class="p">[]</span><span class="w">
</span><span class="ln"> 39 </span><span class="w">
</span><span class="ln"> 40 </span><span class="w">
</span><span class="ln"> 41 </span><span class="w"></span><span class="k">def</span> <span class="nf">main</span><span class="p">():</span><span class="w">
</span><span class="ln"> 42 </span><span class="w"></span> <span class="n">obj</span> <span class="o">=</span> <span class="n">untangle</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">BLOGGER_EXPORT</span><span class="p">)</span><span class="w">
</span><span class="ln"> 43 </span><span class="w">
</span><span class="ln"> 44 </span><span class="w"></span> <span class="n">templates</span> <span class="o">=</span> <span class="mi">0</span><span class="w">
</span><span class="ln"> 45 </span><span class="w"></span> <span class="n">posts</span> <span class="o">=</span> <span class="mi">0</span><span class="w">
</span><span class="ln"> 46 </span><span class="w"></span> <span class="n">comments</span> <span class="o">=</span> <span class="mi">0</span><span class="w">
</span><span class="ln"> 47 </span><span class="w"></span> <span class="n">settings</span> <span class="o">=</span> <span class="mi">0</span><span class="w">
</span><span class="ln"> 48 </span><span class="w"></span> <span class="n">others</span> <span class="o">=</span> <span class="mi">0</span><span class="w">
</span><span class="ln"> 49 </span><span class="w">
</span><span class="ln"> 50 </span><span class="w"></span> <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">obj</span><span class="o">.</span><span class="n">feed</span><span class="o">.</span><span class="n">entry</span><span class="p">:</span><span class="w">
</span><span class="ln"> 51 </span><span class="w"></span> <span class="k">try</span><span class="p">:</span><span class="w">
</span><span class="ln"> 52 </span><span class="w"></span> <span class="n">full_type</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">category</span><span class="p">[</span><span class="s1">'term'</span><span class="p">]</span><span class="w">
</span><span class="ln"> 53 </span><span class="w"></span> <span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span><span class="w">
</span><span class="ln"> 54 </span><span class="w"></span> <span class="c1"># if a post is under multiple categories</span><span class="w">
</span><span class="ln"> 55 </span><span class="w"></span> <span class="k">for</span> <span class="n">my_category</span> <span class="ow">in</span> <span class="n">entry</span><span class="o">.</span><span class="n">category</span><span class="p">:</span><span class="w">
</span><span class="ln"> 56 </span><span class="w"></span> <span class="n">full_type</span> <span class="o">=</span> <span class="n">my_category</span><span class="p">[</span><span class="s1">'term'</span><span class="p">]</span><span class="w">
</span><span class="ln"> 57 </span><span class="w"></span> <span class="c1"># str.find() uses a return of `-1` to denote failure</span><span class="w">
</span><span class="ln"> 58 </span><span class="w"></span> <span class="k">if</span> <span class="n">full_type</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s1">'#'</span><span class="p">)</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span><span class="w">
</span><span class="ln"> 59 </span><span class="w"></span> <span class="k">break</span><span class="w">
</span><span class="ln"> 60 </span><span class="w"></span> <span class="k">else</span><span class="p">:</span><span class="w">
</span><span class="ln"> 61 </span><span class="w"></span> <span class="n">others</span> <span class="o">+=</span> <span class="mi">1</span><span class="w">
</span><span class="ln"> 62 </span><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="w">
</span><span class="ln"> 63 </span><span class="w">
</span><span class="ln"> 64 </span><span class="w"></span> <span class="n">simple_type</span> <span class="o">=</span> <span class="n">full_type</span><span class="p">[</span><span class="n">full_type</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s1">'#'</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">:]</span><span class="w">
</span><span class="ln"> 65 </span><span class="w">
</span><span class="ln"> 66 </span><span class="w"></span> <span class="k">if</span> <span class="s1">'settings'</span> <span class="o">==</span> <span class="n">simple_type</span><span class="p">:</span><span class="w">
</span><span class="ln"> 67 </span><span class="w"></span> <span class="n">settings</span> <span class="o">+=</span> <span class="mi">1</span><span class="w">
</span><span class="ln"> 68 </span><span class="w"></span> <span class="k">elif</span> <span class="s1">'post'</span> <span class="o">==</span> <span class="n">simple_type</span><span class="p">:</span><span class="w">
</span><span class="ln"> 69 </span><span class="w"></span> <span class="n">posts</span> <span class="o">+=</span> <span class="mi">1</span><span class="w">
</span><span class="ln"> 70 </span><span class="w"></span> <span class="c1"># process posts here</span><span class="w">
</span><span class="ln"> 71 </span><span class="w"></span> <span class="k">elif</span> <span class="s1">'comment'</span> <span class="o">==</span> <span class="n">simple_type</span><span class="p">:</span><span class="w">
</span><span class="ln"> 72 </span><span class="w"></span> <span class="n">comments</span> <span class="o">+=</span> <span class="mi">1</span><span class="w">
</span><span class="ln"> 73 </span><span class="w"></span> <span class="n">process_comment</span><span class="p">(</span><span class="n">entry</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span><span class="w">
</span><span class="ln"> 74 </span><span class="w"></span> <span class="k">elif</span> <span class="s1">'template'</span> <span class="o">==</span> <span class="n">simple_type</span><span class="p">:</span><span class="w">
</span><span class="ln"> 75 </span><span class="w"></span> <span class="n">templates</span> <span class="o">+=</span> <span class="mi">1</span><span class="w">
</span><span class="ln"> 76 </span><span class="w"></span> <span class="k">else</span><span class="p">:</span><span class="w">
</span><span class="ln"> 77 </span><span class="w"></span> <span class="n">others</span> <span class="o">+=</span> <span class="mi">1</span><span class="w">
</span><span class="ln"> 78 </span><span class="w">
</span><span class="ln"> 79 </span><span class="w"></span> <span class="n">export_authors</span><span class="p">()</span><span class="w">
</span><span class="ln"> 80 </span><span class="w">
</span><span class="ln"> 81 </span><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'''
</span><span class="ln"> 82 </span><span class="s1"> </span><span class="si">{</span><span class="n">templates</span><span class="si">}</span><span class="s1"> template
</span><span class="ln"> 83 </span><span class="s1"> </span><span class="si">{</span><span class="n">posts</span><span class="si">}</span><span class="s1"> posts (including drafts)
</span><span class="ln"> 84 </span><span class="s1"> </span><span class="si">{</span><span class="n">comments</span><span class="si">}</span><span class="s1"> comments
</span><span class="ln"> 85 </span><span class="s1"> </span><span class="si">{</span><span class="n">settings</span><span class="si">}</span><span class="s1"> settings
</span><span class="ln"> 86 </span><span class="s1"> </span><span class="si">{</span><span class="n">others</span><span class="si">}</span><span class="s1"> other entries'''</span><span class="p">)</span><span class="w">
</span><span class="ln"> 87 </span><span class="w">
</span><span class="ln"> 88 </span><span class="w">
</span><span class="ln"> 89 </span><span class="w"></span><span class="k">def</span> <span class="nf">process_comment</span><span class="p">(</span><span class="n">entry</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span><span class="w">
</span><span class="ln"> 90 </span><span class="w"></span> <span class="c1"># e.g. "tag:blogger.com,1999:blog-26967745.post-4115122471434984978"</span><span class="w">
</span><span class="ln"> 91 </span><span class="w"></span> <span class="n">comment_id</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">cdata</span><span class="w">
</span><span class="ln"> 92 </span><span class="w"></span> <span class="c1"># in ISO 8601 format, usable as is</span><span class="w">
</span><span class="ln"> 93 </span><span class="w"></span> <span class="n">comment_published</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">published</span><span class="o">.</span><span class="n">cdata</span><span class="w">
</span><span class="ln"> 94 </span><span class="w"></span> <span class="n">comment_body</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="n">cdata</span><span class="w">
</span><span class="ln"> 95 </span><span class="w"></span> <span class="n">comment_post_id</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">thr_in_reply_to</span><span class="p">[</span><span class="s1">'ref'</span><span class="p">]</span><span class="w">
</span><span class="ln"> 96 </span><span class="w"></span> <span class="n">comment_author</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">author</span><span class="o">.</span><span class="n">name</span><span class="o">.</span><span class="n">cdata</span><span class="w">
</span><span class="ln"> 97 </span><span class="w"></span> <span class="n">comment_author_pic</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">author</span><span class="o">.</span><span class="n">gd_image</span><span class="p">[</span><span class="s1">'src'</span><span class="p">]</span><span class="w">
</span><span class="ln"> 98 </span><span class="w"></span> <span class="n">comment_author_email</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">author</span><span class="o">.</span><span class="n">email</span><span class="o">.</span><span class="n">cdata</span><span class="w">
</span><span class="ln"> 99 </span><span class="w">
</span><span class="ln">100 </span><span class="w"></span> <span class="c1"># add author and pic to global list</span><span class="w">
</span><span class="ln">101 </span><span class="w"></span> <span class="k">global</span> <span class="n">authors_and_pics</span><span class="w">
</span><span class="ln">102 </span><span class="w"></span> <span class="n">authors_and_pics</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">comment_author</span><span class="p">,</span> <span class="n">comment_author_pic</span><span class="p">))</span><span class="w">
</span><span class="ln">103 </span><span class="w">
</span><span class="ln">104 </span><span class="w"></span> <span class="c1"># use this for a filename for the comment</span><span class="w">
</span><span class="ln">105 </span><span class="w"></span> <span class="c1"># e.g. "4115122471434984978"</span><span class="w">
</span><span class="ln">106 </span><span class="w"></span> <span class="n">comment_short_id</span> <span class="o">=</span> <span class="n">comment_id</span><span class="p">[</span><span class="n">comment_id</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s1">'post-'</span><span class="p">)</span><span class="o">+</span><span class="mi">5</span><span class="p">:]</span><span class="w">
</span><span class="ln">107 </span><span class="w">
</span><span class="ln">108 </span><span class="w"></span> <span class="n">comment_text</span> <span class="o">=</span> <span class="s2">"date: </span><span class="si">{}</span><span class="se">\n</span><span class="s2">author: </span><span class="si">{}</span><span class="se">\n</span><span class="s2">email: </span><span class="si">{}</span><span class="se">\n\n</span><span class="si">{}</span><span class="se">\n</span><span class="s2">"</span>\
<span class="ln">109 </span> <span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">comment_published</span><span class="p">,</span><span class="w">
</span><span class="ln">110 </span><span class="w"></span> <span class="n">comment_author</span><span class="p">,</span><span class="w">
</span><span class="ln">111 </span><span class="w"></span> <span class="n">comment_author_email</span><span class="p">,</span><span class="w">
</span><span class="ln">112 </span><span class="w"></span> <span class="n">comment_body</span><span class="p">)</span><span class="w">
</span><span class="ln">113 </span><span class="w">
</span><span class="ln">114 </span><span class="w"></span> <span class="c1"># article</span><span class="w">
</span><span class="ln">115 </span><span class="w"></span> <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">obj</span><span class="o">.</span><span class="n">feed</span><span class="o">.</span><span class="n">entry</span><span class="p">:</span><span class="w">
</span><span class="ln">116 </span><span class="w"></span> <span class="n">entry_id</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">cdata</span><span class="w">
</span><span class="ln">117 </span><span class="w"></span> <span class="k">if</span> <span class="n">entry_id</span> <span class="o">==</span> <span class="n">comment_post_id</span><span class="p">:</span><span class="w">
</span><span class="ln">118 </span><span class="w"></span> <span class="n">article_entry</span> <span class="o">=</span> <span class="n">entry</span><span class="w">
</span><span class="ln">119 </span><span class="w"></span> <span class="k">break</span><span class="w">
</span><span class="ln">120 </span><span class="w"></span> <span class="k">else</span><span class="p">:</span><span class="w">
</span><span class="ln">121 </span><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="s2">"No matching article for comment"</span><span class="p">,</span> <span class="n">comment_id</span><span class="p">,</span> <span class="n">comment_post_id</span><span class="p">)</span><span class="w">
</span><span class="ln">122 </span><span class="w"></span> <span class="c1"># don't process comment further</span><span class="w">
</span><span class="ln">123 </span><span class="w"></span> <span class="k">return</span><span class="w">
</span><span class="ln">124 </span><span class="w">
</span><span class="ln">125 </span><span class="w"></span> <span class="c1"># article date published</span><span class="w">
</span><span class="ln">126 </span><span class="w"></span> <span class="n">article_publshed</span> <span class="o">=</span> <span class="n">article_entry</span><span class="o">.</span><span class="n">published</span><span class="o">.</span><span class="n">cdata</span><span class="w">
</span><span class="ln">127 </span><span class="w">
</span><span class="ln">128 </span><span class="w"></span> <span class="c1"># article slug</span><span class="w">
</span><span class="ln">129 </span><span class="w"></span> <span class="k">for</span> <span class="n">link</span> <span class="ow">in</span> <span class="n">article_entry</span><span class="o">.</span><span class="n">link</span><span class="p">:</span><span class="w">
</span><span class="ln">130 </span><span class="w"></span> <span class="k">if</span> <span class="n">link</span><span class="p">[</span><span class="s1">'rel'</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'alternate'</span><span class="p">:</span><span class="w">
</span><span class="ln">131 </span><span class="w"></span> <span class="n">article_link</span> <span class="o">=</span> <span class="n">link</span><span class="p">[</span><span class="s1">'href'</span><span class="p">]</span><span class="w">
</span><span class="ln">132 </span><span class="w"></span> <span class="k">break</span><span class="w">
</span><span class="ln">133 </span><span class="w"></span> <span class="k">else</span><span class="p">:</span><span class="w">
</span><span class="ln">134 </span><span class="w"></span> <span class="n">article_title</span> <span class="o">=</span> <span class="n">article_entry</span><span class="o">.</span><span class="n">title</span><span class="o">.</span><span class="n">cdata</span><span class="w">
</span><span class="ln">135 </span><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="s1">'Could not find slug for'</span><span class="p">,</span> <span class="n">article_title</span><span class="p">)</span><span class="w">
</span><span class="ln">136 </span><span class="w"></span> <span class="n">article_link</span> <span class="o">=</span> <span class="n">article_title</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="s1">'-'</span><span class="p">)</span><span class="w">
</span><span class="ln">137 </span><span class="w">
</span><span class="ln">138 </span><span class="w"></span> <span class="n">article_slug</span> <span class="o">=</span> <span class="n">article_link</span><span class="p">[</span><span class="n">article_link</span><span class="o">.</span><span class="n">rfind</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">:</span><span class="w">
</span><span class="ln">139 </span><span class="w"></span> <span class="n">article_link</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s1">'.html'</span><span class="p">)]</span><span class="w">
</span><span class="ln">140 </span><span class="w">
</span><span class="ln">141 </span><span class="w"></span> <span class="n">comment_filename</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">COMMENTS_DIR</span><span class="p">)</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span><span class="w">
</span><span class="ln">142 </span><span class="w"></span> <span class="c1"># folder; if it doesn't exist, create it</span><span class="w">
</span><span class="ln">143 </span><span class="w"></span> <span class="n">comment_filename</span> <span class="o">=</span> <span class="n">comment_filename</span> <span class="o">/</span> <span class="n">article_slug</span><span class="w">
</span><span class="ln">144 </span><span class="w"></span> <span class="n">comment_filename</span><span class="o">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="w">
</span><span class="ln">145 </span><span class="w"></span> <span class="c1"># write the comment file</span><span class="w">
</span><span class="ln">146 </span><span class="w"></span> <span class="n">comment_filename</span> <span class="o">=</span> <span class="n">comment_filename</span> <span class="o">/</span> <span class="p">(</span><span class="n">comment_short_id</span> <span class="o">+</span> <span class="n">COMMENT_EXT</span><span class="p">)</span><span class="w">
</span><span class="ln">147 </span><span class="w"></span> <span class="n">comment_filename</span><span class="o">.</span><span class="n">write_text</span><span class="p">(</span><span class="n">comment_text</span><span class="p">)</span><span class="w">
</span><span class="ln">148 </span><span class="w">
</span><span class="ln">149 </span><span class="w">
</span><span class="ln">150 </span><span class="w"></span><span class="k">def</span> <span class="nf">export_authors</span><span class="p">():</span><span class="w">
</span><span class="ln">151 </span><span class="w"></span> <span class="n">to_export</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">authors_and_pics</span><span class="p">)</span><span class="w">
</span><span class="ln">152 </span><span class="w"></span> <span class="n">to_export</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">to_export</span><span class="p">)</span><span class="w">
</span><span class="ln">153 </span><span class="w"></span> <span class="n">to_export</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span><span class="w">
</span><span class="ln">154 </span><span class="w">
</span><span class="ln">155 </span><span class="w"></span> <span class="n">str_export</span> <span class="o">=</span> <span class="s1">''</span><span class="w">
</span><span class="ln">156 </span><span class="w"></span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">to_export</span><span class="p">:</span><span class="w">
</span><span class="ln">157 </span><span class="w"></span> <span class="n">str_export</span> <span class="o">+=</span> <span class="p">(</span><span class="n">i</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s1">'</span><span class="se">\t\t</span><span class="s1">'</span> <span class="o">+</span> <span class="n">i</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="s1">'</span><span class="se">\n</span><span class="s1">'</span><span class="p">)</span><span class="w">
</span><span class="ln">158 </span><span class="w">
</span><span class="ln">159 </span><span class="w"></span> <span class="n">authors_filename</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">COMMENTS_DIR</span><span class="p">)</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span> <span class="o">/</span> <span class="n">AUTHORS_FILENAME</span><span class="w">
</span><span class="ln">160 </span><span class="w"></span> <span class="n">authors_filename</span><span class="o">.</span><span class="n">write_text</span><span class="p">(</span><span class="n">str_export</span><span class="p">)</span><span class="w">
</span><span class="ln">161 </span><span class="w">
</span><span class="ln">162 </span><span class="w">
</span><span class="ln">163 </span><span class="w"></span><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span><span class="w">
</span><span class="ln">164 </span><span class="w"></span> <span class="n">main</span><span class="p">()</span>
</pre>
</div><p>The code is written to run in Python 3.6, which was released a few days ago. If
you haven’t upgraded yet, I think the only 3.6-specific feature I used was the
<em>f-string</em> at line 81.</p>
<p>The other change you will need to do to use this code for yourself is the
update the configuration at the top of the file (lines 25-32). You will also
need the <span class="caps">XML</span> export of your Blogger blog.</p>
<p><a class="reference external" href="https://untangle.readthedocs.io/en/latest/">untangle</a> is the <span class="caps">XML</span> library I
used. It seemed to work well for the task at hand. It can easily be installed
from <tt class="docutils literal">pip</tt>:</p>
<div class="highlight"><pre><span></span>pip<span class="w"> </span>install<span class="w"> </span>untangle
</pre></div>
<p>THe first task was figuring out what was all in Blogger <span class="caps">XML</span> export. The bulk of
it was “entry“‘s — the first one was my <span class="caps">HTML</span> template, the next batch was a
bunch of Blogger settings, the third batch was my posts, and the last batch was
the comments. Which one it fell into could be determined by looking at the
<tt class="docutils literal"><span class="pre">entry.category['term']</span></tt> (see line 52). This would give a string (a blogger
<span class="caps">URL</span>) that ended in “#template”, “#settings”, “#post”, or “#comment” as the case
may be.</p>
<p>If I had not already exported my entries, this would have been the way to do it
(see line 70).</p>
<p>Comments were processed by pulling the information I wanted out (see lines
90-98), determining what post the comment was attached to (see lines 115-123),
and then write all the data to separate markdown files (see line 147). Each
comment is exported into a folder named after the slug of the entry it was
attached to. Renaming these folders proved one of the more annoying parts, as I
had cleaned up the slugs of many of my posts during their initial export. Then
it was to regenerate my blog (with the comments turned on), and make sure
everything was working as expected. The slug renaming was the only (minor)
show-stopper I ran into.</p>
<p>In other cleanup, I has also changed my name (as the default blog author)
during the initial export on the blog, so I had to change that anywhere it
appeared. As a final touch, I brought over some of the profile pictures of the
other commenters, where available (this is what the <tt class="docutils literal">authors.txt</tt> file,
generated by lines 150-160 is designed to help with). These are configured as
follows in my main <tt class="docutils literal">pelicanconf.py</tt> file (the configuration file for Pelican):</p>
<div class="highlight"><pre><span></span><span class="n">PELICAN_COMMENT_SYSTEM_AUTHORS</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">(</span><span class="s1">'PROTIK KHAN'</span><span class="p">,</span> <span class="s1">'noreply@blogger.com'</span><span class="p">):</span> <span class="s2">"images/authors/rabiul_karim.webp"</span><span class="p">,</span>
<span class="p">(</span><span class="s1">'Matthew Hartzell'</span><span class="p">,</span> <span class="s1">'noreply@blogger.com'</span><span class="p">):</span> <span class="s2">"images/authors/matthew_hartzell.webp"</span><span class="p">,</span>
<span class="p">(</span><span class="s1">'Jens-Peter Labus'</span><span class="p">,</span> <span class="s1">'noreply@blogger.com'</span><span class="p">):</span> <span class="s2">"images/authors/jens-peter_labus.png"</span><span class="p">,</span>
<span class="p">(</span><span class="s1">'Bridget'</span><span class="p">,</span> <span class="s1">'noreply@blogger.com'</span><span class="p">):</span> <span class="s2">"images/authors/bridget.jpg"</span><span class="p">,</span>
<span class="p">(</span><span class="s1">'melissaclee'</span><span class="p">,</span> <span class="s1">'noreply@blogger.com'</span><span class="p">):</span> <span class="s2">"images/authors/melissa_lee.jpg"</span><span class="p">,</span>
<span class="p">(</span><span class="s1">'Melissa'</span><span class="p">,</span> <span class="s1">'noreply@blogger.com'</span><span class="p">):</span> <span class="s2">"images/authors/melissa_lee.jpg"</span>
<span class="p">}</span>
</pre></div>
<p>Hopefully some of this code will prove useful to someone else dealing with
their Blogger export.</p>
<p>The code has also been posted as a <a class="reference external" href="https://gist.github.com/MinchinWeb/7b2e4cf4d1b935c62ce8d6d1968270ff">gist on GitHub</a>, so
you’re welcome to submit improvements as well. If you want to download the
code, this may be the simplest place to get it from.</p>
<p>Code is under the <span class="caps">MIT</span> license.</p>
</body></html>Colourettu 2.0.0 for Python Released2016-11-28T17:51:00-07:002016-11-28T17:51:00-07:00Wm. Minchintag:blog.minchin.ca,2016-11-28:/2016/11/colourettu-2-released.html<p>Version 2.0.0 of <em>Colourettu</em> has been released.</p>
<p>Colourettu is a Python library I’ve written for dealing with colours and
“palettes” (groups of colours).</p>
<html><head></head><body><p>Version 2.0.0 of <em>Colourettu</em> has been released.</p>
<p>Colourettu is a Python library I’ve written for dealing with colours and
“palettes” (groups of colours).</p>
<p>A quick example:</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">colourettu</span> <span class="kn">import</span> <span class="n">Colour</span><span class="p">,</span> <span class="n">Palette</span>
<span class="n">p1</span> <span class="o">=</span> <span class="n">Palette</span><span class="p">()</span>
<span class="n">p1</span><span class="o">.</span><span class="n">blend</span><span class="p">()</span>
<span class="n">p1</span><span class="o">.</span><span class="n">to_image</span><span class="p">(</span><span class="s1">'p1_blended.png'</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="n">vertical</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</code></pre></div>
<div class="text-center">
<p><img alt="Colourettu p1 Blended" src="https://blog.minchin.ca/images/2016/p1_blended.png"/></p>
</div>
<div class="highlight"><pre><span></span><code><span class="n">c1</span> <span class="o">=</span> <span class="n">Colour</span><span class="p">(</span><span class="s1">'#fff'</span><span class="p">)</span>
<span class="n">c2</span> <span class="o">=</span> <span class="n">Colour</span><span class="p">(</span><span class="s1">'#7e1e9c'</span><span class="p">)</span>
<span class="n">p3</span> <span class="o">=</span> <span class="n">Palette</span><span class="p">(</span><span class="n">c1</span><span class="p">,</span> <span class="n">c3</span><span class="p">)</span>
<span class="n">p3</span><span class="o">.</span><span class="n">blend</span><span class="p">(</span><span class="n">cycles</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
<span class="n">p3</span><span class="o">.</span><span class="n">to_image</span><span class="p">(</span><span class="s1">'p3.png'</span><span class="p">,</span> <span class="n">max_width</span><span class="o">=</span><span class="mi">360</span><span class="p">,</span> <span class="n">vertical</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</code></pre></div>
<div class="text-center">
<p><img alt="Colourettu p3" src="https://blog.minchin.ca/images/2016/p3.png"/></p>
</div>
<p>The easiest to install (or upgrade) <em>Colourettu</em> (assuming you already
have Python installed) is to use <em>pip</em>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>colourettu<span class="w"> </span>--upgrade
</code></pre></div>
<p>Additions for this version include the <em>blend</em> functionality, both as a
stand-alone function and as a Palette method.</p>
<p>Breaking changes in this version are the fact that the <em>Colour</em> and <em>Palette</em>
class have been renamed to the CapWords capitalization.</p>
<p>Other changes include:</p>
<ul>
<li>the tests are now included by default at the <em>colourettu.test</em> namespace.
This allows the tests to be run on a system with Colourettu installed by
running <code>green colourettu.test</code></li>
<li>updated documentation build system</li>
<li>better documentation</li>
<li>fix bug where using the <em>max_width</em> parameter with <code>Palette.to_image()</code> would
result ina black strip on the bottom/left of the generated image.</li>
</ul>
<p><a href="http://minchin.ca/colourettu/"><em>Colourettu</em> documentation</a> is now
online. A <a href="http://minchin.ca/colourettu/changelog.html">full changelog</a>
is online as part of that. <a href="https://github.com/MinchinWeb/colourettu/">The code for
<em>Colourettu</em></a> is hosted on Github.</p></body></html>Jinja Filters 1.0.0 for Pelican Released2016-11-18T17:41:00-07:002021-04-30T19:25:00-06:00Wm. Minchintag:blog.minchin.ca,2016-11-18:/2016/11/jinja-filters-for-pelican-100-released.html<p><strong>Jinja Filters</strong> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a
static site generator written in Python.</p>
<p><strong>Jinja Filters</strong> provides a selection of functions (called <em>filters</em>) for
templates to use when building your website. They are packaged for Pelican, but
may prove useful for other projects that make use of
<a href="http://jinja.pocoo.org/">Jinja2</a>.</p>
<html><head></head><body><p><strong>Jinja Filters</strong> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a
static site generator written in Python.</p>
<p><strong>Jinja Filters</strong> provides a selection of functions (called <em>filters</em>) for
templates to use when building your website. They are packaged for Pelican, but
may prove useful for other projects that make use of
<a href="http://jinja.pocoo.org/">Jinja2</a>.</p>
<h2>Installation</h2>
<p>The easiest way to install <strong>Jinja Filters</strong> is through the use of pip. This
will also install the required dependencies automatically.</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.jinja_filters
</code></pre></div>
<p>Then, in your <code>pelicanconf.py</code> file, add <strong>Jinja Filters</strong> to your list of plugins:</p>
<div class="highlight"><pre><span></span><code><span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># ...</span>
<span class="s1">'minchin.pelican.jinja_filters'</span><span class="p">,</span>
<span class="c1"># ...</span>
<span class="p">]</span>
</code></pre></div>
<p>And that’s it! The filters are now available for use in your templates.</p>
<h2>Usage</h2>
<p>At present, the plugin includes the following filters:</p>
<ul>
<li><em>datetime</em> — allows you to change to format displayed for a datetime object.
Optionally supply a <a href="https://docs.python.org/3.6/library/datetime.html#strftime-and-strptime-behavior">datetime format
string</a>
to get a custom format.</li>
<li><em>article_date</em> — a specialized version of <em>datetime</em> that returns datetimes
as wanted for article dates; specifically <em>Friday, November 4, 2016</em>.</li>
<li><em>breaking_spaces</em> — replaceds non-breaking spaces (<span class="caps">HTML</span> code <em> </em>) with
normal spaces.</li>
<li><em>titlecase</em> — Titlecases the supplied string</li>
</ul>
<p>For example, within your theme templates, you might have code like:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"published"</span><span class="p">></span>
Article Published <span class="cp">{{</span> <span class="nv">article.date</span> <span class="o">|</span> <span class="nf">article_date</span> <span class="cp">}}</span>
<span class="p"><!--</span--><span class="nt">span</span><span class="p">></span>
</span></code></pre></div>
<p>gives:</p>
<div class="highlight"><pre><span></span><code>Article Published Friday, November 4, 2016
</code></pre></div>
<p>Or with your own dateformat:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"published"</span><span class="p">></span>
Article Published <span class="cp">{{</span> <span class="nv">article.date</span> <span class="o">|</span> <span class="nf">datetime</span><span class="o">(</span><span class="s1">'%b %d, %Y'</span><span class="o">)</span> <span class="cp">}}</span>
<span class="p"><!--</span--><span class="nt">span</span><span class="p">></span>
</span></code></pre></div>
<p>gives:</p>
<div class="highlight"><pre><span></span><code>Article Published Nov 04, 2016
</code></pre></div>
<p>Filters can also be chained, or applied in sequence. For example to remove
breaking spaces and then titlecase a category name, you might have code like:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">SITEURL</span> <span class="cp">}}</span><span class="s">/</span><span class="cp">{{</span> <span class="nv">article.category.url</span> <span class="cp">}}</span><span class="s">"</span><span class="p">></span>
<span class="cp">{{</span> <span class="nv">article.category</span> <span class="o">|</span> <span class="nf">breaking_spaces</span> <span class="o">|</span> <span class="nf">titlecase</span><span class="cp">}}</span>
<span class="p"><!--</span--><span class="nt">a</span><span class="p">></span>
</span></code></pre></div>
<h2>Known Issues</h2>
<p>An issue, as such, is that there is no formal test suite. Testing is currently
limited to my in-use observations. I also run a basic check upon uploaded the
package to PyPI that it can be downloaded and loaded into Python.</p>
<p>The package is tested in Python 3.5; compatibility with other version of Python
is unknown.</p>
<h2>Code</h2>
<p>The code for this project is available on
<a href="https://github.com/MinchinWeb/minchin.pelican.jinja_filters">GitHub</a>.
Contributions are welcome!</p></body></html>CName Pelican Plugin 1.0.0 Released2016-10-03T20:39:00-06:002016-10-03T20:39:00-06:00Wm. Minchintag:blog.minchin.ca,2016-10-03:/2016/10/cname-plugin-for-pelican-100-released.html<p><strong>CName</strong> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a static site
generator written in Python.</p>
<p><strong>CName</strong> creates a <em><span class="caps">CNAME</span></em> file in the root of your output directory. This is
useful when you are publishing your site to <a href="https://pages.github.com/">GitHub
Pages</a> on a <a href="https://help.github.com/articles/using-a-custom-domain-with-github-pages/">custom
domain</a>.</p>
<html><head></head><body><p><strong>CName</strong> is a plugin for <a href="http://docs.getpelican.com/">Pelican</a>, a static site
generator written in Python.</p>
<p><strong>CName</strong> creates a <em><span class="caps">CNAME</span></em> file in the root of your output directory. This is
useful when you are publishing your site to <a href="https://pages.github.com/">GitHub
Pages</a> on a <a href="https://help.github.com/articles/using-a-custom-domain-with-github-pages/">custom
domain</a>.</p>
<h2>Installation</h2>
<p>The easiest way to install <strong>CName</strong> is through the use of pip. This will also
install the required dependencies automatically.</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.plugins.cname
</code></pre></div>
<p>Then, in your <code>pelicanconf.py</code> file, add <strong>CName</strong> to your list of plugins:</p>
<div class="highlight"><pre><span></span><code><span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># ...</span>
<span class="s1">'minchin.pelican.plugins.cname'</span><span class="p">,</span>
<span class="c1"># ...</span>
<span class="p">]</span>
</code></pre></div>
<p>And that’s it! No further configuration is needed.</p>
<h2>Usage</h2>
<p>No configuration is needed. The value places in the <em><span class="caps">CNAME</span></em> files is based on
your <code>SITEURL</code> setting.</p>
<h2>Known Issues</h2>
<p>As the plugin makes use of the <code>SITEURL</code> plugin, if you are using both a
<code>pelicanconf.py</code> and a <code>publishconf.py</code> settings file, <strong>CName</strong> will likely
generate different results for when you are testing your site locally and when
you push it to production.</p>
<h2>Credits</h2>
<p>I can’t claim I came up with the original idea. It is based on the <a href="https://github.com/getpelican/pelican-plugins/pull/566">original
code</a> by <a href="http://lazycoder.ru/">Dmitriy
Kalinin</a>, that has languished as an open pull request in
the <code>pelican-plugins</code> repo for the last year.</p>
<h2>Code</h2>
<p>The code for this project is available on
<a href="https://github.com/MinchinWeb/minchin.pelican.plugins.cname">GitHub</a>.
Contributions are welcome!</p></body></html>I Redesigned My Blog2016-09-12T19:46:00-06:002016-12-29T22:33:00-07:00Wm. Minchintag:blog.minchin.ca,2016-09-12:/2016/09/i-redesigned-my-blog.htmlI just finished redesigning my blog and moving it from one backend provider to
another. Another post will follow with the technical discussion of the steps
involved, but for this post I wanted to focus on the philosophical considerations.<html><head></head><body><p>I just finished redesigning my blog and moving it from one backend provider to
another. Another post will follow with the technical discussion of the steps
involved, but for this post I wanted to focus on the philosophical considerations.</p>
<h2>Why You Shouldn’t Do This</h2>
<p>To start with, we shouldn’t you do the same thing. In a word, <strong>time</strong>. I’ve
spent numerous evenings over the last <del>two</del> four months to get this
done. This has proved far more than a simple weekend project. If you’re time
available for your blog is limited (which it almost always is), and your
blogging system isn’t broken at some fundamental level, your time is probably
better spend creating content (i.e. writing blog posts) than rebuilding your backend.</p>
<h2>Why I Did It Anyway</h2>
<p>Ultimately, I had three goals that drove me to move blog back ends:</p>
<ol>
<li>I wanted to be able to write new posts in Markdown,</li>
<li>I wanted my blog to look the same as the rest of my site, and</li>
<li>I wanted a backup of my blog.</li>
</ol>
<h3>Markdown</h3>
<p>Personally, I discovered Markdown about five years ago and quickly fell in
love. I started keeping personal and professional notes, and one of my first
projects posted to GitHub was to easily convert raw Markdown into <span class="caps">HTML</span> pages.
Since I was writing elsewhere in Markdown, I wanted to write here in Markdown too.</p>
<h3>Design</h3>
<p>For design, I could either rework my Blogger theme to match what I’d done with
the rest of the site, or I could move my blog over to the same backend I was
using for the rest of my site. Blogger theme editing is a pain (mostly it’s too
complex to do properly in the text area of a browser, and very hard to debug).
I figured it would be easier to move over to the same theme I was using for the
rest of my site. I still think that proved to be easier, but it did result in a
significant update to the
<a href="https://blog.minchin.ca/2016/09/minchin-dot-ca-pelican-theme-version-110-released.html">theme</a>.</p>
<div class="row">
<div class="col-sm-6 col-xs-12">
<h4>Old Design</h4>
<p><img alt="Old Design" src="https://blog.minchin.ca/images/2016/blogger-full-article.png"/></p>
</div>
<div class="col-sm-6 col-xs-12">
<h4>New Design</h4>
<p><img alt="New Design" src="https://blog.minchin.ca/images/2016/minchindotca-theme-full-article.png"/></p>
</div>
</div>
<!-- before and after pictures -->
<h3>Backup</h3>
<p>Before, I never really had a back up of my blog contents. Google didn’t seem
ready to shut down Blogger, but there’s always that fear, at a low level
anyway, in the back of my mind. Now, I have a plain text back up of everything
I’ve written to my blog.</p>
<h2>Next Steps</h2>
<p>Now that it’s done, I hope to focus more on writing. I also hope to move over a
few notes that I have written elsewhere in Markdown to the blog. There are a
few updates still needed to the theme, including <a href="https://blog.minchin.ca/2016/12/blogger-comments-exported.html">re-adding
comments</a> (completed December
29, 2016). But I very much like the look. I hope you enjoy it as much as I do!</p></body></html>Minchin dot CA Pelican Theme 1.1.0 Released2016-09-12T19:29:00-06:002016-09-12T19:29:00-06:00Wm. Minchintag:blog.minchin.ca,2016-09-12:/2016/09/minchin-dot-ca-pelican-theme-version-110-released.htmlThis update was the final touches needed to make my site go live.<html><head></head><body><p>This update was the final touches needed to make my site go live.</p>
<p>I’ve been in the process of redesigning my blog and so as part of that I’ve
tried to make as many pieces as possible installable from <code>pip</code>. To that end,
I’ve packaged my website theme and uploaded it to PyPI.</p>
<p>The theme is based on <a href="http://dandydev.net/">Daan Debie</a>‘s wonderful <a href="https://github.com/getpelican/pelican-themes/tree/master/pelican-bootstrap3">Bootstrap
3
theme</a>,
although heavily modified. Because of the heavy modifications, I’m not sure how
useful this will be to others, but I think it is still a useful
proof-of-concept as I am aware of no other Pelican themes available on PyPI.</p>
<p>To use this theme, first install it via <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.themes.minchindotca
</code></pre></div>
<p>Next, update your <code>pelicanconf.py</code> to use the theme, with its default settings:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="kn">from</span> <span class="nn">minchin.pelican.themes</span> <span class="kn">import</span> <span class="n">minchindotca</span>
<span class="n">THEME</span> <span class="o">=</span> <span class="n">minchindotca</span><span class="o">.</span><span class="n">get_path</span><span class="p">()</span>
<span class="n">BOOTSTRAP_THEME</span> <span class="o">=</span> <span class="s1">'minchindotca'</span>
<span class="n">IMAGE_PROCESS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'article-feature'</span><span class="p">:</span> <span class="p">[</span><span class="s2">"scale_in 848 848 True"</span><span class="p">],</span>
<span class="s1">'index-feature'</span><span class="p">:</span> <span class="p">[</span><span class="s2">"scale_in 263 263 True"</span><span class="p">],</span>
<span class="p">}</span>
<span class="c1"># Jijna2 filters</span>
<span class="k">def</span> <span class="nf">datetimefilter</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="s1">'%Y/%m/</span><span class="si">%d</span><span class="s1"> %H:%M'</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""convert a datetime to a different format."""</span>
<span class="k">return</span> <span class="n">value</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="nb">format</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">article_date</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""Converts a date to the format we want it displayed on the article</span>
<span class="sd"> template.</span>
<span class="sd"> """</span>
<span class="k">return</span> <span class="n">value</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'%A, %B </span><span class="si">%-d</span><span class="s1">, %Y'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">breaking_spaces</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""Converts non-breaking spaces to regular spaces."""</span>
<span class="k">return</span> <span class="n">value</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">'</span><span class="se">\u00A0</span><span class="s1">'</span><span class="p">,</span> <span class="s1">' '</span><span class="p">)</span>
<span class="n">JINJA_FILTERS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'datetimefilter'</span><span class="p">:</span> <span class="n">datetimefilter</span><span class="p">,</span>
<span class="s1">'article_date'</span><span class="p">:</span> <span class="n">article_date</span><span class="p">,</span>
<span class="s1">'breaking_spaces'</span><span class="p">:</span> <span class="n">breaking_spaces</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div>
<p>And then regenerate your site.</p>
<p>The theme depends on the pathlib library, that was introduced in Python 3.4, so
the theme won’t work on earlier versions of Python. Installing the pathlib2
library should provide the necessary functionality for the theme to work, but
this is currently untested by me.</p>
<p>The full settings are not particularly well documented (by me anyway), although
the the
<a href="https://github.com/getpelican/pelican-themes/tree/master/pelican-bootstrap3">ReadMe</a>
for the base theme goes over most of the settings.</p>
<p>For future updates, I would like to simplify the adding the required settings,
move the Jinja filters to their own package, work on the documentation of all
the various settings, and add a proper changelog.</p></body></html>Minchin dot CA Pelican Theme 1.0.0 Released2016-08-15T11:19:00-06:002016-08-15T11:19:00-06:00Wm. Minchintag:blog.minchin.ca,2016-08-15:/2016/08/minchin-dot-ca-pelican-theme-version-100-released.htmlI’ve been in the process of redesigning my blog and so as part of that I’ve
tried to make as many pieces as possible installable from <code>pip</code>. To that end,
I’ve packaged my website theme and uploaded it to PyPI.<html><head></head><body><p>I’ve been in the process of redesigning my blog and so as part of that I’ve
tried to make as many pieces as possible installable from <code>pip</code>. To that end,
I’ve packaged my website theme and uploaded it to PyPI.</p>
<p>The theme is based on <a href="http://dandydev.net/">Daan Debie</a>‘s wonderful <a href="https://github.com/getpelican/pelican-themes/tree/master/pelican-bootstrap3">Bootstrap
3
theme</a>,
although heavily modified. Because of the heavy modifications, I’m not sure how
useful this will be to others, but I think it is still a useful
proof-of-concept as I am aware of no other Pelican themes available on PyPI.</p>
<p>To use this theme, first install it via <code>pip</code>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>minchin.pelican.themes.minchindotca
</code></pre></div>
<p>Next, update your <code>pelicanconf.py</code> to use the theme, with its default settings:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pelicanconf.py</span>
<span class="kn">from</span> <span class="nn">minchin.pelican.themes</span> <span class="kn">import</span> <span class="n">minchindotca</span>
<span class="n">THEME</span> <span class="o">=</span> <span class="n">minchindotca</span><span class="o">.</span><span class="n">get_path</span><span class="p">()</span>
<span class="n">BOOTSTRAP_THEME</span> <span class="o">=</span> <span class="s1">'minchindotca'</span>
</code></pre></div>
<p>And then regenerate your site.</p>
<p>The theme depends on the pathlib library, that was introduced in Python 3.4, so
the theme won’t work on earlier versions of Python. Installing the pathlib2
library should provide the necessary functionality for the theme to work, but
this is currently untested by me.</p>
<p>The full settings are not particularly well documented (by me anyway), although
the the
<a href="https://github.com/getpelican/pelican-themes/tree/master/pelican-bootstrap3">ReadMe</a>
for the base theme goes over most of the settings.</p></body></html>Europe at Night2016-04-13T11:18:00-06:002016-04-13T11:18:00-06:00Wm. Minchintag:blog.minchin.ca,2016-04-13:/2016/04/europe-at-night.html[T]his is actually a composite produced by the National Oceanic and
Atmospheric Administration revealing a decade in European economic
development and decline.<html><head></head><body><blockquote>
<p>[T]his is actually a composite produced by the National Oceanic and
Atmospheric Administration revealing a decade in European economic
development and decline.</p>
<p>Europe at night, showing the change in illumination from 1993-2003. This data
is based on satellite observations. Lights are colour-coded. Red lights
appeared during that period. Orange and yellow areas are regions of high and
low intensity lighting respectively that increased in brightness over the ten
years. Grey areas are unchanged. Pale blue and dark blue areas are of low and
high intensity lighting that decreased in brightness. Very dark blue areas
were present in 1993 and had disappeared by 2003. Much of western and central
Europe has brightened considerably. Some North Sea gas fields closed in the period.</p>
</blockquote>
<p><em>via
<a href="https://boingboing.net/2016/04/13/map-of-europe-shows-the-lights.html">BoingBoing</a></em></p></body></html>River Through Time2016-04-05T14:14:00-06:002016-04-05T14:14:00-06:00Wm. Minchintag:blog.minchin.ca,2016-04-05:/2016/04/river-through-time.htmlBelow is an animation of a river over about 20 years. I like it because it
shows the meander of the river changing over several years, as well as cut-offs
and ox bow lakes forming. This happens for many rivers, but its’ pace is often
too slow for us to notice.<html><head></head><body><p>Below is an animation of a river over about 20 years. I like it because it
shows the meander of the river changing over several years, as well as cut-offs
and ox bow lakes forming. This happens for many rivers, but its’ pace is often
too slow for us to notice.</p>
<p><em>via <a href="http://hinderedsettling.com/2014/03/16/rivers-through-time-as-seen-in-landsat-images/">Hinderd Settleing</a></em></p></body></html>The Three Audience Truths2016-02-10T09:06:00-07:002016-02-10T09:06:00-07:00Wm. Minchintag:blog.minchin.ca,2016-02-10:/2016/02/the-three-audience-truths.html<p>This is advise for when given a speech, or otherwise speaking in public. Remember:</p>
<ol>
<li>They believe you’re the expert, so don’t tell them otherwise.</li>
<li>They want you to succeed, so they’re on your side.</li>
<li>They won’t know when you make a mistake, so don’t announce it.</li>
</ol>
<html><head></head><body><p>This is advise for when given a speech, or otherwise speaking in public. Remember:</p>
<ol>
<li>They believe you’re the expert, so don’t tell them otherwise.</li>
<li>They want you to succeed, so they’re on your side.</li>
<li>They won’t know when you make a mistake, so don’t announce it.</li>
</ol>
<p><em>from <a href="http://www.businessinsider.com/how-to-calm-nerves-before-a-speech-2016-2">Business
Insider</a>
via
<a href="http://lifehacker.com/remember-the-three-audience-truths-before-giving-a-sp-1758115278">Lifehacker</a></em></p></body></html>Sailing (Round the Globe)2015-09-20T16:52:00-06:002015-09-20T16:52:00-06:00Wm. Minchintag:blog.minchin.ca,2015-09-20:/2015/09/sailing-round-globe.htmlSo one of the things my Love and I have talked about is the idea of taking off
to a season of sailing. The ocean has a sort of primitive draw to me that is
hard to explain and not particularly rational. I have no experience sailing. In
fact, my experience on the water is limited to commercial ferries and canoeing
a couple of hours at a time.<!-- Map on Tripline --><html><head></head><body><p>So one of the things my Love and I have talked about is the idea of taking off
to a season of sailing. The ocean has a sort of primitive draw to me that is
hard to explain and not particularly rational. I have no experience sailing. In
fact, my experience on the water is limited to commercial ferries and canoeing
a couple of hours at a time.</p>
<p>To take off on a trip like this, we would need a couple of prep steps: a boat,
training in sailing, and a route would probably be good too.</p>
<p>As for timing, if we were to go in about 15 years, it might work perfect — the
kids would probably be between 10 and 17 years of age. Our other option would
be to wait until they’ve all left home, which would push it out another 10
years or so.</p>
<p>One of thing I worry about with a boat is the limited space. For the mechanics
of living, I don’t think it would be an issue, but I wonder how well several
kids would deal with each other (and me with them) in such tight quarters, with
really nowhere to go. But the bigger the boat, the more expensive it becomes too.</p>
<p>Swimming lessons for the kids growing up (and possibility for me too) take on a
whole new importance. Fishing too.</p>
<p>For a route, we could do something as simple as island hopping through the
Caribbean or in Hawai’i, but the round-the-world trips are much more fun to
muse about. So here’s a proposed round-the-world sailing trip:</p>
<ul>
<li>Fly to Vancouver, Canada, and pick up the sailboat</li>
<li>Sail down the Pacific coast of the <span class="caps">US</span>, and visit California</li>
<li>from somewhere between San Francisco and Cabo San Lucus, Mexico, sail west to
Hawai’i. I remember hearing this is about a 3 week trip in a boat. This would
probably be our longest open-water stretch.</li>
<li>visit the Hawai’ian islands</li>
<li>sail south to the South Pacific, and visit islands such as Tahiti.</li>
<li>keep sailing southwest to the land of ‘The Long White Cloud’, aka New Zealand</li>
<li>sail over to Australia. The biggest cities (and so the places to see?) are
around the southeast ‘corner’ of the country. From here, we want to sail to
the northwest ‘corner’, so we could probably go either way around, but if we
follow the east and north coasts, we’ll get to see the Great Barrier Reef</li>
<li>sail through Indonesia, towards India</li>
<li>in deciding where to go next from India, it takes some peering into the
future. I’d like to go to the Mediterranean next, via the Suez Canal. Today,
the Horn of Africa is too much of a piracy haven for my tastes. But one can
hope that in 15 years it will be sorted out. If it’s still a mess, then maybe
we sail ‘the long way round’, via the Cape of Good Hope.</li>
<li>once in the Mediterranean, I would visit the ancient roots of western
civilization — Egypt, Israel, Greece, and Rome, and possibly Carthage</li>
<li>being this close to Europe, the idea of visiting the rest of (western) Europe
is tempting. The best plan might be to dock at someplace like Marseilles and
then take the train to England, France, and Germany (Marseilles is 6 hours
from Paris by train).</li>
<li>from the Pillars of Hercules, sail southwest to the Canaries.</li>
<li>how far south we go from here depends on how quick we want to be home.
Assuming we’re still at a leisurely pace, I would head on to Brazil.</li>
<li>depending on where we might sell the boat, we might stop early from here on
out and just fly home, but barring that…</li>
<li>From Brazil, wander through the Caribbean islands</li>
<li>pay Florida a visit</li>
<li>sail up the east coast of the <span class="caps">US</span>. Stops as strike our fancy, but likely a
minimum of Washington <span class="caps">DC</span> and New York City</li>
<li>and finish up in Halifax, sell the boat, and fly home</li>
</ul>
<p>The trip seems to be about 49,300km long. Per
<a href="http://travel.stackexchange.com/questions/11518/how-fast-can-one-expect-to-travel-in-an-ocean-going-sailboat">this</a>,
a good average for a sailboat is 100 nautical miles, or 185km, per day. That
would work out to 267 days on the water, giving no time for shore leave. If
timed right (in regards to seasons and shore leave), could we pull this off
inside a year?</p>
<p>As I put together a map of the trip, I found <a href="http://www.sailforgood.org/">another
family</a> proposing something like this. They plan
to go around the southern capes of both Africa and South American, and will end
up crossing the Atlantic three times, but they do give some sense to the time
such a trip will take — 75 countries in 75 months (6 years!)</p></body></html>Colourettu 1.1.0 for Python released2015-07-20T09:25:00-06:002015-08-15T14:23:00-06:00Wm. Minchintag:blog.minchin.ca,2015-07-20:/2015/07/colourettu-1-1-0-for-python.html<p>Version 1.1.0 of <em>Colourettu</em> has been released.</p>
<p>Colourettu is a Python library I’ve written for dealing with colours and
“palettes” (groups of colours).</p>
<html><head></head><body><p>Version 1.1.0 of <em>Colourettu</em> has been released.</p>
<p>Colourettu is a Python library I’ve written for dealing with colours and
“palettes” (groups of colours).</p>
<p>A quick example:</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">colourettu</span>
<span class="n">c1</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">()</span> <span class="c1"># defaults to #FFF</span>
<span class="n">c2</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">(</span><span class="s2">"#eee"</span><span class="p">)</span> <span class="c1"># equivalent to #EEEEEE</span>
<span class="n">c3</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">(</span><span class="s2">"#456bda"</span><span class="p">)</span>
<span class="n">c4</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">([</span><span class="mi">3</span><span class="p">,</span> <span class="mi">56</span><span class="p">,</span> <span class="mi">129</span><span class="p">])</span> <span class="c1"># as an RGB tuple or list</span>
<span class="n">c5</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">((</span><span class="mi">63</span><span class="p">,</span> <span class="mi">199</span><span class="p">,</span> <span class="mi">233</span><span class="p">))</span>
<span class="n">c6</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">([</span><span class="mf">0.242</span><span class="p">,</span> <span class="mf">0.434</span><span class="p">,</span> <span class="mf">0.165</span><span class="p">],</span> <span class="n">normalized_rgb</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">all_colours</span> <span class="o">=</span> <span class="p">[</span><span class="n">c1</span><span class="p">,</span> <span class="n">c2</span><span class="p">,</span> <span class="n">c3</span><span class="p">,</span> <span class="n">c4</span><span class="p">,</span> <span class="n">c5</span><span class="p">,</span> <span class="n">c6</span><span class="p">]</span>
<span class="n">p2</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">palette</span><span class="p">()</span>
<span class="n">p2</span><span class="o">.</span><span class="n">from_list</span><span class="p">(</span><span class="n">all_colours</span><span class="p">)</span>
<span class="n">p2</span><span class="o">.</span><span class="n">to_image</span><span class="p">(</span><span class="s1">'p2.png'</span><span class="p">,</span> <span class="n">max_width</span><span class="o">=</span><span class="mi">360</span><span class="p">,</span> <span class="n">vertical</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</code></pre></div>
<div class="text-center">
<p><img alt="Colourettu p2" src="https://blog.minchin.ca/images/2015/p2.png"/></p>
</div>
<p>The easiest to install (or upgrade) <em>Colourettu</em> (assuming you already have
Python installed) is to use <em>pip</em>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>colourettu<span class="w"> </span>--upgrade
</code></pre></div>
<p>The changes for this version include a project logo (above), and the addition
of the <em>palette</em> class.</p>
<p><a href="http://minchin.ca/colourettu/"><em>Colourettu</em> documentation</a> is now online. A
<a href="http://minchin.ca/colourettu/changelog.html">full changelog</a> is online as part
of that. <a href="https://github.com/MinchinWeb/colourettu/">The code for <em>Colourettu</em></a>
is hosted on Github.</p></body></html>The American Health Care System2015-06-24T20:54:00-06:002015-06-24T20:54:00-06:00Wm. Minchintag:blog.minchin.ca,2015-06-24:/2015/06/the-american-health-care-system.htmlAll you need to know about the American health care system is that there’s a
popular <span class="caps">TV</span> series where a man turns to cooking industrial quantities of
crystal meth in order to pay his hospital bills.<html><head></head><body><blockquote>
<p>All you need to know about the American health care system is that there’s a
popular <span class="caps">TV</span> series where a man turns to cooking industrial quantities of
crystal meth in order to pay his hospital bills.</p>
</blockquote>
<p>(This is a reference to <em>Breaking Bad</em>).</p>
<p><em>via <a href="https://imgur.com/L7vMdH5">imgur</a></em></p></body></html>How to Treat Employees2015-03-31T19:17:00-06:002015-03-31T19:17:00-06:00Wm. Minchintag:blog.minchin.ca,2015-03-31:/2015/03/how-to-treat-employees.htmlAlways treat your employees exactly as you want them to treat your best customers.<html><head></head><body><blockquote>
<p>Always treat your employees exactly as you want them to treat your best customers.</p>
<p>— Stephen R. Covey</p>
</blockquote></body></html>Colourettu 1.0.0 for Python released2015-01-17T09:21:00-07:002015-08-15T14:25:00-06:00Wm. Minchintag:blog.minchin.ca,2015-01-17:/2015/01/colourettu-1-0-0-for-python.html<p>Version 1.0.0 of <em>Colourettu</em> has been released.</p>
<p>Colourettu is a Python library I’ve written for dealing with colours, and
specifically to determine the contrast between two colours.</p>
<html><head></head><body><p>Version 1.0.0 of <em>Colourettu</em> has been released.</p>
<p>Colourettu is a Python library I’ve written for dealing with colours, and
specifically to determine the contrast between two colours.</p>
<p>A quick example:</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">colourettu</span>
<span class="n">c1</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">()</span> <span class="c1"># defaults to #FFF</span>
<span class="n">c2</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">(</span><span class="s2">"#eee"</span><span class="p">)</span> <span class="c1"># equivalent to #EEEEEE</span>
<span class="n">c3</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">(</span><span class="s2">"#456bda"</span><span class="p">)</span>
<span class="n">c4</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">([</span><span class="mi">3</span><span class="p">,</span> <span class="mi">56</span><span class="p">,</span> <span class="mi">129</span><span class="p">])</span> <span class="c1"># as an RGB tuple or list</span>
<span class="n">c5</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">((</span><span class="mi">63</span><span class="p">,</span> <span class="mi">199</span><span class="p">,</span> <span class="mi">233</span><span class="p">))</span>
</code></pre></div>
<div class="highlight"><pre><span></span><code>>>> colourettu.contrast("#FFF", "#FFF") # white on white
1.0
>>> colourettu.contrast(c1, "#000") # black on white
20.999999999999996
>>> colourettu.contrast(c4, c5)
4.363552233203198
</code></pre></div>
<p>The easiest to install (or upgrade) <em>Colourettu</em> (assuming you already have
Python installed) is to use <em>pip</em>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>colourettu<span class="w"> </span>--upgrade
</code></pre></div>
<p>The biggest change with this release is that <a href="http://minchin.ca/colourettu/"><em>Colourettu</em>
documentation</a> is now online. A <a href="http://minchin.ca/colourettu/changelog.html">full
changelog</a> is online as part of
that. <a href="https://github.com/MinchinWeb/colourettu/">The code for <em>Colourettu</em></a> is
hosted on Github.</p></body></html>MetaLibrary 9 for OpenTTD released2015-01-14T22:54:00-07:002015-08-14T08:11:00-06:00Wm. Minchintag:blog.minchin.ca,2015-01-14:/2015/01/metalibrary-9-for-openttd.html<html><head></head><body><p>Version 9 of <em>MetaLibrary</em> has been released.</p>
<p>To recap, <em>MetaLibrary</em> is a collection of code I’ve written to simplify
writing an <span class="caps">AI</span> for <a href="http://www.openttd.org/">OpenTTD</a>, a remake of my childhood
favorite, Transport Tycoon.</p>
<p>Version 9 brings a couple of bug fixes in the Python build script. The
<a href="http://minchin.ca/openttd-metalibrary/">documentation for MetaLibrary</a> has
also been updated.</p>
</body></html>Colourettu 0.1.1 for Python released2014-12-11T17:20:00-07:002015-08-15T14:26:00-06:00Wm. Minchintag:blog.minchin.ca,2014-12-11:/2014/12/colourettu-0-1-1-for-python.html<p>Version 0.1.1 of <em>Colourettu</em> has been released.</p>
<p>Colourettu is a Python library I’ve written for dealing with colours, and
specifically to determine the contrast between two colours.</p>
<html><head></head><body><p>Version 0.1.1 of <em>Colourettu</em> has been released.</p>
<p>Colourettu is a Python library I’ve written for dealing with colours, and
specifically to determine the contrast between two colours.</p>
<p>A quick example:</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">colourettu</span>
<span class="n">c1</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">()</span> <span class="c1"># defaults to #FFF</span>
<span class="n">c2</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">(</span><span class="s2">"#eee"</span><span class="p">)</span> <span class="c1"># equivalent to #EEEEEE</span>
<span class="n">c3</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">(</span><span class="s2">"#456bda"</span><span class="p">)</span>
<span class="n">c4</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">([</span><span class="mi">3</span><span class="p">,</span> <span class="mi">56</span><span class="p">,</span> <span class="mi">129</span><span class="p">])</span> <span class="c1"># as an RGB tuple or list</span>
<span class="n">c5</span> <span class="o">=</span> <span class="n">colourettu</span><span class="o">.</span><span class="n">colour</span><span class="p">((</span><span class="mi">63</span><span class="p">,</span> <span class="mi">199</span><span class="p">,</span> <span class="mi">233</span><span class="p">))</span>
</code></pre></div>
<div class="highlight"><pre><span></span><code>>>> colourettu.contrast("#FFF", "#FFF") # white on white
1.0
>>> colourettu.contrast(c1, "#000") # black on white
20.999999999999996
>>> colourettu.contrast(c4, c5)
4.363552233203198
</code></pre></div>
<p>The easiest to install <em>Colourettu</em> (assuming you already have Python
installed) is to use <em>pip</em>:</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>colourettu
</code></pre></div>
<p>This is the first release of this library. <a href="https://github.com/MinchinWeb/colourettu/">The code for
<em>Colourettu</em></a> is hosted on Github.</p></body></html>Failure or Blame2014-10-25T22:23:00-06:002014-10-25T22:23:00-06:00Wm. Minchintag:blog.minchin.ca,2014-10-25:/2014/10/failure-or-blame.htmlIf you work in an organization, the underlying rule is simple: People are not
afraid of failure, they’re afraid of blame.<html><head></head><body><blockquote>
<p>If you work in an organization, the underlying rule is simple: People are not
afraid of failure, they’re afraid of blame.</p>
<p>— Seth Godin</p>
</blockquote>
<p><em>via <a href="http://sethgodin.typepad.com/seths_blog/2014/10/no-one-to-say-no.html">Seth Godin</a></em></p></body></html>NSA Old-School Spying2014-04-02T09:34:00-06:002014-04-02T09:34:00-06:00Wm. Minchintag:blog.minchin.ca,2014-04-02:/2014/04/nsa-old-school-spying.htmlWhile the recent revelations from Snowden have put a spotlight on the efforts
of the <span class="caps">NSA</span> to spy on seemingly everything, one of the surprising results is
that those that were labelled ‘paranoid’ and dismissed in years past no longer
seem paranoid <em>enough</em>.<html><head></head><body><p>While the recent revelations from Snowden have put a spotlight on the efforts
of the <span class="caps">NSA</span> to spy on seemingly everything, one of the surprising results is
that those that were labelled ‘paranoid’ and dismissed in years past no longer
seem paranoid <em>enough</em>.</p>
<p>I give an example of a joke from 1995 (so almost 20 years ago):</p>
<blockquote>
<p>The <span class="caps">NSA</span> is now funding research not only in cryptography, but in all areas of
advanced mathematics. If you’d like a circular describing these new research
opportunities, just pick up your phone, call your mother, and ask for one.</p>
</blockquote>
<p><em>via the <a href="http://home.snafu.de/tilman/2001.txt">Usenet archives</a></em></p></body></html>MetaLibrary 8 for OpenTTD released2014-03-10T21:12:00-06:002015-08-14T08:11:00-06:00Wm. Minchintag:blog.minchin.ca,2014-03-10:/2014/03/metalibrary-8-for-openttd.html<p>Quick on the heals of the <a href="{filename}20140301-metalibrary-version-7-for-openttd-released.md">release of version
7</a>, version 8
of <em>MetaLibrary</em> has been released.</p>
<p>To recap, <em>MetaLibrary</em> is a collection of code I’ve written to simplify
writing an <span class="caps">AI</span> for <a href="http://www.openttd.org/">OpenTTD</a>, a remake of my childhood
favorite, Transport Tycoon.</p>
<p>Version 8 brings a 9-30x improvement in the speed of <em>Lakes</em>, and by extension,
a speed-up in the Ship Pathfinder. And that’s all for now; I figured that would
be enough. The <a href="http://minchin.ca/openttd-metalibrary/">documentation for
MetaLibrary</a> has also been updated.</p>
<html><head></head><body><p>Quick on the heals of the <a href="https://blog.minchin.ca/2014/03/metalibrary-7-for-openttd.html">release of version
7</a>, version 8
of <em>MetaLibrary</em> has been released.</p>
<p>To recap, <em>MetaLibrary</em> is a collection of code I’ve written to simplify
writing an <span class="caps">AI</span> for <a href="http://www.openttd.org/">OpenTTD</a>, a remake of my childhood
favorite, Transport Tycoon.</p>
<p>Version 8 brings a 9-30x improvement in the speed of <em>Lakes</em>, and by extension,
a speed-up in the Ship Pathfinder. And that’s all for now; I figured that would
be enough. The <a href="http://minchin.ca/openttd-metalibrary/">documentation for
MetaLibrary</a> has also been updated.</p>
</body></html>MetaLibrary 7 for OpenTTD released2014-03-01T22:43:00-07:002015-08-14T08:11:00-06:00Wm. Minchintag:blog.minchin.ca,2014-03-01:/2014/03/metalibrary-7-for-openttd.html<p><em><a href="http://minchin.ca/openttd-metalibrary/">MetaLibrary</a></em> moves forward again!</p>
<p>Building off my recent announcement that <a href="{filename}20140201-metalibrary-documentation-live.md">documentation is now
live</a>, I have completed a
fairly major update to <em>MetaLibrary</em>.</p>
<p>To recap, <em>MetaLibrary</em> is a collection of code I’ve written to simplify
writing an <span class="caps">AI</span> for <a href="http://www.openttd.org/">OpenTTD</a>, a remake of my childhood
favorite, Transport Tycoon.</p>
<html><head></head><body><p><em><a href="http://minchin.ca/openttd-metalibrary/">MetaLibrary</a></em> moves forward again!</p>
<p>Building off my recent announcement that <a href="https://blog.minchin.ca/2014/02/metalibrary-documentation-live.html">documentation is now
live</a>, I have completed a
fairly major update to <em>MetaLibrary</em>.</p>
<p>To recap, <em>MetaLibrary</em> is a collection of code I’ve written to simplify
writing an <span class="caps">AI</span> for <a href="http://www.openttd.org/">OpenTTD</a>, a remake of my childhood
favorite, Transport Tycoon.</p>
<p>This update particularly focused on adding robustness to the Ship Pathfinder.
To that end, I wrote <em>Lakes</em> as a replacement for <em>Waterbody Check</em>. Both serve
the same purpose of determining if two tiles are within the same waterbody,
i.e. if a ship could sail between the two tiles. What sets <em>Lakes</em> apart from
its predecessor is <em>Lakes</em> maintains a ‘memory’ of the tiles it has already
considered, therefore increasing the speed of subsequent requests. Subsequent
requests on an already checked route run in the order of 4 ticks, or about half
of a game day.</p>
<p><em>Lakes</em> has already seen a ten-fold increase in speed, although it, at least
for the first run, is slower than the old <em>Waterbody</em> <em>Check</em>. New versions of
<em>MetaLibrary</em> will likely continue to see speed ups and tweaks to <em>Lakes</em>.</p>
<p>Other improvements include:</p>
<ul>
<li>Ship Pathfinder now uses Lakes rather than WaterBodyCheck</li>
<li>Ship Pathfinder now makes sure every point is in the same waterbody before
adding it to the path</li>
<li>WaterBodyCheck is now deprecated in favour of Lakes</li>
<li><a href="http://minchin.ca/openttd-metalibrary/">Documentation for MetaLibrary</a> is
now online at Minchin.ca</li>
<li>Fix array creation bugs in Array.Create2D(), Array.Create3D()</li>
<li>Added <code>Array.RemoveDuplicates(Array)</code></li>
<li>Added <code>Array.ToAIList(Array)</code></li>
<li>Added <code>Extras.MinDistance(TileID, TargetArray)</code>; can be used as a valuator</li>
<li>Split Constants from Extras (file only, function access remains the same)</li>
<li>Split Industry from Extras (file only, function access remains the same)</li>
<li>Split Station from Extras (file only, function access remains the same)</li>
<li>Bumped maximum Log <code>Debug_Level</code> to 8</li>
<li>Added separate Changelog file</li>
<li>Rename <code>Readme.txt</code> to <code>Readme.md</code></li>
<li>Update requirement to Fibonacci Heap, v.3</li>
<li>Automated creation of tar files for upload to BaNaNaS</li>
<li>Automated translation for the Game Script version</li>
</ul>
<p>The <a href="http://www.tt-forums.net/viewtopic.php?f=65&t=57903">forum thread for <em>MetaLibrary</em>
discussion</a> is on
<span class="caps">TT</span>-Forums, the <a href="https://github.com/MinchinWeb/openttd-metalibrary/">code for
<em>MetaLibrary</em></a> is available
on GitHub, the <a href="http://minchin.ca/openttd-metalibrary/">documentation for
<em>MetaLibrary</em></a> is now online at
Minchin.ca, and <em>MetaLibrary</em> can be downloaded from BaNaNaS (<a href="http://bananas.openttd.org/en/ailibrary/"><span class="caps">AI</span>
version</a>, <a href="http://bananas.openttd.org/en/gslibrary/"><span class="caps">GS</span>
version</a>) or the in-game downloader (recommended).</p></body></html>Birkenhead’s of Backford2014-02-12T10:44:00-07:002024-03-18T21:51:00-06:00Wm. Minchintag:blog.minchin.ca,2014-02-12:/2014/02/birkenheads-of-backford.htmlI have been continuing my research of the Bunbury line. For the 18th century
and earlier, <em>Burke’s Peerage</em> has proved incredibly useful. As <em>Burke’s</em> is
serves mainly for showing how someone inherited their title or lands, it tends
to be patrilineal (meaning descent is calculated through men). However, the
wives are often well noted, including the wife’s father. Such is the link
between the Bunbury’s and the Birkenhead’s.<html><head></head><body><p>I have been continuing my research of the Bunbury line. For the 18th century
and earlier, <em>Burke’s Peerage</em> has proved incredibly useful. As <em>Burke’s</em> is
serves mainly for showing how someone inherited their title or lands, it tends
to be patrilineal (meaning descent is calculated through men). However, the
wives are often well noted, including the wife’s father. Such is the link
between the Bunbury’s and the Birkenhead’s.</p>
<blockquote>
<p>v. <a href="https://genealogy.minchin.ca/profiles/i9874/">Eleanor</a>, <em>b.</em> in 1605,
<em>m.</em> to <a href="https://genealogy.minchin.ca/profiles/i2155/">Thomas</a>, second son
of <a href="https://genealogy.minchin.ca/profiles/i2156/">Sir Henry Bunbury, of
Stanney</a>.</p>
</blockquote>
<p>So this their marriage would fall about 60 years before the Bunbury family
moved from England to Ireland. So I followed up Eleanor’s ancestors and family,
and added 100+ Birkenhead’s to my family tree. The Birkenhead’s go back to four
more generations to <a href="https://genealogy.minchin.ca/profiles/i10122/">Adam Birkenhead, of
Huxley</a>, who lived during the
reigns of King Henry <span class="caps">VII</span> and King Henry <span class="caps">VIII</span> (Henry <span class="caps">VII</span> reigned from 1485-1509.
Henry <span class="caps">VIII</span> reigned from 1509-1547.) There is a note in <em>Burke’s</em> that Adam
Birkenhead “deriv[ed] from John De Birkenhead, who flourished in the reign of
Edward <span class="caps">III</span>.” Edward <span class="caps">III</span> reigned from 1327 - 1377, so this would push the line
back another 150 years or so, but I’ll have to find the names in another place.</p>
<p>Interestingly enough, Eleanor’s grandmother was <a href="https://genealogy.minchin.ca/profiles/i9867/">Elizabeth
Bunbury</a>, tying back in to the
Bunbury line.</p></body></html>The Transformation of Facebook2014-02-04T11:44:00-07:002014-02-04T11:44:00-07:00Wm. Minchintag:blog.minchin.ca,2014-02-04:/2014/02/the-transformation-of-facebook.htmlWhile much has been written about Facebook, this isn’t about how Facebook as a
website or a company has changed, but rather how my relationship with it has
changed. I am particularly inspired to write this this morning after reading
<a href="http://arstechnica.com/business/2014/02/how-we-ruin-social-networks-facebook-specifically/">this
article</a>
by Cassey Johnston in Ars Technica.<html><head></head><body><p>While much has been written about Facebook, this isn’t about how Facebook as a
website or a company has changed, but rather how my relationship with it has
changed. I am particularly inspired to write this this morning after reading
<a href="http://arstechnica.com/business/2014/02/how-we-ruin-social-networks-facebook-specifically/">this
article</a>
by Cassey Johnston in Ars Technica.</p>
<p>It sounds like Cassey got on Facebook a little before me. I remember the
announcement that Facebook would accept anyone with a .edu email address.
Seeing as <a href="http://en.wikipedia.org/wiki/.edu">only American schools could get .edu
domains</a>, this became another case of “only
the Americans get cool tech toys” (a far too common problem when living in
Canada). It was maybe a year later that Facebook opened up to “foreign”
universities. This would have been about 2006. At first I ignored it - I felt
like I could keep track of my friends well enough through email and <span class="caps">MSN</span>
Messenger. I eventually joined because I got an invite from a friend (Isaac)
who was otherwise <span class="caps">MIA</span>.</p>
<p>The first bit was fun. I’d become friends with someone on Facebook, sift
through their friends list to find more people I knew, and invite them to be my
‘friend.’ Lather, Rinse, Repeat. I joined March 27, 2007, and become ‘friends’
with 14 people that first day. My Facebook friends were people I saw frequently
in real life, or wished I did, and so I had no problem sharing my thoughts and
photos of my activities with them. No, I didn’t share my “deepest, darkest
secrets”, but the sort of things I’d share with a friend at a party - personal
triumphs, reflections on life, half formed ideas, and the like.</p>
<p>And I ended up at a number of cool parties that I was welcome to attend, but
had not been personally invited to, because ‘everyone will see it on Facebook.’</p>
<p>But then something changed. Part of it was Facebook, part of it was me. The
biggest change on the Facebook side was the change from a sort of ‘private
club’ to a publicly available repertoire tied to my name. I did what I could to
keep my profile locked down and out of (Google) sight, but I no longer trusted
Facebook really wanted to keep anything private I didn’t want shared with the
world, and Facebook was just waiting for me to mess up or ignore some change in
policy to make everything on my profile visible to the world.</p>
<p>I wasn’t worried about people finding me via Google, but rather those people
(particularly potential employers) making a snap judgement based on a
five-year-old photo taken out of context. And this wasn’t a hypothetical
concern because I’d done it.</p>
<p>When trying to find a job, I’d be writing a cover letter and trying to
determine whether to address “Jamie” as ‘Mr.’ or ‘Mrs.’ and so to Google I
would go. In the first page of results would be their (public) Facebook profile
which would have their profile picture (solving the ‘Mr’ or ‘Mrs’ questions),
but also pictures of their wife and two kids, plus the high school they went to
and the year they graduated, and sometimes pictures of their last holiday in
Mexico. Creepy.</p>
<p>On my own side, I grew up a little and life changed. I started seriously
dating, and then got engaged and then married. As our relationship progressed
and we started spending more and more time together, my need for Facebook
decreased. I was less interested in going to parties to meet people. Facebook
was useless as a list of possible dates. And I didn’t need Facebook to
communicate with my date. And when engaged and first married, your interest in
the outsider world drops considerably.</p>
<p>So, like many other users, Facebook does seem like either “a chore or a
timesink,” depending on the day. The biggest reason I keep it is because it’s
easier that trying to keep an email list for 400 up-to-date and makes
communication relatively painless for me and my Facebook ‘friends.’ Things that
I want to say and have permanence, I post elsewhere, like here on my blog,
which is older than my Facebook profile.</p>
<p>Over time, how I’ve used Facebook has changed many times. Will it change again,
and what will that mean? I’m not sure; only time will tell….</p></body></html>MetaLibrary Documentation Live2014-02-01T18:13:00-07:002014-02-01T18:13:00-07:00Wm. Minchintag:blog.minchin.ca,2014-02-01:/2014/02/metalibrary-documentation-live.htmlOver the last four years, I’ve been working on an <span class="caps">AI</span> called WmDOT for OpenTTD,
a remake of the classic Transport Tycoon, a beloved game from my childhood. To
support WmDOT, I’ve built a library of support functions. Over the last three
days, I have written up documentation for the included functions and the result
can be seen live at my site at <a href="http://minchin.ca/openttd-metalibrary/">http://minchin.ca/openttd-metalibrary/</a> I’m
excited to have it up. I hope it will help others make use of my library.<html><head></head><body><p>Over the last four years, I’ve been working on an <span class="caps">AI</span> called WmDOT for OpenTTD,
a remake of the classic Transport Tycoon, a beloved game from my childhood. To
support WmDOT, I’ve built a library of support functions. Over the last three
days, I have written up documentation for the included functions and the result
can be seen live at my site at <a href="http://minchin.ca/openttd-metalibrary/">http://minchin.ca/openttd-metalibrary/</a> I’m
excited to have it up. I hope it will help others make use of my library.</p>
<p>Metalibrary is currently at version 6.</p></body></html>Alberta Bankruptcy Exceptions2014-01-03T16:48:00-07:002014-01-03T16:48:00-07:00Wm. Minchintag:blog.minchin.ca,2014-01-03:/2014/01/alberta-bankruptcy-exceptions.htmlOut of curiosity, I looked up the Alberta Bankruptcy Exceptions. In a nutshell,
this is what you can keep even if you declare bankruptcy:<html><head></head><body><p>Out of curiosity, I looked up the Alberta Bankruptcy Exceptions. In a nutshell,
this is what you can keep even if you declare bankruptcy:</p>
<blockquote>
<ol>
<li>Property you hold in trust for other people</li>
<li>Food: 12 months’ supply.</li>
<li>Clothing: up to $4,000.</li>
<li>Household furniture and appliances: up to $4,000</li>
<li>One motor vehicle; up to $5,000</li>
<li>Health aids: no dollar limit.</li>
<li>Tools of your trade: up to $10,000.</li>
<li>Farm property: requirements for 12 months operations (if debtor’s primary
income is from farming operations)</li>
<li>Principal residence: up to $40,000, reduced to your share if you are a co-owner.</li>
<li>Farm land: up to 160 acres (if the debtor’s principal residence is
located on that 160 acres and that the 160 acres is part of the debtor’s farm)</li>
<li>Social allowance, handicap benefit or a widow’s pension if the proceeds
from the payment are not intermingled with your other funds.</li>
<li>Registered Retirement Savings Plans (RRSPs), Registered Retirement Income
Funds (RRIFs), Registered Disability Savings Plans (RDSPs) and Deferred
Profit Sharing Plans (DPSPs). (except for contributions made in the year
preceding bankruptcy).</li>
</ol>
<p>Student loans are now dischargeable in a bankruptcy that occurs once a
student has been out of school for seven years. A bankrupt loses their tax
refund for the entire year of bankruptcy.</p>
<p>(<a href="http://www.bankruptcy-canada.ca/what-i-keep-or-lose-in-bankruptcy-in-canada/bankruptcy-exemptions-in-canada.htm">Source</a>
<span class="amp">&</span>
<a href="http://alberta.bankruptcycanada.com/alberta-bankruptcy-exemptions.htm">Source</a>)</p>
</blockquote>
<p>Some thoughts:</p>
<ul>
<li>I suppose one case where you might get close to ‘abusing’ this is creating a
contingency plan for if your business debts that go south and you become
personally liable. Proper legal structuring can help, but it’s not uncommon
for mortgages for revenue properties (rentals) to ask for a personal
guarantee. That said, I imagine many of the people going through bankruptcy
are happy to be able to keep anything at all.</li>
<li>It is good to know that property held in trust is exempt. So if you’re saving
money for the kids, get it out of your bank account and put it in trust to
them (legally)!</li>
<li>Including food on this list is interesting, only because very few (any?)
other jurisdictions include it. If you ever wanted a reason for keeping food
storage around, here it is! I suppose part of this too is no one wants to try
to sell your “used” food at garage sale prices.</li>
<li>It’s also good to know that registered retirement savings are protected too.
In Canada, it’s pretty hard to access this money before you’re actually
retirement age without significant tax costs, but it’s still a fairly
significant protection. Note that <span class="caps">TFSA</span>’s (which aren’t retirement savings
anyway) aren’t protected.</li>
<li>I think the most interesting discussion is the house exemption. Some states
have very high ‘homestead’ exceptions - Florida’s, for example, is unlimited
in value, although limited in acreage. The long and short of it is I don’t
think many people will keep their house with the $40,000 exception. It is a
reasonable question whether the exception should be big enough to allow
people to keep their homes. Not having to move could provide significant
emotional stability at a time of great uncertainty. However, for many people,
their home likely represents their largest asset which might to sold to make
their creditors whole (or at least closer to whole), and the mortgage against
their home is probably their biggest liability. So I guess the note here is
you and your home are probably going to be separated as part of the
bankruptcy process.</li>
<li>Student loans are an interesting note. In the <span class="caps">US</span>, student loans are
non-dischargeable in bankruptcy (meaning if you declare bankruptcy, you still
have to pay your student loans). However, 7 years in a long time. In general,
Canadian student loans are generally amortized over 10 years, with the option
to extend that to 15 years. So by the time that you might be able to
discharge your student loans, you’ve already paid 50-70+% of them off.</li>
<li>I never want to deal with bankruptcy!</li>
</ul></body></html>Scientific Poetry2013-12-30T19:29:00-07:002013-12-30T19:29:00-07:00Wm. Minchintag:blog.minchin.ca,2013-12-30:/2013/12/scientific-poetry.html<p>I came across this little ditty and got a kick out of it. Hope it makes you smile.</p>
<blockquote>
<p>A friend who’s in liquor production,<br>
Has a still of astounding construction,<br>
The alcohol boils,<br>
Through old magnet coils,<br>
He says that it’s proof by induction</p>
</blockquote>
<html><head></head><body><p>I came across this little ditty and got a kick out of it. Hope it makes you smile.</p>
<blockquote>
<p>A friend who’s in liquor production,<br/>
Has a still of astounding construction,<br/>
The alcohol boils,<br/>
Through old magnet coils,<br/>
He says that it’s proof by induction</p>
</blockquote>
<p><em>Via <a href="http://boingboing.net/2013/12/29/scientists-favourite-jokes.html">Boing
Boing</a>.</em></p></body></html>Genealogy is Up!2013-12-18T20:31:00-07:002024-03-18T22:10:00-06:00Wm. Minchintag:blog.minchin.ca,2013-12-18:/2013/12/genealogy-is-up.htmlMany years ago when I first bought <a href="http://minchin.ca/">minchin.ca</a>, I thought
it would be neat to put my genealogy online. This stemmed, in part, from the
fact that most of the genealogy research I was doing was online. But it’s taken
a long time to figure out how to generate the webpages a way I wanted. I wanted
a website that I could update without the links changing, and a way to hide the
information about living people. Just this last week I found <em>Adam</em>, an online
service hosted by Tim Forsythe, that will do that for me. So now my genealogy
is up! Check it out at
<del markdown=1><a href="http://www.minchin.ca/genealogy/">minchin.ca/genealogy/</a></del>
<a href="https://genealogy.minchin.ca/">genealogy.minchin.ca</a>.<html><head></head><body><p>Many years ago when I first bought <a href="http://minchin.ca/">minchin.ca</a>, I thought
it would be neat to put my genealogy online. This stemmed, in part, from the
fact that most of the genealogy research I was doing was online. But it’s taken
a long time to figure out how to generate the webpages a way I wanted. I wanted
a website that I could update without the links changing, and a way to hide the
information about living people. Just this last week I found <em>Adam</em>, an online
service hosted by Tim Forsythe, that will do that for me. So now my genealogy
is up! Check it out at
<del markdown="1"><a href="http://www.minchin.ca/genealogy/">minchin.ca/genealogy/</a></del>
<a href="https://genealogy.minchin.ca/">genealogy.minchin.ca</a>.</p>
<p>My genealogy remains a work in progress. If there are mistakes, let me know so
I can fix them. If we’re related and long lost cousins, I’d love to hear from you!</p>
<h2>Update (July 26, 2016)</h2>
<p><em>Adam</em> is no longer online, but Tim has continued to improve it and has bundled
most (all?) of it’s functionality into
<em><a href="http://timforsythe.com/gigatrees/">Gigatrees</a></em>, a downloadable executable for
Windows. I am continuing to use it to generate my genealogy website.</p></body></html>Real Men2013-08-28T12:01:00-06:002013-08-28T12:01:00-06:00Wm. Minchintag:blog.minchin.ca,2013-08-28:/2013/08/real-men.html<blockquote>
<p>Men are loyal. Men are honest. Men respect and honor women. A man goes out
and finds one woman, and he vows to protect and love her for the rest of his
life. A man would never betray that vow. Even the weakest and most cowardly
man — if he is a man at all — would die for the woman he loves….</p>
<p>You don’t have to be “cool” or athletic. You don’t have to play guitar or fix
cars. These are all fine things, but they don’t define a man. A man is
defined by how he treats women, by how he keeps his promises, and by how he
protects and serves the ones he loves. That’s what makes a man a man.</p>
</blockquote>
<html><head></head><body><blockquote>
<p>Men are loyal. Men are honest. Men respect and honor women. A man goes out
and finds one woman, and he vows to protect and love her for the rest of his
life. A man would never betray that vow. Even the weakest and most cowardly
man — if he is a man at all — would die for the woman he loves….</p>
<p>You don’t have to be “cool” or athletic. You don’t have to play guitar or fix
cars. These are all fine things, but they don’t define a man. A man is
defined by how he treats women, by how he keeps his promises, and by how he
protects and serves the ones he loves. That’s what makes a man a man.</p>
</blockquote>
<p><em>via <a href="http://themattwalshblog.com/2013/08/28/dear-son-dont-let-robin-thicke-be-a-lesson-to-you/">The Matt Walsh Blog</a></em></p></body></html>World from Space, Over the Year2013-08-14T12:02:00-06:002013-08-14T12:02:00-06:00Wm. Minchintag:blog.minchin.ca,2013-08-14:/2013/08/world-from-space-over-the-year.htmlThis <span class="caps">NASA</span> moving image, recorded by satellite over a full year as part of
their Blue Marble Project, shows the ebb and flow of the seasons and vegetation.<html><head></head><body><blockquote>
<p>This <span class="caps">NASA</span> moving image, recorded by satellite over a full year as part of
their Blue Marble Project, shows the ebb and flow of the seasons and vegetation.</p>
</blockquote>
<p><em>via <a href="http://www.washingtonpost.com/blogs/worldviews/wp/2013/08/12/40-maps-that-explain-the-world/">The Washington Post</a></em></p></body></html>Plead the Fifth2013-08-09T12:00:00-06:002013-08-09T12:00:00-06:00Wm. Minchintag:blog.minchin.ca,2013-08-09:/2013/08/plead-the-fifth.htmlA
<a href="http://priceonomics.com/how-sergey-aleynikov-learned-never-to-talk-to-the/">reminder</a>
that the odds are so stacked against you, you probably should just plead the
fifth (i.e. stay silent) every time you have to talk to the cops. It’s a
combination of more crimes than the (<span class="caps">US</span>) Federal government can count (and thus
the very real possibility you’re committed a crime unknowingly), plus the
ability of cops to take your statements out of context and spin them against
you. Plus, you avoid the possibility of a providing a false confession —
confessing to a crime you didn’t commit, sometimes just to get the cops to
leave you alone.<html><head></head><body><p>A
<a href="http://priceonomics.com/how-sergey-aleynikov-learned-never-to-talk-to-the/">reminder</a>
that the odds are so stacked against you, you probably should just plead the
fifth (i.e. stay silent) every time you have to talk to the cops. It’s a
combination of more crimes than the (<span class="caps">US</span>) Federal government can count (and thus
the very real possibility you’re committed a crime unknowingly), plus the
ability of cops to take your statements out of context and spin them against
you. Plus, you avoid the possibility of a providing a false confession —
confessing to a crime you didn’t commit, sometimes just to get the cops to
leave you alone.</p>
<p>From the highest level of the <span class="caps">US</span> Court system:</p>
<blockquote>
<p>In 1956, the Supreme Court noted that “Too many, even those who should be
better advised, view [the 5th Amendment] as a shelter for wrongdoers. They
too readily assume that those who invoke it are either guilty of crime or
commit perjury and claim the privilege.” A more recent case elaborated, “One
of the fifth amendment’s basic functions is to protect innocent men who
otherwise might be ensnared by ambiguous circumstances. Truthful responses of
an innocent witness, as well as those of a wrongdoer, may provide the
government with incriminating evidence from the speaker’s own mouth.”</p>
</blockquote>
<p><em>via <a href="http://priceonomics.com/how-sergey-aleynikov-learned-never-to-talk-to-the/">Priceonomics</a></em></p></body></html>Problems with PRISM (and the need for Privacy)2013-07-13T18:50:00-06:002013-11-22T18:59:00-07:00Wm. Minchintag:blog.minchin.ca,2013-07-13:/2013/07/problems-with-prism-and-the-need-for-privacy.htmlRecently, American Edward Snowden exposed a ton of information about what
information the <span class="caps">US</span> government, via the <span class="caps">NSA</span>, is collecting off the internet. The
<span class="caps">NSA</span>, naturally, had given anything but a straight answer. It seems the <span class="caps">NSA</span>
program exists in some form, but many details remain murky. No matter, Edward
said he wanted to have a discussion about surveillance, and perhaps privacy,
and that is what this post is really about.<html><head></head><body><p>Recently, American Edward Snowden exposed a ton of information about what
information the <span class="caps">US</span> government, via the <span class="caps">NSA</span>, is collecting off the internet. The
<span class="caps">NSA</span>, naturally, had given anything but a straight answer. It seems the <span class="caps">NSA</span>
program exists in some form, but many details remain murky. No matter, Edward
said he wanted to have a discussion about surveillance, and perhaps privacy,
and that is what this post is really about.</p>
<p>I’ve always been somewhat aware of managing my “internet privacy.” When I
started this blog in 2006, I never did put my real name on it. I realized,
thanks to the perfect memory of Google, there would be no way to undo such a
declaration, once made. My fear was never about being tied to my ideas, but
rather becoming unable to change my mind in the coming years.</p>
<p>Even so, I don’t fear the government connecting me to this blog; in fact, I
imagine it would be rather trivial for them to do so. No, my concern is what
happens next, when this blog’s authorship is added to the millions (or
trillions) of other pieces of information the government has collected, and
someone is tasked with giving “meaning” to the collected data.</p>
<p>It’s worth taking a few moments to discuss what privacy is and isn’t. First
off, privacy is not the same thing as secrecy. Something secret is almost
always private, but this is by no means a requirement. It’s also worth noting
that much of what is privacy is maintained by way of social norms. Consider a
“Private Event.” What makes it private is not that no one knows about in (that
would be secrecy), but rather that the host gets to decide who to invite and
who not to, and who to let in and who to turn away. Even if the event ends up
in the newspaper and “everybody” knows about it, the host still has these
powers to decide who is admitted. Thus, a lot of my privacy is about my choices
and the social norms that encourage others to respect my choices.</p>
<p>Further, <a href="https://medium.com/i-m-h-o/c7b9caadfc67">privacy is considered a sign of a civilized
society</a>.</p>
<p>With that out of the way, let’s talk a little about how someone at the <span class="caps">NSA</span>
might give “meaning” to all the collected data.</p>
<p>First, let’s talk a little bit about statistics. If you read the fine print on
most surveys in the newspaper, you’ll notice the phrase “19 times out of 20”
after the results, or something like it. This is a statistician’s way of
hedging his bet. He is saying that his results are only going to be right 19
times out of 20, or 95% of the time. For telephone polls asking who you’ll vote
for, this is probably fine; indeed having the telephone polls wrong from time
to time makes for much more interesting election day coverage. But when the <span class="caps">NSA</span>
wants to pick out “criminals,” I hope they would use something better than a
computer program that is wrong 5% of the time.</p>
<p>Those incorrectly selected as “of interest” are a known problem in statistics
called “false positives”. Similarly, those that the system should catch but
misses are called “false negatives”. Let’s use some numbers to get an idea of
how bad this problem of false positives is. Let’s assume that the <span class="caps">NSA</span> has a
super good model, one that is right 99.7% the time. Let’s further assume that
the <span class="caps">NSA</span> is considering everyone in the United States (population <a href="http://en.wikipedia.org/w/index.php?title=Demographics_of_the_United_States&oldid=575445593">317
million</a>)
and trying to find all the Americans on the terrorism watch list (<a href="http://en.wikipedia.org/w/index.php?title=Terrorist_Screening_Database&oldid=564635829">5% of
875,000</a>,
so about 43,750 people). Our model would flag about 994,488 people across the
<span class="caps">US</span>, of which 950,869 would be false positives (i.e. innocent people). That
means even if you’re flagged, the chances that you’re innocent are still over
96%! Now an agent had to track down leads that are innocent 24 times out of 25.
I think that would be a rather dismal job….</p>
<p>An other important distinction to make with statistics is they show correlation
(A tends to happen at the same time as B), rather than causation (B happens as
a result of A happening). A generous example of this is the “proof” that <a href="http://www.venganza.org/about/open-letter/">fewer
pirates is causing global warming</a>!
This is farcical on the face of it because we recognize that there is nothing
about the nature of pirates to suggest they can influence the global climate.
Will the analysis on those flagged above be complete enough to actually
establish causation between the collected facts and the supposed crimes, or
will they be as connected as pirates and hot summers?</p>
<p>Another part of the problem is the <span class="caps">NSA</span> data collection program relies on the
“third party doctrine” and the “business record exemption.” What these two
things together try and do is make anything not a secret not private. As
already explained, I don’t feel secrecy should be required to have something
private. According to the American government, anything I share with a third
party I no longer have a reasonable exception of privacy in. Do we need to send
the <span class="caps">NSA</span> back to elementary school (kindergarten?) so they can learn how one is
to keep a secret?</p>
<p>But the biggest problem of all, in my opinion, actually has nothing at all to
do with the <span class="caps">NSA</span>. Instead, it has to do with the American justice system. It is
the fact that the American justice system has become unassailable and
unquestionable. Consider that <a href="http://www.cato.org/publications/commentary/devils-bargain-how-plea-agreements-never-contemplated-framers-undermine-justice">95% of charges are dealt with using plea
bargains</a>,
and so never go to trial. The American constitute boldly states “Trial of all
Crimes, except in Cases of Impeachment, shall be by Jury,” but a trial before
one’s peers had become an American constitutional right in name only. Faced
with a plea bargain, the local prosecutor has decided you’re guilty and fixed
your sentence. Of the cases that do go to trial, <a href="http://open.salon.com/blog/barry60x/2012/07/24/john_edwards_roger_clemens_and_americas_other_one_percent">about 2/3 end in
convictions</a>
anyway, and with a harsher sentence than is typically offered at the plea
bargain stage. The other problem is that going to trial is incredibly
expensive. Even if you’re rich, that doesn’t mean that you’ll have money
available to defend yourself; the government can simply declare your wealth
“the proceeds of crime” and confiscate it all before the trial has begun.
Consider the case of <a href="http://www.listener.co.nz/commentary/the-internaut/kim-dotcom-megaupload-new-zealand-timeline/">Kim
Dotcom</a>
who had hundreds of millions of dollars seized and has had to convince the
court to release over $2.7 million for legal fees. It becomes a game where the
only winning move is not to play, but that isn’t really a choice you get to make.</p>
<p>So let us return to the basic principals: innocent under proven guilty, a truly
accessible justice system, and privacy is a sign of a civilized society. May we
all live many more years in such a society.</p></body></html>Alberta Yellows — Photo 100 — Project 3652012-11-24T20:19:00-07:002012-11-24T20:19:00-07:00Wm. Minchintag:blog.minchin.ca,2012-11-24:/2012/11/alberta-yellows-photo-100-project-365.htmlAlberta is amazing for its landscapes. Much of it is flat, and combined with
roads surveyed before anyone lived there on even mile spacing, you can get
roads that seem to go on forever. This picture was take in October
(Thanksgiving weekend, actually) and so the grass was basically dead but the
snow had yet to come, leaving this beautiful palette of yellows.<html><head></head><body><p>Alberta is amazing for its landscapes. Much of it is flat, and combined with
roads surveyed before anyone lived there on even mile spacing, you can get
roads that seem to go on forever. This picture was take in October
(Thanksgiving weekend, actually) and so the grass was basically dead but the
snow had yet to come, leaving this beautiful palette of yellows.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 18-55mm ƒ/5.2-33 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/200 sec<br/>
<em>Aperture</em>: ƒ/10<br/>
<em>Focal Length</em>: 25 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 200<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2012:10:08 16:32:44</p>
</div></body></html>Push a Hg Repo to GitHub2012-10-24T19:25:00-06:002012-10-24T19:25:00-06:00Wm. Minchintag:blog.minchin.ca,2012-10-24:/2012/10/push-hg-repo-to-github.htmlSo the scenario is thus: There is a project that I want to contribute to that
is a Mercurial (Hg) repository (or ‘repo’ for short). I’ve taken a liking to
Git; <a href="http://www.github.com/">GitHub</a> in particular with <a href="http://windows.github.com/">their new and
beautiful Windows frontend</a>. So how do I take a
remote Hg repo and push it to GitHub. As follows (the following assumes you
have a Ubuntu box available to play with, although I imagine many Linux
machines would work the same way):<html><head></head><body><p>So the scenario is thus: There is a project that I want to contribute to that
is a Mercurial (Hg) repository (or ‘repo’ for short). I’ve taken a liking to
Git; <a href="http://www.github.com/">GitHub</a> in particular with <a href="http://windows.github.com/">their new and
beautiful Windows frontend</a>. So how do I take a
remote Hg repo and push it to GitHub. As follows (the following assumes you
have a Ubuntu box available to play with, although I imagine many Linux
machines would work the same way):</p>
<ul>
<li>So first, <a href="https://github.com/repositories/new">create your repo on
GitHub</a>. You don’t have to do anything
more with it at this time, just make a note of its name.</li>
<li>Next, add your machine’s <span class="caps">RSA</span> public key to GitHub. I won’t go through that
here, but if you walk through <a href="https://help.github.com/articles/generating-ssh-keys">GitHub’s setup steps for
Linux</a>, it will
explain all of this.</li>
<li>
<p>Ok next, install Git and Mercurial:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>git<span class="w"> </span>mercurial
</code></pre></div>
</li>
<li>
<p>Next, we install a little program that will link Mercurial to Git</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>python-setuptools
$<span class="w"> </span>sudo<span class="w"> </span>easy_install<span class="w"> </span>hggit
</code></pre></div>
</li>
<li>
<p>Now, create a folder as needed (for example, an ‘hgrepo’ folder in your
home folder)</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>~
$<span class="w"> </span>mkdir<span class="w"> </span>hgrepo
$<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>hgrepo
</code></pre></div>
</li>
<li>
<p>Now pull down the existing hg repo.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>hg<span class="w"> </span>clone<span class="w"> </span>http://web.address.of.hp.repo/repo-name
</code></pre></div>
</li>
<li>
<p>I might make a subfolder and put your repo there. That’s alright. Move to
that new directory.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>new-hg-repo-folder
</code></pre></div>
</li>
<li>
<p>Now we’re going to add the info we need to push to GitHub</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>.hg<span class="sb">`</span>
</code></pre></div>
</li>
<li>
<p>Find a file named “hgrc”. Add the following lines:</p>
<div class="highlight"><pre><span></span><code><span class="k">[path]</span>
<span class="na">git</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">git+ssh://git@github.com/your-username/your-new-github-repo.git</span>
<span class="k">[extensions]</span>
<span class="na">hggit</span><span class="w"> </span><span class="o">=</span>
</code></pre></div>
<p>There will probably be a line in there pointing to your original hg repo.
It’s fine. Leave it there.</p>
</li>
<li>
<p>Now push your repo to GitHub!</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>hg<span class="w"> </span>push<span class="w"> </span>git
</code></pre></div>
</li>
</ul>
<p>If everything worked right, your repo should now be available on GitHub!</p>
<hr/>
<p>One issue I didn’t address was contributor names. See the <a href="https://confluence.atlassian.com/pages/viewpage.action?pageId=269982882">bottom of this
page</a>
for an example of how to fix that.</p>
<p><a href="http://hgtip.com/tips/advanced/2009-11-09-create-a-git-mirror/">This page</a> on
<em>hg tip</em> was also very helpful.</p></body></html>Cookies; Chocolate Chip! — Photo 99 — Project 3652012-10-15T17:16:00-06:002012-10-15T17:16:00-06:00Wm. Minchintag:blog.minchin.ca,2012-10-15:/2012/10/cookies-chocolate-chip-photo-99-project-365.htmlThis is one of those pictures you have to take right away or the subject will
be gone! I also like to take pictures like this where I can play with the depth
of field and what is and isn’t in focus.<html><head></head><body><p>This is one of those pictures you have to take right away or the subject will
be gone! I also like to take pictures like this where I can play with the depth
of field and what is and isn’t in focus.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 18-55mm ƒ/5.2-33 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/60 sec<br/>
<em>Aperture</em>: ƒ/5.0<br/>
<em>Focal Length</em>: 46 mm<br/>
<em>Flash Used</em>: Yes<br/>
<em><span class="caps">ISO</span></em>: 400<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2012:10:15 17:01:43</p>
</div>
<p><span class="caps">P.S.</span> The cookies were delicious :)</p></body></html>Waterfall — Photo 98 — Project 3652012-08-12T02:25:00-06:002012-08-12T02:25:00-06:00Wm. Minchintag:blog.minchin.ca,2012-08-12:/2012/08/waterfall-photo-98-project-365.htmlStill in the National Parks between Banff and Jasper, I found this waterfall.
The effect I was going for here was one of fluid motion - by setting a
(relatively) long shutter speed (a tenth of a second, in this case), the motion
of the water is captured. At the other extreme, with an extremely fast shutter
speed, you can freeze a waterfall, making it look almost as if it had turned to
ice in an instant.<html><head></head><body><p>Still in the National Parks between Banff and Jasper, I found this waterfall.
The effect I was going for here was one of fluid motion - by setting a
(relatively) long shutter speed (a tenth of a second, in this case), the motion
of the water is captured. At the other extreme, with an extremely fast shutter
speed, you can freeze a waterfall, making it look almost as if it had turned to
ice in an instant.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 55-250mm ƒ/4-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/10 sec<br/>
<em>Aperture</em>: ƒ/25<br/>
<em>Focal Length</em>: 74 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 100<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2011:07:05 11:46:46</p>
</div>
<h2>Project Update</h2>
<p>So this is the end of my first batch of ‘Project 365’ photos - 98 photos that
have taken over two years to be put up. I realize that is a far cry from the
original plan of a photo a day for a year. I can blame part of the backlog on
moving five times and the following periods without internet, but regardless, I
am through that backlog. Now I can post pictures closer to when I take them and
hopefully offer more meaningful commentary. In short, I look forward to posting
more pictures here soon!</p></body></html>Wild Rose — Photo 97 — Project 3652012-08-11T13:59:00-06:002012-08-11T13:59:00-06:00Wm. Minchintag:blog.minchin.ca,2012-08-11:/2012/08/wild-rose-photo-97-project-365.htmlThe wild rose is Alberta’s provincial flower, picked by the schoolchildren not
long after the province’s creation in 1905. The flower is beautiful, but I
rarely see it in the city. I was extra excited to find one of this size and
open like this.<html><head></head><body><p>The wild rose is Alberta’s provincial flower, picked by the schoolchildren not
long after the province’s creation in 1905. The flower is beautiful, but I
rarely see it in the city. I was extra excited to find one of this size and
open like this.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 55-250mm ƒ/4-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/400 sec<br/>
<em>Aperture</em>: ƒ/7.1<br/>
<em>Focal Length</em>: 250 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 200<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2011:07:05 11:43:52</p>
</div></body></html>Tiny Bridge — Photo 96 — Project 3652012-07-28T17:44:00-06:002012-07-28T17:44:00-06:00Wm. Minchintag:blog.minchin.ca,2012-07-28:/2012/07/tiny-bridge-photo-96-project-365.htmlBridges are usually seen when standing on them or sometimes beside them, but
rarely straight down like this. This is taken along the Icefields Parkway,
about halfway between Jasper and Banff, looking down from a roadside lookout.<html><head></head><body><p>Bridges are usually seen when standing on them or sometimes beside them, but
rarely straight down like this. This is taken along the Icefields Parkway,
about halfway between Jasper and Banff, looking down from a roadside lookout.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 55-250mm ƒ/4-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/400 sec<br/>
<em>Aperture</em>: ƒ/8.0<br/>
<em>Focal Length</em>: 200 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 200<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2011:07:05 09:07:27</p>
</div></body></html>Daisies — Photo 95 — Project 3652012-06-26T21:29:00-06:002012-06-26T21:29:00-06:00Wm. Minchintag:blog.minchin.ca,2012-06-26:/2012/06/daisies-photo-95-project-365.htmlAs summer comes upon us, I found these daisies growing in the front yard. There
are bonuses to having the lawn mover break!<html><head></head><body><p>As summer comes upon us, I found these daisies growing in the front yard. There
are bonuses to having the lawn mover break!</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 18-55mm ƒ/3.5-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/2000 sec<br/>
<em>Aperture</em>: ƒ/2.5<br/>
<em>Focal Length</em>: 18 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 200<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2012:06:26 21:22:00</p>
</div></body></html>On the Edge — Photo 94 — Project 3652012-05-19T17:19:00-06:002012-05-19T17:19:00-06:00Wm. Minchintag:blog.minchin.ca,2012-05-19:/2012/05/on-the-edge-photo-94-project-365.htmlDriving through the mountains, we stopped at a beautiful overlook for
breakfast. I love shooting in the mountains because it’s so easy to get depth
and great backgrounds are everywhere!<html><head></head><body><p>Driving through the mountains, we stopped at a beautiful overlook for
breakfast. I love shooting in the mountains because it’s so easy to get depth
and great backgrounds are everywhere!</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 55-250mm ƒ/4-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/400 sec<br/>
<em>Aperture</em>: ƒ/7.1<br/>
<em>Focal Length</em>: 250 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 200<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2011:07:05 09:06:21</p>
</div></body></html>Looking On — Photo 93 — Project 3652012-05-11T19:49:00-06:002012-05-11T19:49:00-06:00Wm. Minchintag:blog.minchin.ca,2012-05-11:/2012/05/looking-on-photo-93-project-365.htmlI spent the day at the mall, and used my telephoto lens to get come neat candid
photos. I also liked how everyone milling around in the mall could be used to
generate depth in the photo.<html><head></head><body><p>I spent the day at the mall, and used my telephoto lens to get come neat candid
photos. I also liked how everyone milling around in the mall could be used to
generate depth in the photo.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 55-250mm ƒ/4-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/40 sec<br/>
<em>Aperture</em>: ƒ/5.6<br/>
<em>Focal Length</em>: 250 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 800<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2011:07:01 13:56:30</p>
</div></body></html>Chuckwagon Racing — Photo 92 — Project 3652012-04-22T17:44:00-06:002012-04-22T17:44:00-06:00Wm. Minchintag:blog.minchin.ca,2012-04-22:/2012/04/chuckwagon-racing-photo-92-project-365.html<p>This is the event that brought us to Ponoka for the Stampede. Chuckwagon racing
involves watch eight thoroughbred horses (four with the wagon and four
outriders) fly through the “Half Mile of Hell” — a figure eight in the infield
and then around the track.</p>
<p>When it comes to photography, getting a good picture gets tricky due to the
speed of the racers and the chaos of of the start as the four teams let loose.</p>
<html><head></head><body><p>This is the event that brought us to Ponoka for the Stampede. Chuckwagon racing
involves watch eight thoroughbred horses (four with the wagon and four
outriders) fly through the “Half Mile of Hell” — a figure eight in the infield
and then around the track.</p>
<p>When it comes to photography, getting a good picture gets tricky due to the
speed of the racers and the chaos of of the start as the four teams let loose.</p>
<p>This is the end of my rodeo photos, at least for this year.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 55-250mm ƒ/4-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/200 sec<br/>
<em>Aperture</em>: ƒ/7.1<br/>
<em>Focal Length</em>: 84 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 200<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Post-Production</em>: Cropped<br/>
<em>Date and Time Original</em>: 2011:06:30 01:55:45</p>
</div></body></html>Bull Riding — Photo 91 — Project 3652012-04-22T17:10:00-06:002012-04-22T17:10:00-06:00Wm. Minchintag:blog.minchin.ca,2012-04-22:/2012/04/bull-riding-photo-91-project-365.html<p>If barrel racing is about finesse, bull riding is about sheer power. Holding on
with only hand, the rider aims to stay on for eight seconds. A surprising few
make it. Those that do stand a chance at rodeo glory.</p>
<p>I liked this photo because it freezes the bull’s action so well, legs and in
the air and rope flying, hinting at the bull’s power. It’s interesting to note
the number of people around the periphery, watching attentively and ready to
jump into action at the first sign of trouble to rescue the bull rider.</p>
<html><head></head><body><p>If barrel racing is about finesse, bull riding is about sheer power. Holding on
with only hand, the rider aims to stay on for eight seconds. A surprising few
make it. Those that do stand a chance at rodeo glory.</p>
<p>I liked this photo because it freezes the bull’s action so well, legs and in
the air and rope flying, hinting at the bull’s power. It’s interesting to note
the number of people around the periphery, watching attentively and ready to
jump into action at the first sign of trouble to rescue the bull rider.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 55-250mm ƒ/4-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/250 sec<br/>
<em>Aperture</em>: ƒ/5.6<br/>
<em>Focal Length</em>: 250 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 320<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2011:06:29 22:46:30</p>
</div></body></html>Barrel Racing — Photo 90 — Project 3652012-04-01T23:04:00-06:002012-04-01T23:04:00-06:00Wm. Minchintag:blog.minchin.ca,2012-04-01:/2012/04/barrel-racing-photo-90-project-365.htmlMore rodeo photos! Barrel racing is amazing to watch — a combination speed and finesse.<html><head></head><body><p>More rodeo photos! Barrel racing is amazing to watch — a combination speed and finesse.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 55-250mm ƒ/4-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/250 sec<br/>
<em>Aperture</em>: ƒ/7.1<br/>
<em>Focal Length</em>: 187 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 200<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2011:06:29 22:30:50</p>
</div></body></html>Trick Riding — Photo 89 — Project 3652012-04-01T22:59:00-06:002012-04-01T22:59:00-06:00Wm. Minchintag:blog.minchin.ca,2012-04-01:/2012/04/trick-riding-photo-89-project-365.htmlThe rodeo provided a number of opportunities to take some great photos, so I
have a couple more to come after this. This one was of some of the trick riding.<html><head></head><body><p>The rodeo provided a number of opportunities to take some great photos, so I
have a couple more to come after this. This one was of some of the trick riding.</p>
<div class="photo-infobox">
<p><em>Model</em>: Canon <span class="caps">EOS</span> <span class="caps">DIGITAL</span> <span class="caps">REBEL</span> <span class="caps">XSI</span><br/>
<em>Lens <span class="caps">ID</span></em>: <span class="caps">EF</span>-S 55-250mm ƒ/4-5.6 <span class="caps">IS</span><br/>
<em>Exposure</em>: 1/250 sec<br/>
<em>Aperture</em>: ƒ/7.1<br/>
<em>Focal Length</em>: 109 mm<br/>
<em>Flash Used</em>: No<br/>
<em><span class="caps">ISO</span></em>: 200<br/>
<em>Filter(s)</em>: <span class="caps">HOYA</span> <span class="caps">UV</span>(0)<br/>
<em>Date and Time Original</em>: 2011:06:29 22:16:01</p>
</div></body></html>