<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://david.ps/feed.xml" rel="self" type="application/atom+xml" /><link href="https://david.ps/" rel="alternate" type="text/html" /><updated>2026-09-08T14:05:23+00:00</updated><id>https://david.ps/feed.xml</id><title type="html">David PS</title><subtitle>My blogl</subtitle><entry><title type="html">I am not at RSECon this year 😢</title><link href="https://david.ps/blog/2026/09/07/boycott-sheffield.html" rel="alternate" type="text/html" title="I am not at RSECon this year 😢" /><published>2026-09-07T00:00:00+00:00</published><updated>2026-09-07T00:00:00+00:00</updated><id>https://david.ps/blog/2026/09/07/boycott-sheffield</id><content type="html" xml:base="https://david.ps/blog/2026/09/07/boycott-sheffield.html"><![CDATA[<p>This time of the year, for the last decade, I’ve been celebrating
being a Research Software Engineer (RSE) with more and more people
every year. <a href="https://society-rse.org/">Society of Research Software Engineering</a>’s
<a href="https://rsecon26.society-rse.org">RSECon</a> turns 10 this year and I’ve participated in most of
them. Making a lot of friends on the way.</p>

<p>This year the celebration it is (was for me!) going to be even larger
as <a href="https://www.researchsoft.org">Research Software Allieance</a>’s <a href="https://www.researchsoft.org/irsc/">International Research
Software Conference</a> is co-located and happening just before
RSECon. Two conferences celebrating Research Software in one week. It
sounds like a dream.</p>

<p>And for me, it will be still a dream…</p>

<p>Both conferences are happening at <a href="https://sheffield.ac.uk/">the University of
Sheffield</a>, a place I keep in my heart as it was were I
taught my first <a href="https://www.sunpy.org">SunPy</a> and <a href="https://www.astropy.org">Astropy</a>
<a href="https://openastronomy.org/2016/01/15/Workshop.html">workshop</a> and where many of the Sunpy early developers
were based. However, the management at <a href="https://sheffield.ac.uk/">Sheffield</a> has
been playing dirty games. After <a href="https://ucu.group.shef.ac.uk/weekly-update/dates-of-industrial-action-confirmed/">staff went on 16 days of strike last
year</a>, and therefore classes were missed. The
management of the university decided <a href="https://www.ucu.org.uk/article/14380/Union-condemns-continued-docking-of-staff-pay-following-strike-action-at-University-of-Sheffield">“double deducting” to those who
refused to deliver these missed classes</a>, for which
they haven’t already been paid.</p>

<p>In the light of these circumstances, <a href="https://www.ucu.org.uk/">University and College Union
(UCU)</a> has <a href="https://www.ucu.org.uk/article/14537/Global-academic-boycott-imposed-on-University-of-Sheffield-over-job-cuts--pay-docking">called their members to boycott any activity involved
with the university</a>, such as participating on
conferences being held there.</p>

<p>In short, I’m not celebrating in #Sheffield this week.</p>

<p>It’s hard because I know <a href="https://sheffield.ac.uk/">Sheffield</a> wont even notice
that I won’t be there. And I know that <a href="https://society-rse.org/trustee-statement-on-rsecon26/">RSECon</a> and
<a href="https://www.researchsoft.org/irsc/venue-update/">IRSC have both evaluated the financial impact for our
communities</a> withdrawing and therefore loosing a large
deposit. But, though I understand that our financial impact to the
university is small for them but large to us, I also believe our
personal and academic community impact is more important. This is why
I withdrew the sessions I was taken part on.  By writing here I wish
my words reach to other RSEs and maybe Sheffield’s future students and
staff, so they know what type of values are followed by their
management.</p>

<p>To all staff affected: ✊ Solidarity!</p>]]></content><author><name></name></author><category term="ucu" /><category term="boycott" /><category term="rse" /><category term="conference" /><summary type="html"><![CDATA[This time of the year, for the last decade, I’ve been celebrating being a Research Software Engineer (RSE) with more and more people every year. Society of Research Software Engineering’s RSECon turns 10 this year and I’ve participated in most of them. Making a lot of friends on the way.]]></summary></entry><entry><title type="html">Memorising your students names using Anki</title><link href="https://david.ps/blog/2026/04/14/anki-students.html" rel="alternate" type="text/html" title="Memorising your students names using Anki" /><published>2026-04-14T00:00:00+00:00</published><updated>2026-04-14T00:00:00+00:00</updated><id>https://david.ps/blog/2026/04/14/anki-students</id><content type="html" xml:base="https://david.ps/blog/2026/04/14/anki-students.html"><![CDATA[<p>Many of us believe we are not good with people’s names, but I believe we can remember them if we put the effort into memorising them. And there are tools that can help us to memorise by repetition. <a href="https://apps.ankiweb.net/">Anki</a> is a powerful flashcard application that can help you to memorise many things through repetition.</p>

<p>The steps I’m going to show may be very specific to my university, but I’m sure you have got some similar system at your institution/school.</p>

<h3 id="what-do-we-need">What do we need?</h3>

<ul>
  <li><a href="https://apps.ankiweb.net/">Anki</a> for the memorisation bit. You can use it on your desktop or on your phone. Anki allows you to synchronise between devices but you’ll need to be careful whether this is through a public server, as you don’t want to leak your students names and pictures to the wild. If you want synchronising between various devices, you can run your own <a href="https://docs.ankiweb.net/sync-server.html">sync server on your desktop</a>. If you are going to use a single device, then you don’t need to worry.
    <ul>
      <li>You will need anki desktop client for the step of creating cards.</li>
      <li>And an <a href="https://docs.ankiweb.net/addons.html">Anki addon</a>: <a href="https://ankiweb.net/shared/info/129299120">Media Import 2</a></li>
    </ul>
  </li>
  <li>Students photos named with their names.</li>
</ul>

<p>Then opening the media import 2 menu in anki, and following the indications as explained on their website, you can get cards for it.</p>

<p>This is all easy and good… however, the platform at our university doesn’t provide the photos with the names already, and having to do that manually would be a pain with more than 10 students. So, what would you need?</p>

<h4 id="what-do-we-need-in-ucl">What do we need? (in UCL)</h4>

<ul>
  <li>A browser, I’m using Firefox</li>
  <li>Two browser addons:
    <ul>
      <li><a href="https://addons.mozilla.org/en-GB/firefox/addon/downthemall/">DownThemAll!</a> - to automate the downloading the images.</li>
      <li><a href="https://addons.mozilla.org/en-GB/firefox/addon/greasemonkey/">GreaseMonkey</a> (or similar) - to do some manipulation so the images can be downloaded with the names we want.</li>
    </ul>
  </li>
</ul>

<h3 id="hands-on-ucl">Hands on (UCL)</h3>

<p>Once you access portico, get to your module list with the pictures of your students, then when you try to download them using downthemall you discover that the images contain no file names, and what’s worse, the title and description tags say something like “This student has no photograph” 😓.</p>

<p><img height="200px" src="/gallery/images/2026/04/2026-04-14-portico-dta.png" alt="screenshot of downthemall window showing the media tab and a large list of files available to download. Each line contains a link, a title and a description. The Links have been blurred, but the titles and descriptions all say 'This student has no photograph'." /></p>

<p>We can’t change the file names directly, therefore we need to change the title or the description, which thankfully downthemall can use them as mask to save the files when downloaded (as shown in the bottom of the above image where it’s asking to save them using the <code class="language-plaintext highlighter-rouge">title</code> of each image).</p>

<p>Here’s where greasemonkey comes in. Greasemonkey allows us to modify websites using a bit of javascript. We are going to write a small script that when loading the site modifies the <code class="language-plaintext highlighter-rouge">alt</code> attribute for each photo using the names shown below them. This is how the website looks:</p>

<p><img height="200px" src="/gallery/images/2026/04/2026-04-14-portico-sample.png" alt="screenshot of portico list of students showing the photo of three fictitious students (Albert Einstein, Lamar Hedy and Chien-Shiung Wu) with their registration details such as programme, student number and email. Their full name appear above each photo, where their preferred name appear below the photo." /></p>

<p>And this is the Greasemonkey script to do so:</p>

<pre><code class="language-javascript=">// ==UserScript==
// @name     portico-students-names
// @version  1
// @grant    none
// @include  https://&lt;url-to-the-page-where-photos-are&gt;/*
// ==/UserScript==


function modify_alt(){
  var students = document.getElementsByClassName("sv-panel-body");
for (item in students){
  students[item].children[0].alt = students[item].children[4].innerText;
	}
}
modify_alt();
</code></pre>

<p>This script defines a function <code class="language-plaintext highlighter-rouge">modify_alt</code> which then is called at the end of the script. The function gets first all the students cards (which in our case are under a <code class="language-plaintext highlighter-rouge">div</code> using a class named <code class="language-plaintext highlighter-rouge">sv-panel-body</code>). Then iterates over each of the cards and then adds the <code class="language-plaintext highlighter-rouge">alt</code> attribute the text of where the name is.</p>

<p>Looking at the snippet of one of those cards in html,</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"sv-panel-body"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;img</span> <span class="na">alt=</span><span class="s">"This student has no photograph"</span> <span class="na">align=</span><span class="s">"left"</span> <span class="na">width=</span><span class="s">"auto"</span>
       <span class="na">height=</span><span class="s">"160"</span> <span class="na">src=</span><span class="s">"path/to/image"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;p</span> <span class="na">align=</span><span class="s">"right"</span><span class="nt">&gt;&lt;span</span> <span class="na">class=</span><span class="s">"glyphicon glyphicon-home"</span><span class="nt">&gt;&lt;/span&gt;</span> Filming<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;p</span> <span class="na">align=</span><span class="s">"right"</span><span class="nt">&gt;</span>19141104/2<span class="nt">&lt;br&gt;&lt;span</span> <span class="na">title=</span><span class="s">"MArt Filming"</span><span class="nt">&gt;</span>UMSFILG18<span class="nt">&lt;/span&gt;&lt;br&gt;</span>Year 4<span class="nt">&lt;br&gt;</span>Enrolled<span class="nt">&lt;br&gt;&lt;span&gt;</span>Full-Time<span class="nt">&lt;sup&gt;&lt;/sup&gt;&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;/p&gt;&lt;br&gt;</span>
  <span class="nt">&lt;strong&gt;</span>Hedy<span class="nt">&lt;/strong&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>we can see that the <code class="language-plaintext highlighter-rouge">children</code> values of the <code class="language-plaintext highlighter-rouge">sv-panel-body</code> are 4, the <code class="language-plaintext highlighter-rouge">&lt;img&gt;</code>, the <code class="language-plaintext highlighter-rouge">&lt;p&gt;</code> with the degree, the <code class="language-plaintext highlighter-rouge">&lt;p&gt;</code> with the student information and the <code class="language-plaintext highlighter-rouge">&lt;strong&gt;</code> that contains the name of the student. That’s how we extract the student name and inserted in the <code class="language-plaintext highlighter-rouge">alt</code> attribute that Downthemall will use to download the images.</p>

<p>Once activated the greasemonkey script and loading the page, when we open Downthemall we see that the <code class="language-plaintext highlighter-rouge">title</code> column (and the <code class="language-plaintext highlighter-rouge">description</code>) contains the names of the students as we wanted:</p>

<p><img height="200px" src="/gallery/images/2026/04/2026-04-14-portico-dta-after-gm.png" alt="screenshot of downthemall window showing the media tab and a large list of files available to download. Each line contains a link, a title and a description. The Links have been blurred. The titles and descriptions now all say different names." /></p>

<p>Once you’ve downloaded the images, you can import them with Anki’s Media Import 2 and produce flashcards with the student photo and their name. You can create them as double cards if you want to be prompted with the photo and the name and you have to guess the other.</p>]]></content><author><name></name></author><category term="anki" /><category term="ucl" /><category term="learning" /><summary type="html"><![CDATA[Many of us believe we are not good with people’s names, but I believe we can remember them if we put the effort into memorising them. And there are tools that can help us to memorise by repetition. Anki is a powerful flashcard application that can help you to memorise many things through repetition.]]></summary></entry><entry><title type="html">Queuing email on emacs using mu and msmtp</title><link href="https://david.ps/blog/2025/11/19/queue-email.html" rel="alternate" type="text/html" title="Queuing email on emacs using mu and msmtp" /><published>2025-11-19T00:00:00+00:00</published><updated>2025-11-19T00:00:00+00:00</updated><id>https://david.ps/blog/2025/11/19/queue-email</id><content type="html" xml:base="https://david.ps/blog/2025/11/19/queue-email.html"><![CDATA[<p><em>TL;DR: <a href="#all-together">jump to the end</a> to see all together if you don’t care about my learning process</em></p>

<p>I’ve started to use <a href="https://djcbsoftware.nl/code/mu/mu4e/index.html"><code class="language-plaintext highlighter-rouge">mu4e</code></a> a while ago, motivated by the
instructions shared on <a href="https://m-x-research.github.io/">M-x Research</a> when Martin
Richter showed us how to do it.</p>

<p>One thing I’d like to have is a send-later feature as outlook and
gmail allows you to do so. So I can schedule an email to be sent on a
particular day. Unfortunately I’ve not found a way to do so yet that
works for me. I’ve been told that <a href="https://gnus.org/">gnus</a> has an option to do so, but
you’ll need a computer switched on waiting that the time comes. I have
some ideas on how that could be implemented for Outlook, but I’ve not
come around that yet.</p>

<p>An alternative that I’d be happy with is to be able to queue my
email. Either because I don’t want to send emails later in the
evening, or just because I’m in a place without internet.
<a href="https://www.djcbsoftware.nl/code/mu/mu4e/Queuing-mail.HTML"><code class="language-plaintext highlighter-rouge">mu4e</code> has the machinery to queue emails</a> as it’s shown
in their documentation.
However, it took a while for me to realise what I was missing.
So, hopefully my lines below helps to others and also people can
comment on my elisp so I can improve.</p>

<p><code class="language-plaintext highlighter-rouge">mu4e</code> documentation seems straight forward, you need only two
variables being set (<code class="language-plaintext highlighter-rouge">smtpmail-queue-mail</code> and <code class="language-plaintext highlighter-rouge">smtpmail-queue-dir</code>)
and that the queue directory actually exists. For much I tried, this
was not working, the emails were either sent, or erroing when I was
disconnected from the net. Reading through the code I figured out that
the issue was that I hadn’t configured my email using <code class="language-plaintext highlighter-rouge">smtpmail</code> emacs
library, but instead <a href="https://marlam.de/msmtp/"><code class="language-plaintext highlighter-rouge">msmtp</code></a> program. I believe the reason is
because the oauth authentication workflow I need for my university
requires that I use <a href="https://github.com/harishkrupo/oauth2ms">oauth2ms</a> (please, feel free to correct me
if I’m wrong!).</p>

<p>Once I new the cause was there I started thinking on how I could queue
it. This brought me to search those two terms: <code class="language-plaintext highlighter-rouge">msmtp</code> and
<code class="language-plaintext highlighter-rouge">queue</code>. And that pointed me to this particular bit of the <a href="https://github.com/marlam/msmtp/tree/master/scripts/msmtpqueue">source
code of <code class="language-plaintext highlighter-rouge">msmtp</code></a>. Some scripts untouched from 17 years ago
(if it works, why touch it, right?). I started to dive into them,
thinking how I could use them, then I realised that on the nav bar on
the left said <code class="language-plaintext highlighter-rouge">emacs</code>, and I got exited thinking that the solution may
have been there already… but no, it was not. However, that
distraction helped me to find <a href="https://github.com/marlam/msmtp/tree/master/scripts/msmtpq"><code class="language-plaintext highlighter-rouge">msmtpq</code></a>… which I clicked on
it thinking it was what I was trying to digest before. But it was not!</p>

<p><code class="language-plaintext highlighter-rouge">msmtpq</code> provides a layer on top of <code class="language-plaintext highlighter-rouge">msmtp</code> that before sending any
email checks whether you’ve got a live connection. This seemed
promising!</p>

<p>So, what did I do? I started messing up with my emacs config code, and
look into what mu4e was doing. This is what I come up with.</p>

<p>First, I needed to put the <code class="language-plaintext highlighter-rouge">msmtpq</code> script on my path. In my Arch distro I had to do:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> ~/.local/bin
<span class="nb">ln</span> <span class="nt">-s</span> /usr/share/doc/msmtp/msmtpq/msmtpq <span class="nb">.</span>
<span class="nb">ln</span> <span class="nt">-s</span> /usr/share/doc/msmtp/msmtpq/msmtp-queue <span class="nb">.</span>
</code></pre></div></div>

<p>Then in emacs I had a variable that defines what the <code class="language-plaintext highlighter-rouge">sendmail</code> program is</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">sendmail-program</span>  <span class="p">(</span><span class="nv">executable-find</span> <span class="s">"msmtp"</span><span class="p">))</span>
</code></pre></div></div>
<p>and I wanted to change it to the <code class="language-plaintext highlighter-rouge">msmtpq</code> when I want to queue the email. So I crated a function that depending whether I’m in queue or direct mode (i.e., whether <code class="language-plaintext highlighter-rouge">smtpmail-queue-mail</code> is true or not) tells what executable to use:</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">dps/queue-or-not</span> <span class="p">()</span>
  <span class="p">(</span><span class="k">if</span> <span class="nv">smtpmail-queue-mail</span>
      <span class="c1">;; need to set some system variables</span>
      <span class="c1">;; EMAIL_CONN_TEST='q'</span>
      <span class="c1">;; EMAIL_QUEUE_QUIET=1 ;; to keep it quiet</span>
      <span class="p">(</span><span class="k">progn</span> <span class="p">(</span><span class="nv">setenv</span> <span class="s">"EMAIL_QUEUE_QUIET"</span> <span class="s">"1"</span><span class="p">)</span>
	     <span class="p">(</span><span class="nv">setenv</span> <span class="s">"EMAIL_CONN_TEST"</span> <span class="s">"q"</span><span class="p">)</span>
	     <span class="p">(</span><span class="nv">executable-find</span> <span class="s">"msmtpq"</span><span class="p">)</span>
	     <span class="p">)</span>
    <span class="p">(</span><span class="nv">executable-find</span> <span class="s">"msmtp"</span><span class="p">)</span>
    <span class="p">))</span>
</code></pre></div></div>

<p>Originally, I had set that <code class="language-plaintext highlighter-rouge">EMAIL_CON_TEST</code> system variable to <code class="language-plaintext highlighter-rouge">'x'</code>,
wrongly thinking that it meant that it was not going to be sent, but
it actually means to don’t check for internet connection. Since I
wanted to force it to queue even if I had internet, I introduced a new
parameter into <code class="language-plaintext highlighter-rouge">msmtpq</code>.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">316,317d315
</span><span class="gd">&lt;   elif [ "$EMAIL_CONN_TEST" = 'q' ] ; then                     # force it to queue
&lt;     return 1
</span></code></pre></div></div>

<p>I’ve opened a <a href="https://github.com/marlam/msmtp/pull/211">PR to msmtp to include that option</a>, in
case this becomes useful for more people.</p>

<blockquote>
  <p><em>Note to my future self:</em><br />
In a future refactoring, probably all I need on the function above is
to set that variable from <code class="language-plaintext highlighter-rouge">'x'</code> to <code class="language-plaintext highlighter-rouge">'q'</code>, and always use <code class="language-plaintext highlighter-rouge">msmtpq</code>.</p>
</blockquote>

<p>Once I had that function, I could define the <code class="language-plaintext highlighter-rouge">sendmail-program</code> to
read from it. So, I tried something like:</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">sendmail-program</span>  <span class="ss">'dps/queue-or-not</span><span class="p">)</span>
</code></pre></div></div>

<p>And other variants of the above (you know with <code class="language-plaintext highlighter-rouge">#</code>, with <code class="language-plaintext highlighter-rouge">,</code>, with all
of them….), but that was not working because where that variable was
used was expecting the path, not a function to execute. So I had to
approach the problem in a different way.</p>

<p>My first approach was to set that variable when I toggle the sending
mode on mu4e, and mu4e has that encapsulated into the
<a href="https://github.com/djcb/mu/blob/20b2fec339e91051c1aaf303117957c047054540/mu4e/mu4e-main.el#L408-L419"><code class="language-plaintext highlighter-rouge">mu4e--main-toggle-mail-sending-mode</code> function</a>.
So, I can copy it and modified, and that’s what I did:</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">mu4e--main-toggle-mail-sending-mode</span> <span class="p">()</span>
  <span class="s">"Toggle sending mail mode, either queued or direct."</span>
  <span class="p">(</span><span class="nv">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">file-directory-p</span> <span class="nv">smtpmail-queue-dir</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">mu4e-error</span> <span class="s">"`smtpmail-queue-dir' does not exist"</span><span class="p">))</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">smtpmail-queue-mail</span> <span class="p">(</span><span class="nb">not</span> <span class="nv">smtpmail-queue-mail</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">mu4e-message</span>
   <span class="p">(</span><span class="nv">concat</span> <span class="s">"Outgoing mail will now be "</span>
           <span class="p">(</span><span class="k">if</span> <span class="nv">smtpmail-queue-mail</span> <span class="s">"queued"</span> <span class="s">"sent directly"</span><span class="p">)))</span>
  <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nb">or</span> <span class="p">(</span><span class="nb">eq</span> <span class="nv">mu4e-split-view</span> <span class="ss">'single-window</span><span class="p">)</span>
              <span class="p">(</span><span class="nb">not</span> <span class="p">(</span><span class="nv">buffer-live-p</span> <span class="p">(</span><span class="nv">get-buffer</span> <span class="nv">mu4e-main-buffer-name</span><span class="p">))))</span>
    <span class="p">(</span><span class="k">setq</span> <span class="nv">sendmail-program</span> <span class="p">(</span><span class="nv">dps/queue-or-not</span><span class="p">))</span>
    <span class="p">(</span><span class="nv">mu4e--main-redraw</span><span class="p">)))</span>
</code></pre></div></div>

<p>Inserting that <code class="language-plaintext highlighter-rouge">setq</code> line in the second last line.</p>

<p>Perfect, that works! I can toggle the email on the dashboard, and it
queues or send the email as I need.</p>

<p>Next I wanted to display how many emails were on the queue… and for that I had to rewrite another function, this time <a href="https://github.com/djcb/mu/blob/20b2fec339e91051c1aaf303117957c047054540/mu4e/mu4e-main.el#L379-L386"><code class="language-plaintext highlighter-rouge">mu4e--main-queue-size</code> function</a>. And this is how that looks now:</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">mu4e--main-queue-size</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">length</span> <span class="p">(</span><span class="nv">directory-files</span> <span class="s">"~/.msmtp.queue"</span> <span class="no">nil</span> <span class="s">"msmtp"</span><span class="p">))</span>
  <span class="p">)</span>
</code></pre></div></div>

<p>Which essentially counts how many <code class="language-plaintext highlighter-rouge">.msmtp</code> files are in the queue directory.</p>

<p>And finally, I needed to update flushing the queue command. <code class="language-plaintext highlighter-rouge">mu4e</code> uses <a href="https://github.com/emacs-mirror/emacs/blob/master/lisp/mail/smtpmail.el#L421-L478"><code class="language-plaintext highlighter-rouge">smtpmail-send-queued-mail</code></a>, but I’m not using that anywhere, so I could rewrite as:</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">smtpmail-send-queued-mail</span> <span class="p">()</span>
  <span class="p">(</span><span class="nv">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">setenv</span> <span class="s">"EMAIL_CONN_TEST"</span> <span class="s">"x"</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">shell-command</span> <span class="p">(</span><span class="nv">concat</span> <span class="p">(</span><span class="nv">executable-find</span> <span class="s">"msmtp-queue"</span><span class="p">)</span> <span class="s">" -r &amp;"</span><span class="p">)</span> <span class="s">"*msmstp-queue*"</span><span class="p">)</span>
  <span class="p">)</span>
</code></pre></div></div>

<p>And that’s all I needed. I’m not too happy on copy-paste and changing functions, but it works for now. Making me very happy to see that when I change the email mode the interface reflects that and emails get queued or flushed as I want.</p>

<p>…</p>

<p>Till I went to demonstrate it live yesterday on the <a href="https://emacs.london/">Emacs London meetup</a>. There I told them why I did and how so I could get some feedback. And I did.</p>

<p>First, why it was not working when I show it to them? It took me a while to figure it out, and <code class="language-plaintext highlighter-rouge">toggle-debug-on-error</code> was not helpful. However, I found out that the emails were not being queued because emacs couldn’t find <code class="language-plaintext highlighter-rouge">msmtpq</code>. This happened because I’m starting emacs as a service, and the <code class="language-plaintext highlighter-rouge">$PATH</code> in that environment doesn’t includes <code class="language-plaintext highlighter-rouge">~/.local/bin</code>. Though surprisingly, if I do <code class="language-plaintext highlighter-rouge">M-! echo $PATH</code> it does. So I solved it by creating a copy of <code class="language-plaintext highlighter-rouge">emacs.service</code> into my <code class="language-plaintext highlighter-rouge">~/.config/systemd/user/</code> where I added:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Environment="PATH=~/.local/bin:/usr/local/bin:/usr/bin"
</code></pre></div></div>
<p>and now it works.</p>

<p>Additionally, I’ve got some “advice” on how to modify the large function I had to copy from mu4e to define the program (and variables) depending of the mode.</p>

<p>So, this</p>
<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">mu4e--main-toggle-mail-sending-mode</span> <span class="p">()</span>
  <span class="s">"Toggle sending mail mode, either queued or direct."</span>
  <span class="p">(</span><span class="nv">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">file-directory-p</span> <span class="nv">smtpmail-queue-dir</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">mu4e-error</span> <span class="s">"`smtpmail-queue-dir' does not exist"</span><span class="p">))</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">smtpmail-queue-mail</span> <span class="p">(</span><span class="nb">not</span> <span class="nv">smtpmail-queue-mail</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">mu4e-message</span>
   <span class="p">(</span><span class="nv">concat</span> <span class="s">"Outgoing mail will now be "</span>
           <span class="p">(</span><span class="k">if</span> <span class="nv">smtpmail-queue-mail</span> <span class="s">"queued"</span> <span class="s">"sent directly"</span><span class="p">)))</span>
  <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nb">or</span> <span class="p">(</span><span class="nb">eq</span> <span class="nv">mu4e-split-view</span> <span class="ss">'single-window</span><span class="p">)</span>
              <span class="p">(</span><span class="nb">not</span> <span class="p">(</span><span class="nv">buffer-live-p</span> <span class="p">(</span><span class="nv">get-buffer</span> <span class="nv">mu4e-main-buffer-name</span><span class="p">))))</span>
    <span class="p">(</span><span class="k">setq</span> <span class="nv">sendmail-program</span> <span class="p">(</span><span class="nv">dps/queue-or-not</span><span class="p">))</span>
    <span class="p">(</span><span class="nv">mu4e--main-redraw</span><span class="p">)))</span>
</code></pre></div></div>

<p>become</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">dps/mu4e-toggle-mail</span> <span class="p">(</span><span class="nv">orig-func</span> <span class="k">&amp;rest</span> <span class="nv">args</span><span class="p">)</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">sendmail-program</span> <span class="p">(</span><span class="nv">dps/queue-or-not</span><span class="p">))</span>
  <span class="p">(</span><span class="nb">apply</span> <span class="nv">orig-func</span> <span class="nv">args</span><span class="p">)</span>
<span class="p">)</span>

<span class="p">(</span><span class="nv">advice-add</span> <span class="nf">#'</span><span class="nv">mu4e--main-toggle-mail-sending-mode</span> <span class="ss">:around</span>
	    <span class="nf">#'</span><span class="nv">dps/mu4e-toggle-mail</span><span class="p">)</span>
</code></pre></div></div>

<p>Which is a lot nicer and I don’t need to keep an eye on what’s maintained.</p>

<h4 id="all-together">All together</h4>

<p>At the end I followed my suggestion above and have just refactor it a
bit more, so this is what I’ve got.</p>

<p>I need that <code class="language-plaintext highlighter-rouge">smtpmail-queue-dir</code> variable exists pointing to a <code class="language-plaintext highlighter-rouge">mu</code>
directory created <a href="https://www.djcbsoftware.nl/code/mu/mu4e/Queuing-mail.HTML">as de queuing docs suggest</a>, even
though, the emails are not stored there. And I’m going to keep it that
for a bit till I have more time to play.</p>

<p>I’m starting on queueing mode by default and this is how all fits
together (don’t forget to modify <code class="language-plaintext highlighter-rouge">msmtpq</code> to include the <code class="language-plaintext highlighter-rouge">q</code> as I said
above first):</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">require</span> <span class="ss">'smtpmail</span><span class="p">)</span>

<span class="p">(</span><span class="nv">setenv</span> <span class="s">"EMAIL_QUEUE_QUIET"</span> <span class="s">"1"</span><span class="p">)</span>  <span class="c1">;; to keep msmtpq quiet</span>
<span class="p">(</span><span class="nv">setenv</span> <span class="s">"EMAIL_CONN_TEST"</span> <span class="s">"q"</span><span class="p">)</span> <span class="c1">;; queing by default</span>
<span class="p">(</span><span class="k">setq</span> <span class="nv">smtpmail-queue-mail</span> <span class="no">t</span>  <span class="c1">;; start in queuing mode</span>
      <span class="nv">smtpmail-queue-dir</span>   <span class="s">"~/Mail/queue/cur"</span> <span class="c1">;; though we are not using it mu4e needs it to be able to toggle</span>
      <span class="nv">sendmail-program</span> <span class="p">(</span><span class="nv">executable-find</span> <span class="s">"msmtpq"</span><span class="p">)</span>
      <span class="nv">send-mail-function</span> <span class="ss">'message-send-mail-with-sendmail</span>
<span class="p">)</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">dps/queue-or-not</span> <span class="p">()</span>
  <span class="p">(</span><span class="k">if</span> <span class="nv">smtpmail-queue-mail</span>
      <span class="p">(</span><span class="nv">setenv</span> <span class="s">"EMAIL_CONN_TEST"</span> <span class="s">"q"</span><span class="p">)</span> <span class="c1">;; queue</span>
      <span class="p">(</span><span class="nv">setenv</span> <span class="s">"EMAIL_CONN_TEST"</span> <span class="s">"x"</span><span class="p">)</span> <span class="c1">;; try to send (will queue if fails)</span>
  <span class="p">)</span>
<span class="p">)</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">dps/mu4e-toggle-mail</span> <span class="p">(</span><span class="nv">orig-func</span> <span class="k">&amp;rest</span> <span class="nv">args</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">apply</span> <span class="nv">orig-func</span> <span class="nv">args</span><span class="p">)</span> 
  <span class="p">(</span><span class="nv">dps/queue-or-not</span><span class="p">)</span> <span class="c1">;; evaluated after the toggle so we know the smtpmail-queue-mail value</span>
<span class="p">)</span>

<span class="p">(</span><span class="nv">advice-add</span> <span class="nf">#'</span><span class="nv">mu4e--main-toggle-mail-sending-mode</span> <span class="ss">:around</span>
	    <span class="nf">#'</span><span class="nv">dps/mu4e-toggle-mail</span><span class="p">)</span>

<span class="c1">;; rewrite of mu4e--main-queue-size</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">mu4e--main-queue-size</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">length</span> <span class="p">(</span><span class="nv">directory-files</span> <span class="s">"~/.msmtp.queue"</span> <span class="no">nil</span> <span class="s">"msmtp"</span><span class="p">))</span>
<span class="p">)</span>

<span class="c1">;; rewrite of smtpmail-send-queued-mail</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">smtpmail-send-queued-mail</span> <span class="p">()</span>
  <span class="p">(</span><span class="nv">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">sys_email_conn</span> <span class="p">(</span><span class="nv">getenv</span> <span class="s">"EMAIL_CONN_TEST"</span><span class="p">)))</span> <span class="c1">;; temp variable to restore the value of varialbe</span>
	   <span class="p">(</span><span class="nv">setenv</span> <span class="s">"EMAIL_CONN_TEST"</span> <span class="s">"x"</span><span class="p">)</span>
       <span class="p">(</span><span class="nv">shell-command</span> <span class="p">(</span><span class="nv">concat</span> <span class="p">(</span><span class="nv">executable-find</span> <span class="s">"msmtp-queue"</span><span class="p">)</span> <span class="s">" -r &amp;"</span><span class="p">)</span> <span class="s">"*msmstp-queue*"</span><span class="p">)</span>
	   <span class="p">(</span><span class="nv">setenv</span> <span class="s">"EMAIL_CONN_TEST"</span> <span class="nv">sys_email_conn</span><span class="p">)</span>
  <span class="p">)</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Any improvements you can think of is highly appreciated! Tell me on
<a href="https://mastodon.green/@dvdgc13/115578263537884476">mastodon</a> or through an <a href="https://github.com/dpshelio/dpshelio.github.io/issues/new">issue on
GitHub</a>.</p>

<p>Thanks to the <a href="https://emacs.london/">Emacs London meetup</a>, in particular to Przemek and Eric.</p>]]></content><author><name></name></author><category term="email" /><category term="emacs" /><category term="learning" /><summary type="html"><![CDATA[TL;DR: jump to the end to see all together if you don’t care about my learning process]]></summary></entry><entry><title type="html">Catching up with e-mail after a holiday break</title><link href="https://david.ps/blog/2022/06/24/catching-up-with-email.html" rel="alternate" type="text/html" title="Catching up with e-mail after a holiday break" /><published>2022-06-24T00:00:00+00:00</published><updated>2022-06-24T00:00:00+00:00</updated><id>https://david.ps/blog/2022/06/24/catching-up-with-email</id><content type="html" xml:base="https://david.ps/blog/2022/06/24/catching-up-with-email.html"><![CDATA[<p>I’m just back from two weeks of holidays and one week of workshop. Normally I
only set my Out of Office email saying something like.</p>

<blockquote>
  <p>I’m off till D Month. I’ll be come back to you as soon as I can after that day.</p>
</blockquote>

<p>That kind of message doesn’t help anyone. I’m giving a false hope that I’ll
reach to that email and when I’m back in the office I get overwhelmed with the
large amount of emails I’ve collected while I was away.</p>

<p>This time, and induced by reading <a href="http://www.worldcat.org/oclc/1241252289">“A world without email” book by Cal
Newport</a> I had first decided to change
the OoO message to:</p>

<blockquote>
  <p>I’m off till D Month. Your email has been deleted, send it again when I’ve returned.</p>
</blockquote>

<p>However, while discussing the plans with a colleague who made me think on the
pressure that this may put on others, I considered a different approach. This
was my OoO for the last three weeks:</p>

<blockquote>
  <p>Hi there!
I’m away enjoying my holidays (till the 20th) or at a workshop (20th-24th)!</p>

  <p>TL;DR: Your email is safe – eventually, I’ll come back to you. You need
something urgently – jump to the end of the email.</p>

  <p>I was planning to delete any incoming email while I was away, and let you know
about such actions. But a good friend has made me rethink this course of
action, as doing, so I’d be putting the burden on you! What am I doing
instead? Glad you’ve asked!! I’m archiving all the emails received during my
time off under a special folder. When I’m back, a little program will be
pulling two random emails a day into my inbox, and I commit to answer these.
How long it will pass before I answer you will depend on luck and the number
of emails I may have received. By doing so I’ll have a better start and don’t
get overwhelmed by a large amount of things to pay attention to.</p>

  <p>Something urgent about
[…]</p>

  <p>Have a great time (I’ll make sure I’ll be doing so)!!
David</p>

  <p>PS: You can trick this system (and this is allowed!) by sending me a reply to
the original email when I’m back. This way, the old email will resurface
again!</p>
</blockquote>

<p>I’ve received various positive comments from people who received this OoO and my
Inbox is empty. So, I suppose this approach has worked well… at least till
now. I checked the number of emails I’ve got in that separate folder this
morning and it would take me more than 9 months to go through them. Let’s see
who are the lucky ones that get replied this month.</p>

<p>What’s the “technical side” of this? I first attempted to do something very
fancy with <a href="https://emea.flow.microsoft.com/en-us/">MS Power Automate</a>, which
allows to grab emails from your account and do a set of things. However, I got
stuck with the random selection. Their email tool they have only take 25 emails
and that would only benefit at those who wrote closer to my return. I also tried
to come up with some spreadsheet formulae, but that also failed.</p>

<p>First of all I created a rule on the outlook web client. I’ve set it so that all
the emails with me in the “To” field were going to be archived in the
<code class="language-plaintext highlighter-rouge">OnHolidays</code> folder. The rest of emails are redirected to <code class="language-plaintext highlighter-rouge">OnHolidaysSecondary</code>.
I’ll be drawing random emails from the primary folder till is empty, then go to
the Secondary (which it should be less important things - I think!)</p>

<p>At the end, I went for a simple approach with the tools I’m more familiar: emacs
and unix tools. I’m reading my email there, so why not do the random selection
from there too? Using org-mode and the <code class="language-plaintext highlighter-rouge">mu</code> command I come up with the
following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mu find <span class="nt">-u</span> maildir:<span class="s1">'/OnHolidays'</span> <span class="nt">-f</span> <span class="s2">"i l"</span> | <span class="nb">sort</span> | <span class="nb">uniq</span> |  <span class="nb">awk</span> <span class="s1">'{print rand()*1000000, $1, $2}'</span> | <span class="nb">sort</span> | <span class="nb">head</span> <span class="nt">-n</span> 2 | <span class="nb">awk</span> <span class="s1">'{print  "mu4e:msgid:"$2}'</span>
</code></pre></div></div>

<p>The result of that looks something like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mu4e:msgid:VI1PR01MB5135E9E1D4A1C701E11F182FC0A39@VI1PR01MB5135.eurprd01.prod.exchangelabs.com
mu4e:msgid:AM6PR0102MB314123DDE920ACBFA727172BADA39@AM6PR0102MB3141.eurprd01.prod.exchangelabs.com
</code></pre></div></div>

<p>which in emacs are clickable links to that particular email on my mu4e views.</p>

<p>I hope this is helpful</p>]]></content><author><name></name></author><category term="email" /><category term="work" /><category term="holidays" /><summary type="html"><![CDATA[I’m just back from two weeks of holidays and one week of workshop. Normally I only set my Out of Office email saying something like.]]></summary></entry><entry><title type="html">Setting an out of office during a strike period</title><link href="https://david.ps/blog/2022/03/23/an-out-of-office-during-strike-period.html" rel="alternate" type="text/html" title="Setting an out of office during a strike period" /><published>2022-03-23T00:00:00+00:00</published><updated>2022-03-23T00:00:00+00:00</updated><id>https://david.ps/blog/2022/03/23/an-out-of-office-during-strike-period</id><content type="html" xml:base="https://david.ps/blog/2022/03/23/an-out-of-office-during-strike-period.html"><![CDATA[<p>Sadly, strikes are becoming too frequent in the last years (though seeing the
bright side I enjoy meeting university colleagues in the picket lines that I
don’t have the chance otherwise).</p>

<p>One of our rights is that we don’t need to inform our employer (or any one) our
intentions of taking strike action before we do it, only after we have done it.
That’s a way to avoid that the university reschedule things knowing before hand
that you are not going to be working, reducing, therefore, the impact of the
strike action.</p>

<p>I, and most of the people I know, set an out of office auto-replies on our email
during the strike period. So, when we get emails, they get a nice message back
from us telling them that we are on strike and use that opportunity to tell them
why we do so. Making that some kind of digital picket line.</p>

<p>However, the default mechanism for out-of-office auto-replies has two problems:</p>
<ol>
  <li>
    <p>The out-of-office email are only sent once to the sender during the period of
time booked. So, if you set a 3-day period when you are taking strike action
and the same person emails you everyday, that “sender” will only receive one
auto-reply. This makes sense when you go for holidays and you say in your
email when you will be back.</p>
  </li>
  <li>
    <p>At least on MS systems, all the platforms (calendar, teams, etc…) will show
the dates that you are set out-of-office.</p>
  </li>
</ol>

<p>These two problems might me think about a work around to email back to every
email I get from the same sender (they are giving me more work) and to don’t
disclose the time I’m going to be on strike.</p>

<p>My first approach was to set the out of office manually at the end of the day
for the next day. This solve partially both problems, if someone emails me one
email per day they get one auto response and they only become aware I’m not
available for a meeting just on the day I’m taking the strike action.</p>

<p>However, it requires me every day to log into the system and enable it (when I
may know how many days I’m taking action).</p>

<h3 id="automation-to-the-rescue">Automation to the rescue!</h3>

<p>There’s a tool that comes with the full Office365 that allows you to create
workflows that could work for this. The tool is call <a href="https://emea.flow.microsoft.com/en-us/">power automate</a>, and you
can access through the candy-box icon in the top left corner of any of the
office365 pages. With this tool I’ve created a workflow that does the following,
checks the date of each email I’ve received, if the dates matches with any of
the dates in a spreadsheet, then it will send a reply (with a message I’ve also
set in the spreadsheet).</p>

<p>Let’s see how to do it.</p>

<p>First thing we will do is to create a spreadsheet on one drive. That spreadsheet
will contain a table with two columns: date and message (as shown in Figure 1).</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-Excel-create-table.png" alt="Create a table in excel" /><br /><strong>Figure 1.</strong></p>

<p>To create it as a table, select the columns, go to the insert menu and click on
table. Be as original as you&gt; want on the messages column. As Excel is not as
good as to write long text, I suggest we write it somewhere else and then
copy&amp;paste it in the right cell. Otherwise, if we want to create a new line on
an excel cell press <kbd>alt</kbd>+<kbd>enter</kbd>.</p>

<p>Once we’ve got the spreadsheet ready, we can proceed to create the power
automate flow.</p>

<p>Open <a href="https://emea.flow.microsoft.com/en-us/">power automate</a> from the candy-box menu and select the create button as
shown in the Figure 2. If the tool doesn’t appear in that list, click on the
“All apps” button and search it on the list shown.</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-create.png" alt="Power automate page with the create button highlighted" /><br /><strong>Figure 2.</strong></p>

<p>We are going to start from a blank automated cloud flow (Figure 3).</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-cloud-flow.png" alt="power automate page showing the button of the automate cloud flow" /><br /><strong>Figure 3.</strong></p>

<p>Then we have to provide a name to our flow and its trigger. The name is only
important for us to differentiate this from other flows we may create. Let’s
call it “OoO during Strikes”. The trigger is the action that will kick the flow,
in this case it’s the arrival of a new email. Type “email arrives” and the list
will be filtered. We want to choose “When a new email arrives (V3)” as shown in
the Figure 4.</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-choose-trigger.png" alt="Power automate trigger selection - highlights the “When a new email arrives (V3)” trigger" /><br /><strong>Figure 4.</strong></p>

<p>We click create and we have the start of our flow created.</p>

<p>This trigger allows us to choose which folder to track for emails arriving and
some other options to filter such as the sender, to who was sent (e.g., if you
want to only reply the ones that included you - or any other address - in the
To or CC field, etc.). For now, we will only leave it as it comes by default.</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-email-settings.png" /><br /><strong>Figure 5.</strong></p>

<p>And click at the bottom to create a new step.</p>

<p>Now we will create an “action” that will extract a row from the excel
spreadsheet we created before.</p>

<p>Choose Excel Online (Business) from the menu shown. Then in the following menu choose: “Get a row”.</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-select-excel.png" /> <img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-select-row.png" /></p>

<p>In the “Get a row” dialogue, fill the parameters from the drop-down menus (the
file name, the table, …). As shown in the figure. The “key value” field
doesn’t come from a drop down, we need to write an expression down.</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-select-row-filled.png" /> <img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-expression.png" /></p>

<p>The expression to write is the following:</p>

<p><code class="language-plaintext highlighter-rouge">div(sub(ticks(triggerBody()?['receivedDateTime']), ticks('1899-12-30T00:00:00')), 864000000000)</code></p>

<p>This expression is converting the time from the email to the value that excel
keeps dates in (essentially the number of days from December 30th, 1899).</p>

<p>With this, the “get a row” step will look into the file and the table we linked
it too. Search the “key value” into the “key column”, and if it matches it will
return that row from excel. We are using that now to compose our email to send
back to the sender.</p>

<p>Let’s now create a new step. Here we choose “office 365 Outlook”, and “Send an
email (V2)” as an action.</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-select-email.png" /> <img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-select-send-email.png" /></p>

<p>In the send-email dialogue we need to add “dynamic content”. There we can choose
“From” field from the step below for the “To” field (i.e., send an email to sent
it to us). For the subject we can also use the dynamic content to take the
subject from the email we received and add any other decoration we want.</p>

<p>Finally, for the body, we will select the message column from the row we
extracted in the step below. We can also add any other text in that box that
will be common to all the days.</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-email-fill.png" /> <img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-email-fill-to.png" /> <img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-email-filled.png" /></p>

<p>And that’s all! Click save, go back clicking the back arrow in the workflow top-bar (as shown below),</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-whole-workflow.png" /></p>

<p>Make sure you turn it ON</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-flow-on.png" /></p>

<p>And your email will be sending back emails to everyone who is writing to you.</p>

<p>To test it, we send ourselves and email and wait for the response. You can also
see whether the flow is working or failing at the bottom of the page.</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-flow-results.png" /></p>

<p>Now that we’ve done this… how do you think we could only send replies to the
people in our university? For that we would need to create an intermediate step
that creates a condition and checks whether the email ends by our university
domain. If does so, then we proceed send the email, otherwise, send another
general email.</p>

<p><strong>Update:</strong> Check the <a href="/blog/2022/03/23/out-of-office-2.html">next post to see how to include that condition</a>.</p>]]></content><author><name></name></author><category term="strikes" /><category term="university" /><category term="software" /><summary type="html"><![CDATA[Sadly, strikes are becoming too frequent in the last years (though seeing the bright side I enjoy meeting university colleagues in the picket lines that I don’t have the chance otherwise).]]></summary></entry><entry><title type="html">Setting an out of office during a strike period - sending replies only to the people on your university</title><link href="https://david.ps/blog/2022/03/23/out-of-office-2.html" rel="alternate" type="text/html" title="Setting an out of office during a strike period - sending replies only to the people on your university" /><published>2022-03-23T00:00:00+00:00</published><updated>2022-03-23T00:00:00+00:00</updated><id>https://david.ps/blog/2022/03/23/out-of-office-2</id><content type="html" xml:base="https://david.ps/blog/2022/03/23/out-of-office-2.html"><![CDATA[<p>Yesterday’s post ended with a question about how to send the answers only to the
people sending you emails from your university. Let’s look how we can implement it!</p>

<p>We are starting from <a href="/blog/2022/03/23/an-out-of-office-during-strike-period.html">the flow that we created yesterday</a> - so, if you haven’t
done it yet, then start there. I’ll wait for you here.</p>

<p>Our previous power automate wokflow looked like this:</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-23-power-automate-whole-workflow.png" /></p>

<p>Now, we want to introduce a condition before sending these “personalised”
replies only to senders from a particular domain, for example,
<code class="language-plaintext highlighter-rouge">youruniversity.ac.uk</code>. In programming this is call <a href="https://en.wikipedia.org/wiki/Control_flow">control
flow</a>, in particular the conditional expression.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IF some conditions happens 
  THEN do x 
  ELSE do y
</code></pre></div></div>

<p>So, in our case this could be as</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IF the email of the sender ends by `youruniversity.ac.uk`
  THEN send a reply
  ELSE do nothing
</code></pre></div></div>

<p>Let’s try to code this into our flow. First we need to think where we want to
include it. Certainly we want it after the receive email box (the trigger of the
flow) and before the “send email” action. We have two options, just after the
email is received, or after the excel spreadsheet is loaded. Since the
spreadsheet only contains the messages for when we want to send the reply, then
it makes sense to make it before that. However, if we were to also automate the
responses to external people, then we would want it after the row from the excel
file is extracted.</p>

<p>When editing the flow, we can add a new step by clicking in the button at the
bottom, or by clicking the plus symbol that appears when hovering in the
connectors between the steps, as shown by this image:</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-24-power-automate-add-intermediate.png" /></p>

<p>We are going to create a condition action before getting the excel row. Choose add
an action, and from the menu, choose Control. Inside the Control menu, pick condition.</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-24-power-automate-add-control.png" /> 
<img height="200px" src="/gallery/images/2022/03/2022-03-24-power-automate-add-condition.png" /></p>

<p>The flow should look like:</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-24-power-automate-flow-after-control.png" /></p>

<p>And by dragging and dropping the excel and send email actions into the “yes”
box, should then look like this:</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-24-power-automate-flow-after-control-drag.png" /></p>

<p>We’ve got so far the skeleton of what we want to happen.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. Trigger the flow when an email is received
2. IF &lt;condition to create&gt; happens
     THEN
        - extract the row for today
        - send a reply with the personalised message
     ELSE
        - do nothing
</code></pre></div></div>

<p>What we are missing is to set the condition. Remember, we want to only reply
emails that comes from our own institution. Therefore, we need to look up the
address of the sender and check if the end of the email is like
<code class="language-plaintext highlighter-rouge">youruniversity.ac.uk</code>.</p>

<p>To do this step, we need to fill the three fields under the condition action.
The first one is the value we want to compare. Clicking there you’ll see all the
dynamic content generated from the previous action, and between these the “From”
field - i.e., the email of the sender. In the second field we are going to set
the condition. From all the options there, the simplest one for our needs is
“ends with” and the last field we set the pattern we want the address of the
sender to match. <code class="language-plaintext highlighter-rouge">youruniversity.ac.uk</code>. At the end, it should look like:</p>

<p><img height="200px" src="/gallery/images/2022/03/2022-03-24-power-automate-add-condition-filled.png" /></p>

<p>And that’s it! Now only the emails from a sender with a domain as specified in
the condition will get through the “If yes” branch and get a reply.</p>

<p>One more thing! Remember to turn the workflow off when you don’t want to process
your incoming email. You won’t be sending replies if there’s not a date matched
on the spreadsheet, but we probably won’t like to have some machine somewhere in
the world running these workflows once and again to fail all the times.</p>]]></content><author><name></name></author><category term="strikes" /><category term="university" /><category term="software" /><summary type="html"><![CDATA[Yesterday’s post ended with a question about how to send the answers only to the people sending you emails from your university. Let’s look how we can implement it!]]></summary></entry><entry><title type="html">February: a ‘functional’ month</title><link href="https://david.ps/blog/2022/03/01/a-functional-month.html" rel="alternate" type="text/html" title="February: a ‘functional’ month" /><published>2022-03-01T00:00:00+00:00</published><updated>2022-03-01T00:00:00+00:00</updated><id>https://david.ps/blog/2022/03/01/a-functional-month</id><content type="html" xml:base="https://david.ps/blog/2022/03/01/a-functional-month.html"><![CDATA[<p>Long time no see!! I should write more often in here, right?</p>

<p>We’ve started a new month in this year (yeah 2022 already) and I’ve been
yesterday and today wishing <code class="language-plaintext highlighter-rouge">x2</code> happy new year (i.e., for 2021 and 2022) to so
many colleagues I’ve not seen since we left the offices. So, Happy new years to
you too!!</p>

<p>I’ve took over the “organisation” of the <a href="https://m-x-research.github.io/">M-x Research group</a> - an
<a href="https://www.gnu.org/software/emacs/">emacs</a> group for people working on research projects. We’ve <a href="https://m-x-research.github.io/2022/01/21/a-new-year.html">planned some
activities for this year</a>, and one of them was making February a
functional month - i.e., to practice some elisp by our own solving some problems
and share it with the group for feedback and encouragement. Unfortunately, I
don’t think I did a good job advertising this challenge as I think I’ve been the
only one trying to do it, and this February had a lot of strikes in UK
universities, making it harder for people to engage with volunteering activities.</p>

<p>Nevertheless, I managed to do some of it! In average I’ve done 1 problem every
two days! (and I’ll try to continue learning on March). Here I try to summarise
how has been my learning voyage so far.</p>

<h2 id="learning-elisp">Learning elisp</h2>

<p>I’ve been an <a href="https://www.gnu.org/software/emacs/">emacs</a> user since early 2000s. I still remember the first time I
saw the emacs window and how confused I was with the word *buffers*. I only
learnt things here and there from a printed copy of the emacs manual that was in
the student’s computer room at my uni. Over the years I learnt that the
configuration of the editor was managed by some weird language that used a lot
of parenthesis and without knowing much about it and via copy&amp;paste I got to set
up things somehow right for me. It hasn’t been till two years ago that I started
to consider learning that magic language. I have tried without really
progressing much learning lisp from</p>

<ul>
  <li><a href="https://mitpress.mit.edu/sites/default/files/sicp/index.html">Structure and Interpretation of Computer Programs</a>, via reading the book and <a href="https://www.youtube.com/watch?v=-J_xL4IGhJA&amp;list=PLE18841CABEA24090">watching the classes from 1986</a>; and</li>
  <li><a href="http://landoflisp.com/">Land of Lisp</a> book.</li>
</ul>

<p>I didn’t really stick to any of these… other things happened and I didn’t progress much.</p>

<p>This year, however, it’s been a bit different! Our first event of <a href="https://m-x-research.github.io/">M-x
Research</a> was an introduction to (e)lisp: <a href="https://www.youtube.com/watch?v=MMRAfd9_d6k">Functional programming:
an (Emacs) Lisp view 1/n</a> (<a href="http://purl.org/net/epubs/work/51348587">slides</a>) by Jens
Jensen. That was on the first of February and I had already “decided” that
February was going to be a functional month.</p>

<p>From what I learnt that day I set the goal to solve all the <a href="https://exercism.org/">exercism</a> problems
on the <a href="https://exercism.org/tracks/emacs-lisp">elisp track</a>.</p>

<p><a href="https://exercism.org/">exercism</a> is an online platform that contains some programming problems to be
solved directly on the website (similar to <a href="https://www.codewars.com/">codewars</a>). Once you submit your
solution it allows you to see how others have solved the same problem.</p>

<p>There are 28 exercises for elisp, and I thought it would be great if I could do
one a day. It turned out I have only manged to complete 14 of them, but I’m so
so so happy of my progress!</p>

<p>One of the things I’m more impressed with - and I don’t know whether that’s a
good practice on functional languages yet - is that I’ve not created any
variable on any of the problems and that many of the problems had my head
spinning with recursion. Also, how many things from Jens’s talk “clicked” at
different times during these exercises - I probably should re-watch that as
there are probably many other things I hadn’t absorbed and I’ve not realised
yet.</p>

<p>I’ve <a href="https://github.com/dpshelio/exercism-voyage">saved my progress on a repository</a>, and included <a href="https://github.com/dpshelio/exercism-voyage/blob/main/elisp/learning-elisp.org">a file with
things I was learning as I was going</a>. During this set of exercises
I’ve learnt to use the <code class="language-plaintext highlighter-rouge">info</code> command in emacs (<code class="language-plaintext highlighter-rouge">C-h i</code>), and how to search in
the <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/">elisp manual</a>. Interestingly, I’ve only read so far the firs four sections,
up to lists, and it feels as I have super powers now. I can’t even imagine
what I’ll be able to do when I read the other 37 other sections!</p>

<p>I’ve also starting to understand how to read the error messages and it has also
helped me to debug problems that have appeared on emacs modules during this time.</p>

<p>In summary, <a href="https://www.youtube.com/watch?v=MMRAfd9_d6k">Jens’s lecture</a> helped a lot to get me started,
<a href="https://exercism.org/">exercism</a> has been a great companion to get me “motivated” and to learn how
others have solved the same problem (I still need to investigate the mentoring
program they offer), and though the problems I’ve solved are labelled as easy,
it feels so good to achieve these little things day after day. Now lisp doesn’t
look that mysterious to me (well, it still does, but I can read it now!).</p>

<p>👋 Thanks for reading!</p>]]></content><author><name></name></author><category term="learning" /><category term="emacs" /><summary type="html"><![CDATA[Long time no see!! I should write more often in here, right?]]></summary></entry><entry><title type="html">What the learners see when I use zoom to teach</title><link href="https://david.ps/blog/2021/07/06/teaching-with-zoom.html" rel="alternate" type="text/html" title="What the learners see when I use zoom to teach" /><published>2021-07-06T00:00:00+00:00</published><updated>2021-07-06T00:00:00+00:00</updated><id>https://david.ps/blog/2021/07/06/teaching-with-zoom</id><content type="html" xml:base="https://david.ps/blog/2021/07/06/teaching-with-zoom.html"><![CDATA[<p>Since I started teaching online I thought that a good approach to screen sharing
was to only share half of my desktop. My reasoning being that this way I could
keep the other half of my screen for my teaching notes and the chats (with the
learners and other instructors), whereas the learners could use their other
half for following along.</p>

<p>However, zoom is, by default, configured to switch to full-screen mode when
someone is sharing the screen. It’s useful if you are watching some talk or
lecture, but not that much if you need to code-along. As an instructor I didn’t
realise this was a problem till a couple of weeks into one of the courses when
a student gave us the following feedback:</p>

<blockquote>
  <p>the aspect ratio for Davids screen when he is screensharing means that anything he types is pretty much unreadable.</p>
</blockquote>

<p>I believe this is related to that. Also, if the screen gets maximised and I’m
sharing only half of the screen, they get half of the screen in the centre of
their screen, leaving only one quarter on either side to work.</p>

<p>To get ready for my last workshop, and <a href="/blog/2021/05/02/teaching-with-obs.html">after multiple problems I’ve had wit
zoom lately</a>, I thought it would be better to test how my screen
was seeing on different operative systems and using different clients (the
desktop vs the browser client). Let’s see what I found.</p>

<p>In the sections below I’m testing by sharing some files and a terminal running on
VS Code. The sharing was done by <a href="https://support.zoom.us/hc/en-us/sections/201740106-Screen-Sharing">selecting only a portion of the screen option
in the screen sharing menu in zoom</a>.</p>

<h2 id="what-would-you-see-when-having-a-single-screen">What would you see when having a single screen?</h2>

<h3 id="browser-vs-zoom-desktop-app">Browser vs Zoom desktop app.</h3>

<p>First I tried on Linux. On the left, we’ve got the web client on Firefox, on the
right, the zoom app.</p>

<!-- ![Left shows the zoom interface from Firefox, on the right the zoom interface from the desktop app](/gallery/images/2021/07/2021-07-06-zoom-app-firefox-webcam.png) -->
<p><img height="400px" alt="Left shows the zoom interface from Firefox, on the right the zoom interface from the desktop app" src="/gallery/images/2021/07/2021-07-06-zoom-app-firefox-webcam.png" /></p>

<p>There are already some interesting differences.</p>

<ol>
  <li>
    <p>The area! In the screenshot, the shared area shown by Firefox covers a
948x1133 px, whereas the desktop zoom app shows 879x1041. In Firefox you get
17% more than on the desktop.</p>

    <!-- ![comparing the shared area of both clients](/gallery/images/2021/07/2021-07-06-zoom-app-firefox-webcam-area.png) -->
    <p><img height="200px" alt="comparing the shared area of both clients" src="/gallery/images/2021/07/2021-07-06-zoom-app-firefox-webcam-area.png" /></p>

    <p>Why is that? Well, because the video from the presenter is shown as a
floating window, that you can move around (even outside of the view) as you
please, whereas in Zoom you are stuck to either have it on the top or on the
side. Ideally, I could control where to appear, but <a href="/blog/2021/05/02/teaching-with-obs.html">I’ve had other problems
with that</a>.</p>
  </li>
  <li>
    <p>The zoom actions menu. It hides on both applications, however, in Firefox
doesn’t hide any content, whereas it does so in the desktop app.</p>

    <!-- ![highlighting the menu bar of both Firefox and Zoom](/gallery/images/2021/07/2021-07-06-zoom-app-firefox-menu-bar.png) -->
    <p><img height="300px" alt="highlighting the menu bar of both Firefox and Zoom" src="/gallery/images/2021/07/2021-07-06-zoom-app-firefox-menu-bar.png" /></p>

    <p>I find this very frustrating when you want to see something and the app keeps
showing you a bar just because you hovered the mouse over. And by the time
you calm down and stop moving the mouse, the thing you wanted to see is
gone.</p>
  </li>
</ol>

<p>Focusing only on the shared screen, we should then prefer to use Firefox over
the desktop app. You get more space and nothing hiding any of the important
bits. However, if you need to also use the chat, then this may not be the best
option. (We tend to not use the chat but a shared document instead).
Additionally, you could even get more space hiding some bits from the firefox
bar that you may not need.</p>

<p>How does this look in the Windows world?</p>

<p>In windows, the first browser I tried was Chrome, and to my surprised, it was
different from Firefox on Linux. A popup appeared with the great news when you
connect saying that you’ve got now a gallery/images view on Chrome! 🤦 That would have
been awesome if it would let you choose which view you’d like. But it doesn’t!</p>

<!-- ![Connecting from Windows, desktop application on the left vs Chrome on the right](/gallery/images/2021/07/2021-07-06-zoom-windows-chrome.png) -->
<p><img height="300px" alt="Connecting from Windows, desktop application on the left vs Chrome on the right" src="/gallery/images/2021/07/2021-07-06-zoom-windows-chrome.png" /></p>

<p>With Firefox, however, you can still get the floating window for the webcams, but
the area is not that different! The desktop app being ~5% larger. Though, the bottom
menu bar will still hide part of the shared screen.</p>

<h3 id="multiple-screens">Multiple screens</h3>

<p>If you’ve got more than one screen then you haven’t got many problems with
space, right? However, there’s a nice feature of zoom that you can enable when
using multiple screens, <a href="https://support.zoom.us/hc/en-us/articles/201362583-Using-Dual-Monitors-with-the-Zoom-Desktop-Client">enabling dual monitors</a>. This separates
the zoom application into two windows, one for the bits being shared, and another
where you can see the webcam of the participants.</p>

<!-- ![Zoom desktop app with the dual-monitor feature enabled, left half showing the shared content and the right half shows the video participants](/gallery/images/images/2021/07/2021-07-06-zoom-app-windows-multiplescreen.png) -->
<p><img height="300px" alt="Zoom desktop app with the dual-monitor feature enabled, left half showing the shared content and the right half shows the video participants" src="/gallery/images/2021/07/2021-07-06-zoom-app-windows-multiplescreen.png" /></p>

<p>This is the best of all the options! You get the zoom menu bar on the
participants’ view, not hiding any of the content! Sadly, you need to have two
screens… or do you? There’s some trickery we can do to our computers to make
<a href="https://answers.microsoft.com/en-us/windows/forum/all/how-to-enable-a-fake-second-display-in-windows-10/3c02e2ee-cade-4916-be46-390e59ea2a04">zoom believe that there are multiple screens</a> and make this
view available. However, this is not something I can ask my learners to set up
before a class or a workshop. If you start zoom with a second screen connected,
then I believe zoom keeps believing that you have that set up for the following
calls. (Though it seems absurd that you find in such a situation, this trick has
helped me many times where I can steal my girlfriend’s second monitor to start zoom
and return it to her afterwards).</p>

<h2 id="final-thoughts">Final thoughts</h2>

<p>First of all, I need to tell my students to <a href="https://makeitallwork.com/how-to-prevent-zoom-automatically-going-full-screen/">disable the “enter full screen when
a participant shares screen” option</a>. This will at least let
them place the window whenever they want.</p>

<p>Secondly, I need to test and re-test every time before teaching. In my <a href="/blog/2021/05/02/teaching-with-obs.html">last
post, I explained why I had to use OBS</a>. That worked fine for the
teaching I needed to do in that time which didn’t require live-coding. However,
when trying to use OBS to share only half of my screen I found a different
problem. Since Zoom believes it’s your webcam that gets transmitted, Zoom keeps a
webcam ratio on the screen, instead of what you normally get from a screen share,
defeating the point of sharing half of the screen.</p>

<p>This time I went to “fix” zoom on my machine, and I did it by trying different
versions you can install in Linux. The one that worked on my machine (Archlinux
with Gnome over Wayland) was using <a href="https://www.flatpak.org/">flatpak</a>. Or so I thought. I run
all these tests, took lots of screenshots to share with my learners the optimal
way to connect. During these tests I had noticed that my mouse pointer was not
seen at all, but that was not new, I had to be more specific with my language.
However, when I started teaching, I got one of the attendees saying “the screen
is not changing”… 😱 Panic moment again! Zoom was not streaming my screen to
the participants. Or well, it was, but only when I was hovering the mouse over
the screen share bar that zoom creates. This meant I had to type and move the
mouse every couple of seconds and made the flow of the workshop not as smooth as
I would have liked it. Updating zoom again a couple of days later (from 5.6.1
to 5.7.xxx) seems to have fixed that issue on my latest tests.</p>

<p>If that was not enough, one of the attendees that tried to follow my indications
to use Firefox on the university desktop couldn’t get the sound working,
whereas the zoom app worked fine. Maybe I’ll design a flowchart before the next
academic year starts so the students could get the most optimal setup with a
less confusing set of instructions.</p>]]></content><author><name></name></author><category term="teaching" /><category term="carpentries" /><category term="tools" /><category term="SwC" /><category term="workshops" /><summary type="html"><![CDATA[Since I started teaching online I thought that a good approach to screen sharing was to only share half of my desktop. My reasoning being that this way I could keep the other half of my screen for my teaching notes and the chats (with the learners and other instructors), whereas the learners could use their other half for following along.]]></summary></entry><entry><title type="html">Using OBS to teach</title><link href="https://david.ps/blog/2021/05/02/teaching-with-obs.html" rel="alternate" type="text/html" title="Using OBS to teach" /><published>2021-05-02T00:00:00+00:00</published><updated>2021-05-02T00:00:00+00:00</updated><id>https://david.ps/blog/2021/05/02/teaching-with-obs</id><content type="html" xml:base="https://david.ps/blog/2021/05/02/teaching-with-obs.html"><![CDATA[<p>A week ago I was running a workshop (online!) about git and GitLab for
colleagues at work. After a whole year of teaching online with Zoom, I thought I
was prepared for everything that could go wrong. However… I didn’t expect
Zoom to fail me now.</p>

<h4 id="zoom-failing-me-or-me-being-super-confident">Zoom failing me (or me being super confident…)</h4>

<p>I hadn’t taught that particular workshop for more than a year and I have
prepared over the days before some diagrams to account for the whiteboard I had
in the room last time I taught it. We were starting earlier than usual, so I
left everything ready the night before, laptop, browser with the tabs open,
terminal with the teaching setup, etc. At 9’00 am I started introducing myself
and the workshop to the participants - all good so far (everyone could see and
hear me). Then the teaching started.</p>

<p>Let me make a parenthesis and explain first my setup. I’ve found very useful the
Zoom feature that allows <a href="https://support.zoom.us/hc/en-us/articles/201362153-Sharing-your-screen-content-or-second-camera">sharing only a portion of the screen</a> when
teaching with a single screen. I’ve normally got the terminal, vs code, browser
or whatever I need to show on my left half of the desktop. That allows me to
keep my notes, the chat window, list of participants, etc on the right half.</p>

<p><img height="400px" alt="Screen layout with Zoom" src="/gallery/images/2021/05/2021-05-02-Teaching-with-zoom.png" /></p>

<p>When shared your screen like this, it’s suggested to tell your “viewers” to exit
from the full-screen mode that Zoom starts automatically by default when someone
shares their screen. If they don’t do so they’ll have my content in the middle
of their screen and one-quarter of the screen free on each side to code-along.
Not very useful, right? I’d recommend <a href="https://makeitallwork.com/how-to-prevent-zoom-automatically-going-full-screen/">you disable that default
behaviour</a>.</p>

<p>Ok, back to my class. All was ready, I was full of energy and enthusiasm to try
this fun workshop. But, when I shared my screen, I heard one saying “I don’t see
anything”, and another “me neither”… Hum… That’s strange, Zoom is telling me
I’m sharing. Ok, let’s try to get out of the call and back again. Same thing.
Ok, then, let’s try to share the full screen (and keep my notes in my head).
Nada. Then one of the participants said, “maybe you can try restarting the
computer”. Ha! I did run an update recently, maybe that’s why. I restarted the
computer, reconnect to the call… and I still can’t share. It was already 9’25
and we hadn’t started, so I decided to give it a go with my personal laptop. I
connect, share the screen and yeah! people can see my screen!!! But all my
files, diagrams, notes, etc. were on the other computer. Thankfully I got all my
files in a single place that I could easily <code class="language-plaintext highlighter-rouge">scp</code> across, also most of the
software I needed to be installed there too. In the end, we started almost 40 minutes
late but we managed to cover the important bits we needed. Not as interactively
as I had prepared, but people were happy at the end.</p>

<p>This last week I was having more teaching to do, and this time with less margin
for problems as the workshop was two full days from 9 to 5. I knew we were going
to use Zoom again, so I had to fix my problem before the teaching started. I
browsed a bit to see whether people were having the same problem I had but I
couldn’t find anything. I believe it may be related to the update to the
newest version of gnome (40!) or the latest update of Zoom. I haven’t had the
time to do much testing.</p>

<h4 id="entering-obs">Entering OBS</h4>

<p>I had used <a href="https://obsproject.com/">OBS</a> (Open Broadcast Software) twice before, once for recording
a tutorial for the translations of the Carpentries and another time for a talk
about an autogradding tool (<a href="https://submitty.org">submitty</a>). That last time had been more
than a year ago and I had some problems. However, I had heard that the new version
made some things easier! So, I decided to give it a go. Here I’m explaining the
setup I used, how I “embedded” into my flow while teaching, what went well and
what things didn’t work that well.</p>

<p>OBS is very neat! You can have multiple scenes, and switch between them with
hotkeys from almost anywhere on your desktop. A scene is composed of one or
many sources. A source could be your webcam, a static image, text, a window
capture, etc. Each of these sources can be placed wherever you want in the scene.
For example, you can get something like the image below, where I’ve got a scene
(bottom left box) named Fun scene, with four sources (box next to “scene”): an
image, some overlay text, a browser (shown below of OBS Studio) and my webcam.</p>

<p><img height="400px" alt="OBS basic setup" src="/gallery/images/2021/05/2021-05-02-obs-desktop.png" /></p>

<p>If you want to stream that through Zoom/MS Teams/Blackboard/Jitsi or your
favourite video conference tool, OBS provide now an easy way to tell these
programs that you have a new webcam and that the content of that webcam is what
you see on OBS. That’s done by clicking first on the “Start Virtual Camera” you can
see on the set of buttons on the right-hand side, and then selecting that new
webcam on your video conference tool. The image below shows where you can select
that on Zoom.</p>

<p><img height="75px" alt="Select OBS Camera on Zoom" src="/gallery/images/2021/05/2021-05-02-zoom-camera.png" /></p>

<p>If for any reason you don’t see the camera there, close the video conference
program and open it again. At least that worked for me in Zoom.</p>

<p>I don’t know about other operative systems or Linux flavours, but for me, if OBS is
using the webcam other programs can’t get the feed from the webcam at the same
time. So, in Zoom, for example, I either have OBS camera or nothing, even if I
got the option to choose the webcam.</p>

<p>This is a problem if I want to switch between them while I’m in a call as I can
only show what I’ve got in OBS. Or… is it really a problem? You can have
multiple scenes in OBS, remember? So, we could have one with only the webcam,
and another with the scene where I’m presenting/sharing the stuff I want.</p>

<p>In this screenshot, I’ve got the studio mode enabled (button above the settings
on the bottom left corner). With it, you get two views, a preview on the left and
a program on the right. The view on the right would be what you are streaming,
the one on the left is the one you can transition to. The buttons between these
views let you choose which transition you want to use between one scene and
another. There are a few options where to choose from, and a few more can be
installed with additional plugins.</p>

<p>Great! The first problem solved! Now we don’t need to switch cameras in Zoom.</p>

<p>However, there’s another problem we will face. In Zoom (and similarly in other
programs), when one shares the screen that gets “spotlighted” for everyone. This
is, it becomes the main view for all, even when other people are talking.
However, if we are telling these programs that we are using a “webcam”, then
they are going to treat it as such and the main view will be switching from
presenter to others if they talk. Fortunately, in Zoom, you can <a href="https://support.zoom.us/hc/en-us/articles/201362653">“spotlight” a
particular presenter</a>. This was one of the problems I had last
year when I gave a talk using this because MS Teams didn’t allow me (and still
doesn’t) to share my screen, we recorded the presentation for others that
couldn’t attend it live… only to discover then that the recording had me and
my slides in a corner.</p>

<p><img height="350px" alt="Select OBS Camera on Zoom" src="/gallery/images/2021/05/2021-05-02-teams-fail.png" /></p>

<p>At that time I didn’t think about that, but it seems <a href="https://support.microsoft.com/en-us/office/spotlight-someone-s-video-in-a-teams-meeting-58be74a4-efac-4e89-a212-8d198182081e">MS Teams now has that option</a>.
I don’t know whether that works for the recordings too.</p>

<p>For the workshop I had this week I prepared three scenes:</p>

<ul>
  <li>one with me (as the webcam)</li>
  <li>another sharing my browser (where I had my slides, the shared document, etc.)</li>
  <li>another with the browser and myself in a corner</li>
</ul>

<p>The last one was to give a more personal view of the slides, so people could see
my expressions and gestures when I was talking and not only the slides.</p>

<p>I set some hotkeys in OBS to transition between scenes. To do so, click
on settings (bottom right corner), select the hotkeys section, and find the name of
the scenes you want. Each of them has a “switch to scene” setting that you can
input your keybindings for it. I used:</p>

<ul>
  <li><kbd>ctrl</kbd>+<kbd>alt</kbd>+<kbd>m</kbd> for the one with the presentation and <kbd>m</kbd>yself</li>
  <li><kbd>ctrl</kbd>+<kbd>alt</kbd>+<kbd>n</kbd> for the one with only the presentation and <kbd>n</kbd>othing else</li>
  <li><kbd>ctrl</kbd>+<kbd>alt</kbd>+<kbd>,</kbd> for just me (the key next to <kbd>m</kbd>)</li>
</ul>

<p>With that, I got a good mnemonic that was also with three keys next to each
other in a meaningful order (<kbd>n</kbd> for presentation; <kbd>m</kbd> for
both, <kbd>;</kbd> for me).</p>

<p>From my side, this worked very well! I’ve got the transitions working well
through the teaching and I was happy with myself. However, it was still not
perfect! And thankfully, I’ve got a lot of feedback from the participants on
this training session.</p>

<p>On the first day one of them wrote on the feedback:</p>

<blockquote>
  <p>Very minor bit of feedback but in the morning some of David’s face was often
covered by the Zoom banner. Might be better to have it at the top of the
slides instead of the bottom.</p>
</blockquote>

<p>Of course, I hadn’t accounted for that! I had placed myself in the bottom right
corer of the screen and many video conference tools have the obsession to place
menus in top of the stream (and though they “disappear” after a while they come
back again if your mouse passes by and you need to wait again a bit for it to
disappear).</p>

<p>This was an easy fix! select the scene with me in the bottom corner and move
the source to the top. Checked that the slides didn’t have anything important on
that side and all sorted. (Read <a href="#set-up-slides">below how to make sure you are not hiding any text on your slides</a>)</p>

<p>At the end of the workshop I received some more feedback:</p>

<blockquote>
  <p>When Evgenij (the other instructor) shared the screen I could use a
side-by-side gallery of participants + shared screen focus (and annotation was
available). When David used OBS these Zoom tools weren’t available, which
worked less well for my setup.</p>
</blockquote>

<p>This is the problem I mentioned above, Zoom is not seeing a screen share, but a
webcam. Then we need to spotlight it if we want to keep it visible for everyone
even if someone else talks. Though I don’t have a solution about annotating the
presentation by the participants (and I saw a good use for it in this workshop!)
I have, however, a solution about seeing the other participants at the same
time. This is by <a href="https://support.zoom.us/hc/en-us/articles/201362583-Using-Dual-Monitors-with-the-Zoom-Desktop-Client">enabling the dual monitors setting on Zoom</a>.
However, as the name says you need to have two monitors for Zoom to be able to
work in this mode. I’ve found the following:</p>

<ul>
  <li>There are some ways to create “virtual” screens on Linux [<a href="https://github.com/dianariyanto/virtual-display-linux">1</a>],
[<a href="https://github.com/brunodles/linux-second-screen">2</a>], [<a href="https://github.com/kbumsik/VirtScreen">3</a>], but haven’t tested them. I doubt they’ll work as I’m
using Wayland.</li>
  <li>If you start Zoom and a call with two monitors and then disconnect one, Zoom
doesn’t change behaviour. That’s good, that means I could trick Zoom to
believe there is another screen.</li>
  <li>
    <p>So, if when I connect only a cable/dongle to my laptop, then set</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo </span>on <span class="o">&gt;</span>  /sys/kernel/debug/dri/0/HDMI-A-1/force
</code></pre></div>    </div>

    <p>as root (<a href="https://wiki.archlinux.org/title/Extreme_Multihead#Using_an_HDMI_output">thank ArchWiki</a>), and on the display settings,
select a new display and enable the toggle for it. When I start a call on
Zoom, it starts with the two screens!</p>
  </li>
</ul>

<p>The next piece of feedback I got was:</p>

<blockquote>
  <p>OBS also blurred the quality for me</p>
</blockquote>

<p>This is a bit trickier. I don’t know exactly where that’s happening, on OBS
settings or Zoom. Let’s see the options I’ve found:</p>

<ul>
  <li>
    <p>OBS offers some pre-configured profiles depending you are using it for
streaming, recording or virtual camera. On each of these, you can select the
output resolution. I’ve played a bit with the video output settings and
checked the outputs with Zoom on my side, and connected to a call from the
browser. On my side, it seems OK, but when comparing with what’s obtained on
the other side is really bad. This is how it looks!</p>

    <p><img height="350px" alt="Image quality of the source, Zoom remote, and Zoom locally" src="/gallery/images/2021/05/2021-05-02-zoom-quality.png" /></p>

    <p>There are more settings on OBS, but I believe there may be something on the
 Zoom side.</p>
  </li>
  <li>
    <p>Zoom has also some settings for the webcam that I believe might also affect
the quality of what’s sent: checks for original ratio, HD, touch up my
appearance, and, under the advanced settings, optimise video quality with
de-noise. Changing any of these parameters doesn’t show any improvement
compared with what I showed above. However, when trying to connect to the call
from a different computer - either using the web or desktop app - I could see
a large improvement! The desktop app is slightly better.</p>
  </li>
</ul>

<p>I believe the “touch up my appearance” was selected during the workshop and that
might have been what produced some of the blurs on the participants’ side. Now I
wonder how many students from this year might think I’m way younger! Whenever we
meet again in person people will think I’ve aged super quick because Zoom is
hiding all my wrinkles!</p>

<p>Anyway, I should keep testing these quality parameters. Maybe recording the
calls and checking that output. Doing these tests I’ve noticed that the web
client besides showing a reduced quality, it also shows a delay that varies from
2-3 seconds when selecting text on a website, to up to 10 seconds if showing a
video.</p>

<p>Let’s now look at some neat things I used on my OBS setup that may be helpful
for others:</p>

<h5 id="webcam-in-a-circle">Webcam in a circle</h5>

<p>I don’t know exactly why, but I’ve found it more “appealing” to have my head in a
circle than in a square. Maybe because it leaves more space for what I’m
presenting removing all these useless corners. In any case, this is what you
need to do (based on the [How to get a circular webcam in OBS
Studio][obs-circle] by Gael LEVEL):</p>

<ul>
  <li>We need to create a <a href="https://en.wikipedia.org/wiki/Mask_(computing)#Image_masks">mask</a>. A mask is a grayscale image that
indicates the opacity of what we want to show. Therefore we want to create a
black image the same size as our webcam and draw a white circle in it. I
used <a href="https://www.gimp.org/">gimp</a> for that. If your webcam has a resolution of 1280x720 pixels
then you can get <a href="/gallery/images/2021/05/2021-05-02-circle-webcam.png">my mask</a>. Otherwise, follow the steps below:
    <ul>
      <li>Find the right size of your webcam. One way to do so is using the
<a href="https://webcamtests.com/resolution">resolution tool at the webcamtest website</a></li>
      <li>Create a new image with these dimensions and fill the image with black.</li>
      <li><a href="https://alvinalexander.com/gimp/gimp-how-to-create-draw-circle-in-gimp-tutorial/">Create a circle</a> with the same height as the
background in the centre and fill it white.</li>
      <li>Export that image as a png.</li>
    </ul>
  </li>
  <li>Create a new scene (name it for example <code class="language-plaintext highlighter-rouge">webcam</code>).
    <ul>
      <li>In that scene add a new Video Capture Device source.</li>
      <li>If the webcam source video is larger than your “screen”, then you can click
the right button, see the “transform” menu, and select “fit to screen”.</li>
      <li>rename the source with something meaningful, e.g., <code class="language-plaintext highlighter-rouge">webcam src</code></li>
    </ul>
  </li>
  <li>Let’s now create a new scene (name it: <code class="language-plaintext highlighter-rouge">webcam full</code>),
    <ul>
      <li>As a source for that scene, we will choose a scene, <code class="language-plaintext highlighter-rouge">webcam</code>.</li>
      <li>Yes! it seems that now we have two scenes that are equal, but this will be
useful later.</li>
    </ul>
  </li>
  <li>Let’s now create a scene with us full screen but in a circle, let’s name it <code class="language-plaintext highlighter-rouge">webcam circle</code>
    <ul>
      <li>We add as a source the <code class="language-plaintext highlighter-rouge">webcam</code> scene as we did before.</li>
    </ul>
  </li>
  <li>Now, on the <code class="language-plaintext highlighter-rouge">webcam circle</code> scene (not in the source!) we click with the right
button and select filters. In the new window that appears we add a new “Image
mask/Blend” effect filter. You can name it as you like.
    <ul>
      <li>In the filter options select:
        <ul>
          <li>Type: Alpha Mask (Colour Channel)</li>
          <li>Path: the path to your mask file created above.</li>
          <li>Colour: <code class="language-plaintext highlighter-rouge">#ffffff</code> (white)</li>
          <li>Opacity: 100.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Now you can include that scene <code class="language-plaintext highlighter-rouge">webcam circle</code> as a source to the scene where
you are sharing your browser/slides/desktop program/etc…</li>
</ul>

<h5 id="changing-scenes-with-a-nice-move-transition">changing scenes with a nice move transition</h5>

<p>If you’ve followed up the names from the steps above for the source and the
scenes, then you won’t have many troubles in this step. Good luck if you
haven’t.</p>

<p>The <a href="https://obsproject.com/forum/resources/move-transition.913/">move transition</a> plugin produces very neat transitions between
scenes when these scenes contain the same sources but they may have different
sizes, orientations or are in different locations. Check the <a href="https://www.youtube.com/watch?v=gLS8g5YqnIM">“Move Transition -
Install/Set Up - OBS” video by Optomod</a> to see how it works.</p>

<p>The installation steps depend on your operative system and Linux flavour. For
<a href="https://aur.archlinux.org/packages/obs-move-transition/">ArchLinux there’s a package for it at the Arch User repository</a>.</p>

<p>Once installed, you can open the “move” settings under the “scene transitions”
column. The first block of settings asks how you want the plugin to find out
when two sources are “compatible”. I’ve got the three options selected (but the
middle one - “numbers removed from end” is not required with the steps from
above. You’ve got a bunch of other options to configure in these settings, play
with them.</p>

<p>Now, using the studio mode, you can preview the transitions by clicking on move in
the transitions menu shown between the two previews.</p>

<h5 id="define-transitions-between-scenes">define transitions between scenes</h5>

<p>A very useful plugin I used was the <a href="https://obsproject.com/forum/resources/obs-transition-matrix.751/">obs transition matrix</a>. This
<a href="https://visualsbyimpulse.com/blog/obs-transition-matrix/">“Transition Matrix OBS Guide”</a> explains well in detail how to
use it. In short, it allows you to define which transition you want to use when
moving from one scene to another. So, you can go from the full webcam to the
presentation and webcam with the move transition, but from the presentation and
webcam to the presentation only with a fade.</p>

<h5 id="set-up-slides">set up slides</h5>

<p>Finally, the last “trick” of today is not to do with OBS, but with google
slides. If you are preparing a set of slides and you want to see whether your
head is going to cover some bits, then <a href="https://edu.gcfglobal.org/en/googleslides/editing-master-slides-and-layouts/1/">you can create a colourful square/circle
on the master slide</a> positioned where your “head” is going
to appear. This will make it easier for you to clearly distinguish that space
across all the slides. When you have finished, you can go back to edit the
master slides and remove the square you set.</p>

<hr />

<p>And that’s all! Congratulations if you’ve got this far! Feel free to ask me
anything about this or let me know you’ve found something wrong. Please, let me
know if this has been helpful to you ;)</p>]]></content><author><name></name></author><category term="teaching" /><category term="carpentries" /><category term="tools" /><category term="SwC" /><category term="workshops" /><summary type="html"><![CDATA[A week ago I was running a workshop (online!) about git and GitLab for colleagues at work. After a whole year of teaching online with Zoom, I thought I was prepared for everything that could go wrong. However… I didn’t expect Zoom to fail me now.]]></summary></entry><entry><title type="html">A sustainable way to translate The Carpentries lessons</title><link href="https://david.ps/blog/2018/04/15/a-sustainable-way-to-translate-the-carpentries-lessons.html" rel="alternate" type="text/html" title="A sustainable way to translate The Carpentries lessons" /><published>2018-04-15T00:00:00+00:00</published><updated>2018-04-15T00:00:00+00:00</updated><id>https://david.ps/blog/2018/04/15/a-sustainable-way-to-translate-the-carpentries-lessons</id><content type="html" xml:base="https://david.ps/blog/2018/04/15/a-sustainable-way-to-translate-the-carpentries-lessons.html"><![CDATA[<p>The Carpentries lessons are a very useful resource for any researcher around the
world, a proof of that is how popular software and data carpentries workshops
have become! I’ve been part of this community since 2012, and soon after that
some of us started to think on translations (probably there were others before
us!). At that time all the lessons were in a single repository
(<a href="https://github.com/swcarpentry/DEPRECATED-bc">you can still find it</a>), and the
way we started to translate the lessons into Spanish
(<a href="https://github.com/franktoffel/swcarpentry-es/pulls?q=is%3Apr+is%3Aopen">check our PRs</a>)
was to keep track of our original files by adding a comment with the hash of the
version of the original file. We didn’t got too far. The main problem being the
time that all of us could commit to this. In parallel there was also Raniere 
translating <a href="https://github.com/rgaiacs/bc/tree/pt-BR/translations/pt-BR">it into Portuguese</a>.</p>

<p>However… much has changed since 2014! Now there’s a repository for each lesson,
the lessons are written in markdown and the community has grown a lot!</p>

<p>Since last summer there’s been a strong push by many on latinoamerica, and 
they’ve got <a href="https://github.com/swcarpentry?utf8=%E2%9C%93&amp;q=-es&amp;type=&amp;language=">a lot done</a>!
They have organised amazingly well, creating a <a href="https://github.com/Carpentries-ES/board/blob/master/Convenciones_Traduccion.md">glossary and style</a>, 
a <a href="https://groups.google.com/a/carpentries.org/forum/#!forum/latinoamerica">mailing list</a> and
get a bunch of dedicated volunteers! (67 members on the mailing list, and roughly counting
more than 20 that have contributed to the translations on github!).</p>

<p>When talking about translations there are two main models one can follow, let’s
call them <strong>upstream</strong> and <strong>wikipedia</strong>. There are probably more models, but I
thing these are the two that needs an attention in this project.
With <strong>upstream</strong> I am referring to a project that originates in a language, and
the translations follow that. Think it as a book - El Quijote was written in
Spanish and many translations appeared from it. There’s no way a translation will
feed the author of the book to improve something. Another example is the menus and
guides on software tools. If you use Firefox in Italian the words are translated
from the English - there’s no a new functionality coming from the translation that
needs to be brought into the upstream version.
On the other hand, <strong>wikipedia</strong> has a translation of pages that are diverging.
Some article may have been created in Tamil and someone translated into Turkish
and then other contributors have added more information on the Turkish than 
what it was on the original Tamil article. However, another wikipedia editor may
add these bits back into the original.</p>

<p>In the first case it’s very simple to track what’s been translated and what’s
not, also, if something changes upstream, it’s easy to track too. On the
wikipedia case it’s impossible (or almost impossible) to track - the articles
become rapidly different enough to be considered a translation of the original.
(And in a way that’s the beauty of wikipedia, right?)</p>

<p>What’s best for the Carpentries? Probably an hybrid between both.
Anyone can contribute to the English lessons via a pull request, as for 
any software project. In the same manner the community can provide comments
and suggestions via GitHub and the maintainers have the last power to decide
what to merge and what not. If we have lesson A and lesson A-es, then similar
process occur, and that’s great to fix translations, typos and similar “minor”
changes. But what happens if the “es” community finds out about a better flow to 
teach a lesson? If such new approach can be generalised to other cultures
then probably it could be brought up for discussion upstream and then after approval
implement it in all the translated lessons (A-es, A-it, A-gr, …). However, if such change
doesn’t make sense to other cultures then we have a diverging point. If 
it’s a simple change on the order on how something is taught (A.2 before A.1)
probably we have the means to keep track of that, though if it’s something
more intrinsic… then I don’t know what could be done.</p>

<h2 id="whats-done-now">What’s done now?</h2>

<p>At the moment the lessons at the carpentries are “forks”
(<a href="http://swcarpentry.github.io/lesson-example/setup.html">imported</a>)
of <a href="https://github.com/swcarpentry/styles">the style lesson</a>.
This
<a href="http://swcarpentry.github.io/lesson-example/02-tooling/index.html">workflow is well explained at the documentation</a>
(look at the diagrams!). In a similar manner, the translated lessons are imported from
the original. The maintainer of the translated lesson will pull from upstream
(the English one) and that will show the changes that need to be translated
(probably as merge conflicts - though I’ve not suffered that myself yet).</p>

<p>On the other side, the translators take the original files and start translating
them on the text editor. Sentence by sentence. When completed they make a pull
request to the A-es repository, reviewed and finally merged.</p>

<h2 id="what-am-i-proposing">What am I proposing?</h2>

<p>Translations are hard! Mostly because you need to keep a flow and consistency
over a text. You need to keep your glossary at hand and keep a track of what
changes upstream without the need to re-read the whole text again to know where
the new change belongs.</p>

<p>Thing is that we are not alone! Translations have been a common practice in many
areas before. From legal documents on countries with more than an official
language and companies that expand internationally to software used by many. We
have to learn from them that have been doing it for decades. Many open source
communities have created glossaries and style guides for their documentation
(e.g., <a href="https://developer.gnome.org/gdp-style-guide/2.32/index.html.en">Gnome’s</a>) 
and there are lots of tools to make the translation process easier and better
to track.</p>

<p>The main tools used to translate (interfaces and documentation) by many software
groups are based on <a href="https://en.wikipedia.org/wiki/Gettext">gettext and PO files</a>.
These tools tokenise code or text and keep track of what happens to them. If in
a new version only one token changes the special text editors
(e.g., <a href="http://www.poedit.net">PoEdit</a>,
<a href="https://wiki.gnome.org/Apps/Gtranslator">GTranslator</a>,
<a href="https://userbase.kde.org/Lokalize">Lokalize</a>)
will show what needs to be updated. There’s other projects like
<a href="http://docs.translatehouse.org/projects/translate-toolkit/en/latest/commands/index.html">Translate toolkit</a>
that offers lots of tools to work with these type of files.</p>

<p>Using these tools we could keep a track of what’s been translated, what’s 
new, find where we need help and visualise all that in a simple dashboard.</p>

<p>During the hack-day at the last <a href="https://www.software.ac.uk/cw18">Collaborations Workshop</a>
I tackle more of less of what’s need be done in terms of infrastructure.
However, to make it work properly we would need some structure (again,
imitating of what I learn from translators from Gnome).</p>

<ol>
  <li>Lessons release calendar. That helps translators to schedule time
to focus on the translations of the lesson.</li>
  <li>Have an editor (or few) for each language - similar role than the
maintainer.</li>
  <li>Main maintainer (English) don’t need to know about the languages
as they may have been approved by the editors.</li>
  <li>Glossary and writing style guides.</li>
</ol>

<p>In the model I built (see figure below) I depend on two things.
The first is that each lesson would use submodules to bring in the translations.
That will make the url of the lessons something like: <code class="language-plaintext highlighter-rouge">lesson/&lt;lang&gt;/index.html</code>
where <code class="language-plaintext highlighter-rouge">&lt;lang&gt;</code> is the language code.
The second is to use Travis-ci to generate a dashboard with the translations
progress for each lesson and each language and to generate the markdown files
based on the po translated files and push them to the respective repository.</p>

<p><img height="400px" alt="i18n diagram" src="/gallery/images/2018/04/2018-04-15-i18n_diagram.png" /></p>

<p>Each box represents a repository and the arrows are the connections as submodules
between them. Travis would be in charge of making a dashboard and create and update
the repositories with new translations.</p>

<h3 id="what-have-i-got-so-far">What have I got so far?</h3>

<p>In <a href="https://github.com/swcarpentry-i18n">this test GitHub organisation</a> I’ve got
the <a href="https://github.com/swcarpentry-i18n/po4gitbook">markdown to po</a> tool that I
have adopted and modified so it works with the carpentries material. A central 
<a href="https://github.com/swcarpentry-i18n/i18n">i18n</a> repository where all the magic
happens - keeps track via submodules of the upstream repositories and generates
a gh-pages with a dashboard of what’s been done (still very crude).</p>

<h3 id="whats-still-needed">What’s still needed?</h3>

<p>To make the whole thing to work I still need 
<a href="https://github.com/swcarpentry-i18n/i18n/issues?q=is%3Aissue+is%3Aopen">certain things to work</a>. 
These includes write tests, get Travis to generate the stats and new repositories 
and even a bot that analyses your PRs before they are merged (like pep8speaks or 
codecov reports)</p>

<h3 id="what-problems-could-this-bring">What problems could this bring?</h3>

<p>Translators will need to become familiar to use the po format. The good news is
that there are plenty of tools that shows that in a nice way. However it is a 
tool to learn - whereas now they can just use a text editor. I think the advantages
that these tools bring worth the learning.</p>

<p>Also this needs a change on how all the lessons are maintained. Will this be
very disruptive to how are things now? I don’t think so, but this is why I’m
trying this in a different set of repositories so I could show how everything
works (if it does!).</p>

<h4 id="more-thoughts">More thoughts</h4>

<p>While learning how everything “hangs” from the styles repository I’m thinking
into convert that as a jekyll theme <a href="https://blog.github.com/2017-11-29-use-any-theme-with-github-pages/">now that github allows to use them from
github repositories</a>. 
That would remove the task on the maintainers to keep up to date with the styles.</p>]]></content><author><name></name></author><category term="SwC" /><category term="translations" /><category term="hackday" /><summary type="html"><![CDATA[The Carpentries lessons are a very useful resource for any researcher around the world, a proof of that is how popular software and data carpentries workshops have become! I’ve been part of this community since 2012, and soon after that some of us started to think on translations (probably there were others before us!). At that time all the lessons were in a single repository (you can still find it), and the way we started to translate the lessons into Spanish (check our PRs) was to keep track of our original files by adding a comment with the hash of the version of the original file. We didn’t got too far. The main problem being the time that all of us could commit to this. In parallel there was also Raniere translating it into Portuguese.]]></summary></entry></feed>