Dusting off the Résumé, Part 2: Converting to HTML

Earlier, I shared with you the Schema I use for writing my Résumé in XML. In this post, I want to talk about how that XML becomes something human-readable: Hyper Text Markup Language (HTML).

I wrote my first Web Page in 1993, one year after Tim Berners-Lee released the now ubiquitous language of the Internet. Back then, to get information on the Internet you were restricted to dial-up Bulletin Board Systems (BBS), Gopher and Archie for file search and retrieval, and Usenet for staying abreast of the latest computer news or just chew the fat. I was, in fact, a very early adopter, immediately seeing the potential of HTML to revolutionize the world of digital communications.

What’s more, unlike JavaScript Object Notation (JSON), which is best for HTML POST requests and related operations, because HTML is a markup language and a full subset of XML, it makes more sense to encode my work history and Curriculum Vitae (CV). This is why I continue to maintain my Résumé and the longer form CV in XML and don’t convert it to JSON.

That said, it would be fascicle to convert XML to JSON and many application already do this.

The translation from XML to HTML, however, takes more finesse. Fortunately, there’s a subset of XML known as eXtensible Stylesheet Language Transformation (XSLT). XSLT is a rudimentary rule-based language with recursion. It allows me to iterate over fields and concatenate them with commas (,), or hide fields which are deprecated as no longer relevant to the modern software job market.

As such, XSLT is a very verbose language which requires many elements to define how each XML component will be handled. Nonetheless, the full transform is included below.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:param name="amazon" select="'http://www.amazon.com/exec/obidos/ASIN/'"/>
<xsl:param name="deprecated_text" select="'Hidden text has been excized from this document!'"/>
<xsl:param name="redacted_text" select="'Redacted text has been blacked out from this document!'"/>
<xsl:param name="col_of_data" select="2"/>

<xsl:variable name="deprecated">
  <span class="deprecated">deprecated</span>
  <xsl:comment>
    <xsl:value-of select="$deprecated_text"/>
  </xsl:comment>
</xsl:variable>

<xsl:variable name="redacted">
  <span class="redacted">redacted</span>
  <xsl:comment>
    <xsl:value-of select="$redacted_text"/>
  </xsl:comment>
</xsl:variable>

<!-- Entities -->
<!-- Question: How do I get Entities to appear as entities?? -->

<!-- Pure (PCDATA) Elements -->
<!-- Names and Addresses -->

<xsl:template match="name">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="oldname">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="street">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="apartment">
  <xsl:choose>
    <!-- Use param / param-with -->
    <xsl:when test="@spellout = 'true'">
      <xsl:text disable-output-escaping="yes">Apartment&amp;nbsp;</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text disable-output-escaping="yes">Apt.&amp;nbsp;</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="city">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="state">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="province">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="postal">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="country">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="e-mail">
  <a href="mailto:{.}">
    <xsl:choose>
      <xsl:when test="parent::address">
        <xsl:attribute name="class">
          <xsl:text>address</xsl:text>
        </xsl:attribute>
      </xsl:when>
      <xsl:when test="parent::reference">
        <xsl:attribute name="class">
          <xsl:text>reference</xsl:text>
        </xsl:attribute>
      </xsl:when>
    </xsl:choose>
    <xsl:apply-templates/>
  </a>
</xsl:template>

<xsl:template match="phone">
  <xsl:apply-templates/> (H)
</xsl:template>

<xsl:template match="mobile">
  <xsl:apply-templates/> (M)
</xsl:template>
<!-- Keys and Headings -->

<xsl:template match="heading">
  <hr class="separator"/>
  <h2 class="heading">
    <xsl:apply-templates/>
  </h2>
</xsl:template>

<xsl:template match="key">
  <span class="text_heading">
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="value">
  <xsl:apply-templates/>
</xsl:template>

<!-- Date and Time -->

<xsl:template match="year">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="month">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="day">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="hour">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="minute">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="second">
  <xsl:apply-templates/>
</xsl:template>

<!-- Descriptive Elements -->

<xsl:template match="position">
  <span class="position">
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="product">
  <span class="product">
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="degree">
  <span class="degree">
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="course">
  <span class="course">
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="author">
  <span class="author">
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="author-et-al">
  <xsl:text> </xsl:text><span class="author"><em>et al</em></span>
</xsl:template>

<xsl:template match="publisher">
  <span class="publisher">
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="subject">
  <p class="subject">
    <xsl:apply-templates/>
  </p>
</xsl:template>

<xsl:template match="isbn">
  <a class="isbn" href="{$amazon}{.}/">
    <xsl:apply-templates/>
  </a>
</xsl:template>

<!-- deprecated -->

<xsl:template match="deprecated">
  <!-- Ingore deprecated text -->
  <xsl:copy-of select="$deprecated"/>
</xsl:template>

<!-- Compound Elements -->
<!-- Compound Time -->

<xsl:template match="date">
  <span class="date">
    <xsl:if test="day">
      <!-- European Date Format -->
      <xsl:apply-templates select="day"/>
      <!-- day requires month, so we know that the month is next -->
      <xsl:text> </xsl:text>
    </xsl:if>
    <xsl:if test="month">
      <xsl:apply-templates select="month"/>
    </xsl:if>
    <xsl:if test="year">
      <xsl:if test="month">
        <xsl:text> </xsl:text>
      </xsl:if>
      <xsl:apply-templates select="year"/>
    </xsl:if>
    <xsl:if test="hour">
      <xsl:if test="not(*[position() = 1 and self::hour])">
        <xsl:text> </xsl:text>
      </xsl:if>
      <xsl:apply-templates select="country"/>
    </xsl:if>
    <xsl:if test="minute">
      <!-- Always follows hour -->
      <xsl:text>:</xsl:text>
      <xsl:apply-templates select="minute"/>
    </xsl:if>
    <xsl:if test="second">
      <!-- Always follows minute -->
      <xsl:text>:</xsl:text>
      <xsl:apply-templates select="second"/>
    </xsl:if>
  </span>
</xsl:template>

<xsl:template match="from">
  <span class="from_date">
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="to">
  <span class="to_date">
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="period">
  <xsl:choose>
    <xsl:when test="date">
      <p class="period">
        <xsl:apply-templates select="date"/>
      </p>
    </xsl:when>
    <xsl:when test="from and to">
      <p class="period">
        <xsl:apply-templates select="from"/>
        <xsl:text disable-output-escaping="yes"> &amp;ndash; </xsl:text>
        <xsl:apply-templates select="to"/>
      </p>
    </xsl:when>
  </xsl:choose>
</xsl:template>

<!-- Simple Compound Elements -->

<xsl:template match="address">
  <span class="address">
    <xsl:if test="street">
      <xsl:apply-templates select="street"/>
      <xsl:if test="apartment">
        <xsl:text>, </xsl:text>
        <xsl:apply-templates select="apartment"/>
      </xsl:if>
    </xsl:if>
    <xsl:if test="city">
      <xsl:if test="not(*[position() = 1 and self::city])">
        <xsl:text>, </xsl:text>
      </xsl:if>
      <xsl:apply-templates select="city"/>
    </xsl:if>
    <xsl:choose>
      <xsl:when test="state">
        <xsl:if test="not(*[position() = 1 and self::state])">
          <xsl:text>, </xsl:text>
        </xsl:if>
        <xsl:apply-templates select="state"/>
      </xsl:when>
      <xsl:when test="province">
        <xsl:if test="not(*[position() = 1 and self::province])">
          <xsl:text>, </xsl:text>
        </xsl:if>
        <xsl:apply-templates select="province"/>
      </xsl:when>
    </xsl:choose>
    <xsl:if test="postal">
      <xsl:if test="not(*[position() = 1 and self::postal])">
        <xsl:text> </xsl:text>
      </xsl:if>
      <xsl:apply-templates select="postal"/>
    </xsl:if>
    <xsl:if test="country">
      <xsl:if test="not(*[position() = 1 and self::country])">
        <xsl:text> </xsl:text>
      </xsl:if>
      <xsl:apply-templates select="country"/>
    </xsl:if>
    <xsl:if test="e-mail">
      <xsl:if test="not(*[position() = 1 and self::e-mail])">
        <br/>
      </xsl:if>
      <xsl:apply-templates select="e-mail"/>
    </xsl:if>
    <!-- TODO: Put the phone and mobile phone on the same line. -->
    <xsl:if test="phone">
      <xsl:if test="not(*[position() = 1 and self::phone])">
        <br/>
      </xsl:if>
      <xsl:apply-templates select="phone"/>
    </xsl:if>
    <xsl:if test="mobile">
      <xsl:if test="not(*[position() = 1 and self::mobile])">
        <br/>
      </xsl:if>
      <xsl:apply-templates select="mobile"/>
    </xsl:if>
  </span>
</xsl:template>

<xsl:template match="book">
  <xsl:choose>
    <xsl:when test="not(boolean(@deprecated))">
      <!-- This is a full Book Definition -->
      <p class="bibliography">
        <xsl:if test="author">
          <xsl:for-each select="author">
            <xsl:if test="not(position() = 1)">
              <xsl:text>, </xsl:text>
            </xsl:if>
            <xsl:if test="(position() = last() and not(boolean(../author-et-al)))">
              <xsl:text> and </xsl:text>
            </xsl:if>
            <xsl:apply-templates select="."/>
          </xsl:for-each>
          <xsl:if test="author-et-al">
            <xsl:apply-templates select="author-et-al"/>
          </xsl:if>
          <xsl:text>. </xsl:text>
        </xsl:if>
        <!-- If ISBN is provided, use to link to Amazon -->
        <xsl:choose>
          <xsl:when test="isbn">
            <a class="book" href="{$amazon}{isbn}/">
              <xsl:apply-templates select="name"/>
            </a>
          </xsl:when>
          <xsl:otherwise>
            <span class="book">
              <xsl:apply-templates select="name"/>
            </span>
          </xsl:otherwise>
        </xsl:choose>
        <xsl:if test="publisher | date">
          <xsl:text>. </xsl:text>
        </xsl:if>
        <xsl:if test="address">
          <xsl:apply-templates select="address"/>
          <!-- Publisher must follow -->
          <xsl:text>: </xsl:text>
        </xsl:if>
        <xsl:if test="publisher">
          <xsl:apply-templates select="publisher"/>
          <xsl:if test="date">
            <xsl:text>, </xsl:text>
          </xsl:if>
        </xsl:if>
        <xsl:if test="date">
          <xsl:apply-templates select="date"/>
        </xsl:if>
      </p>
      <xsl:if test="subject">
        <xsl:apply-templates select="subject"/>
      </xsl:if>
    </xsl:when>
    <xsl:otherwise>
      <!-- Ingore deprecated text -->
      <xsl:copy-of select="$deprecated"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="institution">
  <xsl:choose>
    <xsl:when test="not(boolean(@deprecated))">
      <!-- This is a full Institutional Definition -->
      <!-- Normally a Job or School will access the elements
           independently, so this is only bibliographical. -->
      <p class="institution">
        <xsl:choose>
          <xsl:when test="@url">
            <span class="institution">
              <a href="{@url}">
                <xsl:apply-templates select="name"/>
              </a>
            </span>
          </xsl:when>
          <xsl:otherwise>
            <span class="institution">
              <xsl:apply-templates select="name"/>
            </span>
          </xsl:otherwise>
        </xsl:choose>
        <xsl:for-each select="oldname">
          <xsl:choose>
            <xsl:when test="position() = 1">
              <xsl:text> (Formerly </xsl:text>
            </xsl:when>
            <xsl:when test="not(position() = last())">
              <xsl:text>; </xsl:text>
            </xsl:when>
          </xsl:choose>
          <xsl:apply-templates/>
          <xsl:if test="position() = last()">
            <xsl:text>)</xsl:text>
          </xsl:if>
        </xsl:for-each>
        <xsl:if test="address">
          <xsl:text>, </xsl:text>
          <xsl:apply-templates select="address"/>
        </xsl:if>
      </p>
    </xsl:when>
    <xsl:otherwise>
      <!-- Ingore deprecated text -->
      <xsl:copy-of select="$deprecated"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="bookref">
  <xsl:variable name="ref" select="@ref"/>
  <xsl:variable name="book" select="//book[@name = $ref]"/>
  <xsl:choose>
    <xsl:when test="not(boolean($book/@deprecated))">
      <xsl:choose>
        <xsl:when test="$book/@url">
          <a class="bookref" href="{$book/@url}">
            <xsl:apply-templates select="$book/name"/>
          </a>
        </xsl:when>
        <xsl:when test="$book/isbn">
          <a class="bookref" href="{$amazon}{$book/isbn}/">
            <xsl:apply-templates select="$book/name"/>
          </a>
        </xsl:when>
        <xsl:otherwise>
          <span class="bookref">
            <xsl:apply-templates select="$book/name"/>
          </span>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <!-- Ingore deprecated text -->
      <xsl:copy-of select="$deprecated"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="instref">
  <xsl:variable name="ref" select="@ref"/>
  <xsl:variable name="inst" select="//institution[@name = $ref]"/>
  <xsl:choose>
    <xsl:when test="not(boolean($inst/@deprecated))">
      <!-- This is an Inline Institution, therefore treat as character formatting -->
      <xsl:choose>
        <xsl:when test="$inst/@url">
          <span class="instref">
            <a href="{$inst/@url}">
              <xsl:apply-templates select="$inst/name"/>
            </a>
          </span>
        </xsl:when>
        <xsl:otherwise>
          <span class="instref">
            <xsl:apply-templates select="$inst/name"/>
          </span>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:if test="$inst/address">
        <xsl:text>, </xsl:text>
        <xsl:apply-templates select="$inst/address"/>
      </xsl:if>
    </xsl:when>
    <xsl:otherwise>
      <!-- Ingore deprecated text -->
      <xsl:copy-of select="$deprecated"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- Simple Compound Markups -->

<xsl:template match="buzzword">
  <xsl:choose>
    <xsl:when test="not(boolean(@deprecated))">
      <xsl:choose>
        <xsl:when test="@url">
          <span class="buzzword">
            <a href="{@url}">
              <xsl:apply-templates/>
            </a>
          </span>
        </xsl:when>
        <xsl:otherwise>
          <span class="buzzword">
            <xsl:apply-templates/>
          </span>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <!-- Ingore deprecated text -->
      <xsl:copy-of select="$deprecated"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="language">
  <xsl:choose>
    <xsl:when test="not(boolean(@deprecated))">
      <span class="language">
        <xsl:apply-templates/>
      </span>
      <!-- Level is Required -->
      <xsl:text> (</xsl:text>
      <xsl:value-of select="@level"/>
      <xsl:text>)</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <!-- Ingore deprecated text -->
      <xsl:copy-of select="$deprecated"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="status">
  <p class="status">
    <span class="text_heading">
      <xsl:value-of select="key"/>
      <xsl:text>: </xsl:text>
    </span>
    <xsl:apply-templates select="value"/>
  </p>
</xsl:template>

<xsl:template match="skill">
  <xsl:choose>
    <xsl:when test="not(boolean(@deprecated))">
      <p class="skill">
        <span class="text_heading">
          <xsl:value-of select="key"/>
          <xsl:text>: </xsl:text>
        </span>
        <!-- Use for-each to get buzzwords or languages because they
             cannot appear in the same skill set, so if one for-each
             has not element, it is not executed and saves me the trouble
             of writing an xsl:if or writing xsl:for-each
             select="*[self::buzzword | self::langauge]" -->
        <xsl:for-each select="buzzword[not(boolean(@deprecated))]">
          <xsl:if test="not(position() = 1)">
            <xsl:text>, </xsl:text>
          </xsl:if>
          <xsl:apply-templates select="."/>
        </xsl:for-each>
        <xsl:for-each select="language[not(boolean(@deprecated))]">
          <xsl:if test="not(position() = 1)">
            <xsl:text>, </xsl:text>
          </xsl:if>
          <xsl:apply-templates select="."/>
        </xsl:for-each>
      </p>
    </xsl:when>
    <xsl:otherwise>
      <!-- Ingore deprecated text -->
      <xsl:copy-of select="$deprecated"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="curriculum">
  <xsl:choose>
    <xsl:when test="not(boolean(@deprecated))">
      <p class="curriculum">
        <xsl:if test="child::key">
          <span class="text_heading">
            <xsl:value-of select="key"/>
            <xsl:text>: </xsl:text>
          </span>
        </xsl:if>
        <xsl:for-each select="course">
          <xsl:if test="not(position() = 1)">
            <xsl:text>, </xsl:text>
          </xsl:if>
          <xsl:apply-templates select="."/>
        </xsl:for-each>
      </p>
    </xsl:when>
    <xsl:otherwise>
      <!-- Ingore deprecated text -->
      <xsl:copy-of select="$deprecated"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="tools">
  <span class="tools">
    <xsl:for-each select="buzzword[not(boolean(@deprecated))]">
      <xsl:if test="not(position() = 1)">
        <xsl:text>, </xsl:text>
      </xsl:if>
      <xsl:apply-templates select="."/>
    </xsl:for-each>
  </span>
</xsl:template>

<xsl:template match="task">
  <li class="task"/>
  <p class="task">
    <xsl:apply-templates/>
  </p>
</xsl:template>

<xsl:template match="interest">
  <xsl:choose>
    <xsl:when test="@url">
      <a class="interest" href="{@url}">
        <xsl:apply-templates/>
      </a>
    </xsl:when>
    <xsl:otherwise>
      <span class="interest">
        <xsl:apply-templates/>
      </span>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="para">
  <!-- This will typically be overridden by another template -->
  <p>
    <xsl:apply-templates/>
  </p>
</xsl:template>

<xsl:template match="objective">
  <xsl:for-each select="para">
    <p class="objective">
      <xsl:apply-templates/>
    </p>
  </xsl:for-each>
</xsl:template>

<xsl:template match="summary">
  <xsl:for-each select="para">
    <p class="summary">
      <xsl:apply-templates select="."/>
    </p>
  </xsl:for-each>
  <table border="0" width="100%">
    <xsl:for-each select="task">
      <xsl:choose>
        <xsl:when test="position() mod $col_of_data = 1">
          <xsl:text disable-output-escaping="yes">&lt;tr&gt;</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <td>
            <img src="spacer.gif" class="spacer"/>
          </td>
        </xsl:otherwise>
      </xsl:choose>
      <!-- Spacer messes the percentage up! -->
      <td class="task" width="{100 div $col_of_data}%">
        <xsl:apply-templates select="."/>
      </td>
      <xsl:if test="position() mod $col_of_data = 0">
        <xsl:text disable-output-escaping="yes">&lt;/tr&gt;</xsl:text>
      </xsl:if>
    </xsl:for-each>
    <!-- Extra Close Row for odd numbers -->
    <xsl:if test="count(task) mod $col_of_data != 0">
      <xsl:text disable-output-escaping="yes">&lt;/tr&gt;</xsl:text>
    </xsl:if>
  </table>
</xsl:template>

<xsl:template match="achievement">
  <!-- Do the First Paragraph Separately with Tools, then the rest -->
  <!-- Problem with indentation and blank line after -->
  <li class="achievement"/>
  <p class="achievement">
    <xsl:if test="tools">
      <!-- Double Spanning on Tools! -->
      <xsl:apply-templates select="tools"/>
      <xsl:if test="para">
        <span class="tools">
          <xsl:text>: </xsl:text>
        </span>
      </xsl:if>
    </xsl:if>
    <!-- Wacky way of saying "Don't Apply this template,
         apply the ones below! -->
    <xsl:for-each select="para[1]">
      <xsl:apply-templates/>
    </xsl:for-each>
  </p>
  <xsl:for-each select="para[position() &gt; 1]">
    <!-- Convert to Unordered List -->
    <p class="achievement">
      <xsl:apply-templates/>
    </p>
  </xsl:for-each>
</xsl:template>

<xsl:template match="title">
  <span class="title">
    <h1 class="name">
      <xsl:apply-templates select="name"/>
      <xsl:for-each select="oldname">
        <xsl:if test="position() = 1">
          <xsl:text> (</xsl:text>
        </xsl:if>
        <xsl:apply-templates/>
        <xsl:choose>
          <xsl:when test="position() = last()">
            <xsl:text>)</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>, </xsl:text>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </h1>
    <p class="address">
      <xsl:apply-templates select="address"/>
    </p>
  </span>
</xsl:template>

<xsl:template match="bibliography">
  <xsl:if test="not(boolean(@deprecated))">
    <xsl:if test="heading">
      <xsl:apply-templates select="heading"/>
    </xsl:if>
    <xsl:for-each select="book">
      <xsl:apply-templates select="."/>
    </xsl:for-each>
    <xsl:for-each select="institution">
      <xsl:apply-templates select="."/>
    </xsl:for-each>
  </xsl:if>
</xsl:template>

<xsl:template match="job">
  <xsl:if test="not(boolean(@deprecated))">
    <table width="100%">
      <tr>
        <td>
          <xsl:apply-templates select="institution"/>
        </td>
        <td>
          <img src="spacer.gif" class="spacer"/>
        </td>
        <td>
          <!-- To Do: Please try to if the position is
               present, extend this cell and the spacer
               to rowspan="2" -->
          <!-- Problem: Width of "Period" too small! -->
          <xsl:apply-templates select="period"/>
        </td>
      </tr>
      <xsl:if test="position">
        <tr>
          <td colspan="2">
            <xsl:apply-templates select="position"/>
          </td>
        </tr>
      </xsl:if>
      <xsl:for-each select="achievement">
        <tr>
          <td colspan="2">
            <xsl:apply-templates select="."/>
          </td>
        </tr>
      </xsl:for-each>
    </table>
  </xsl:if>
</xsl:template>

<xsl:template match="school">
  <xsl:if test="not(boolean(@deprecated))">
    <table width="100%">
      <tr>
        <td>
          <xsl:apply-templates select="institution"/>
        </td>
        <td>
          <img src="spacer.gif" class="spacer"/>
        </td>
        <td>
          <!-- To Do: Please try to, if the degree is
               present, extend this cell and the spacer
               to rowspan="2" -->
          <!-- Problem: Width of "Period" too small! -->
          <xsl:apply-templates select="period"/>
        </td>
      </tr>
      <xsl:if test="degree">
        <tr>
          <td colspan="2">
            <xsl:apply-templates select="degree"/>
          </td>
        </tr>
      </xsl:if>
      <xsl:for-each select="achievement">
        <tr>
          <td colspan="2">
            <xsl:apply-templates select="."/>
          </td>
        </tr>
      </xsl:for-each>
      <xsl:for-each select="curriculum">
        <tr>
          <td colspan="2">
            <xsl:apply-templates select="."/>
          </td>
        </tr>
      </xsl:for-each>
    </table>
  </xsl:if>
</xsl:template>

<xsl:template match="reference">
  <xsl:if test="not(boolean(@deprecated))">
    <p class="reference">
      <xsl:apply-templates select="name"/>
      <xsl:if test="position">
        <br/>
        <xsl:apply-templates select="position"/>
      </xsl:if>
      <xsl:if test="instref">
        <!-- Since we know the Institution-Reference requires a
             position, we will add the comma here before the
             institution definition -->
        <xsl:text>, </xsl:text>
        <xsl:apply-templates select="instref"/>
      </xsl:if>
      <xsl:if test="e-mail">
        <br/>
        <xsl:apply-templates select="e-mail"/>
      </xsl:if>
      <xsl:if test="phone">
        <br/>
        <xsl:apply-templates select="phone"/>
      </xsl:if>
      <xsl:if test="mobile">
        <br/>
        <xsl:apply-templates select="mobile"/>
      </xsl:if>
    </p>
  </xsl:if>
</xsl:template>

<xsl:template match="section">
  <xsl:if test="not(boolean(@deprecated))">
    <xsl:apply-templates select="heading"/>
    <xsl:choose>
      <xsl:when test="child::objective">
        <xsl:apply-templates select="objective"/>
      </xsl:when>
      <xsl:when test="child::status">
        <table border="0" width="100%">
          <!-- For Simplicity I will but the Spanning cells first -->
          <!-- Ideally, the XSL should use an iterative approach that
               would end a row tag when it finds a spanning, then
               span the cell across all columns, then reset the
               numbering to "1" or some odd number to continue the
               data as if the next cell was the first one.  The problem
               with this is simply that I can't reset the position()
               counter to do this.  So, voilà, the simple approach -->
          <xsl:for-each select="status[@spanning = 'true']">
            <tr>
              <td class="status" colspan="{$col_of_data}">
                <xsl:apply-templates select="."/>
              </td>
            </tr>
          </xsl:for-each>
          <xsl:for-each select="status[not(boolean(@spanning))]">
            <xsl:choose>
              <xsl:when test="position() mod $col_of_data = 1">
                <xsl:text disable-output-escaping="yes">&lt;tr&gt;</xsl:text>
              </xsl:when>
              <xsl:otherwise>
                <td>
                  <img src="spacer.gif" class="spacer"/>
                </td>
              </xsl:otherwise>
            </xsl:choose>
            <!-- Spacer messes the percentage up! -->
            <td class="status" width="{100 div $col_of_data}%">
              <xsl:apply-templates select="."/>
            </td>
            <xsl:if test="position() mod $col_of_data = 0">
              <xsl:text disable-output-escaping="yes">&lt;/tr&gt;</xsl:text>
            </xsl:if>
          </xsl:for-each>
          <!-- Extra Close Row for odd numbers -->
          <xsl:if test="count(resume/section/summary/task) mod $col_of_data != 0">
            <xsl:text disable-output-escaping="yes">&lt;/tr&gt;</xsl:text>
          </xsl:if>
        </table>
      </xsl:when>
      <xsl:when test="child::summary">
        <xsl:apply-templates select="summary"/>
      </xsl:when>
      <xsl:when test="child::skill">
        <xsl:for-each select="skill">
          <xsl:apply-templates select="."/>
        </xsl:for-each>
      </xsl:when>
      <xsl:when test="child::job">
        <xsl:for-each select="job">
          <xsl:apply-templates select="."/>
          <!-- Blank Line After to separate Records? -->
        </xsl:for-each>
      </xsl:when>
      <xsl:when test="child::school">
        <xsl:for-each select="school">
          <!-- Blank Line After to separate Records? -->
          <xsl:apply-templates select="."/>
        </xsl:for-each>
      </xsl:when>
      <xsl:when test="child::interest">
        <xsl:for-each select="interest">
          <xsl:if test="not(position() = 1)">
            <xsl:text>, </xsl:text>
          </xsl:if>
          <xsl:apply-templates select="."/>
        </xsl:for-each>
      </xsl:when>
      <xsl:when test="child::reference">
        <xsl:for-each select="reference">
          <xsl:apply-templates select="."/>
        </xsl:for-each>
      </xsl:when>
    </xsl:choose>
  </xsl:if>
</xsl:template>

<xsl:template match="/">
  <html>
  <head>
  <link href="Résumé.css" rel="stylesheet" type="text/css"/>
  <xsl:apply-templates select="resume/title"/>
  </head>
  <body>
    <xsl:for-each select="resume/section[not(boolean(@deprecated))]">
      <xsl:apply-templates select="."/>
    </xsl:for-each>
    <xsl:apply-templates select="resume/bibliography[not(boolean(@deprecated))]"/>
    <hr class="separator"/>
    <xsl:if test="resume/@url">
      <table width="100%">
        <tr>
          <td width="33%"></td>
          <td width="67%">
            <p class="online">
              <xsl:text>Text available on-line at </xsl:text>
              <a type="online" href="{resume/@url}">
                <xsl:value-of select="resume/@url"/>
              </a>
              <xsl:text>.</xsl:text>
            </p>
          </td>
        </tr>
      </table>
    </xsl:if>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

Résumé.xsl, the eXstensible Stylesheet Language Transform of an XML Résumé.

Once the XML has been translated to HTML, it’s still rather rough. However, one can use Cascaded Style Sheets (CSS) to transform that raw XML into something much more pleasant to look at. It also hides Deprecated elements to they don’t appear in the final, rendered page. It sets the font, and colors the hard breaks, and formats everything in neat boxes.

Because the XSLT adds class modifiers to a number of the tags, the CSS can be rather richly defined and very specific to each document element.

body
{
    FONT-SIZE: 11pt;
    FONT-FAMILY: 'Times New Roman'
}
td
{
vertical-align : top;
}
span..buzzword
{
    FONT-STYLE: italic;
    FONT-FAMILY: 'Courier New'
}
span.institution
{
}
p.summary
{
    TEXT-ALIGN: justify
}
hr.separator
{
    BORDER-RIGHT: blue thick groove;
    BORDER-TOP: blue thick groove;
    BORDER-LEFT: blue thick groove;
    BORDER-BOTTOM: blue thick groove
}
H2.heading
{
    BORDER-TOP: medium none;
    FONT-SIZE: 12pt;
    COLOR: blue;
    FONT-FAMILY: Helvetica;
    LETTER-SPACING: 6pt;
    TEXT-ALIGN: center
}
H1.name
{
    FONT-WEIGHT: bolder;
    FONT-SIZE: 20pt;
    TEXT-ALIGN: center
}
SPAN.title
{
    COLOR: #ff9900;
    FONT-STYLE: italic;
    FONT-FAMILY: Helvetica;
    TEXT-ALIGN: center
}
P.address
{
    TEXT-ALIGN: center
}
SPAN.address
{
}
SPAN.text_heading
{
    FONT-WEIGHT: bolder
}
SPAN.position
{
    FONT-STYLE: italic;
    FONT-FAMILY: Arial
}
SPAN.product
{
    FONT-STYLE: italic
}
SPAN.degree
{
    FONT-STYLE: italic;
    FONT-FAMILY: Arial
}
SPAN.course
{
}
SPAN.author
{
}
A.isbn
{
    VISIBILITY: inherit
}
SPAN.date
{
}
SPAN.from_date
{
}
SPAN.to_date
{
}
P.period
{
    COLOR: #800080;
    FONT-STYLE: italic;
    TEXT-ALIGN: right
}
SPAN.publisher
{
}
P.bibliography
{
}
A.book
{
    VISIBILITY: inherit;
    FONT-STYLE: italic
}
SPAN.book
{
    FONT-STYLE: italic
}
SPAN.language
{
}
P.subject
{
}
SPAN.instref
{
    FONT-STYLE: italic
}
P.institution
{
    FONT-WEIGHT: bolder;
    FONT-SIZE: 12pt;
    FONT-FAMILY: Arial
}
A.address
{
    VISIBILITY: inherit
}
P.status
{
}
P.skill
{
}
P.curriculum
{
}
SPAN.tools
{
    FONT-WEIGHT: bolder;
    COLOR: #4c8000
}
P.task
{
}
SPAN.interest
{
}
A.interest
{
    VISIBILITY: inherit
}
P.objective
{
}
HEAD
{
    FONT-SIZE: 11pt;
    FONT-FAMILY: 'Times New Roman'
}
P.reference
{
}
P.online
{
    TEXT-ALIGN: right
}
A.online
{
    VISIBILITY: inherit
}
SPAN.redacted
{
    color: black;
    background-color: black;
    TEXT-DECORATION: line-through;
    font-style: italic
}
SPAN.deprecated
{
    DISPLAY: none;
    TEXT-DECORATION: line-through
}
TD.task
{
    VERTICAL-ALIGN: top;
    LIST-STYLE-TYPE: disc
}
TD.status
{
    VERTICAL-ALIGN: top;
}
img.spacer
{
width : 10px;
}
li.task
{
list-style-type : disc;
}
li.achievement
{
list-style-type : disc;
}

Résumé.css, the Cascaded Style Sheet used to transform the Résumé.

Once the CSS is applied, the final Résumé can be generated. Although I still had to perform some tweaking to the HTML, mainly to fix deprecated sections to eliminate trailing commas and other cleanups, for the most part, once the CSS was applied, the Résumé looked nearly perfect. The only change to the above CSS, because CSS on this site is site-wide, was that I put everything within a resume2002 class so that the CSS didn’t apply to every post, only to those inside a Résumé block.

You can see what the final, 2002 version, with a couple modern edits for some of my subsequent publications, of the Résumé looks like here.

I am working on an update to the XML Résumé thanks to figuring out how to get around the Xmplify bug with including Entity definition in an XML Schema. Apparently, if you Entities in your XML, you need to use a DTD to define your XML, you can’t use XML Schema. Fortunately, I did have the full DTD version of my XML specification, so that was easy to solve in the short term while Xmplify is fixed to allow XML Schema with entity-only DOCTYPE sections at the top of XML documents.

I have one final note about the code included in the Résumé in XML posts. In part 1, I neglected to take into account multiple spaces would get reduced to one, meaning that the formatting lost all of the proper indentation and made the code hard to read. I was hasty when I generated the HTML-compatable markup for the two documents there. When I generated the above documents, however, I generated the correct HTML using the simply four lines of Python below, once for each file.

>>> with open('Résumé.xml', 'rb') as f:
...     xml = f.read().decode('latin_1')
... 
>>> with open('xml.html', 'w') as f:
...     f.write(html.escape(xml).replace('\n', '
').replace(' ', ' '))
... 
>>>

Python code to turn XML into into readable HTML

I would love to show you the 2020 version of my Résumé but, alas, I’m still working on it and still have 2002–2012 to cover. I did write entries for 2010–2012, but then realized that I combined the description of two distinct projects into one, so I have to rewrite that section and then finish the first eight years of entries. That killed my Friday when this post was supposed to go out.

I figure that will take me another week, and have about five more projects to cover, but won’t know for sure until I go through the remaining Year End Reviews. But I do have the complete last eight years and, again, if you like what you see, I’m still happily available for hire.

The Green Card

A Timer Most Colourful

Today, I will be the official Timer for tonight’s Loudoun Toastmasters. Last time, I was on hold to do an Evaluation but the speech maker was ill so instead I was instead without a role. On the upside, it gave me time to consider using my Zoom background to enhance the effect of the Timer role. I was therefore anxious to try it out as soon as possible.

Originally, my dear friend Capt. Laura Savino was planning to be Timer, but, since SARS-CoV-2 she’s been busy hanging out with her wonderful boys as she’s hunkered down, sheltered in place. Hope to see her again after Covidapolis is over. But, in the mean time, for tonight, I’ll be stepping into her role.

The role of the timer is to time how long speeches are and to indicate when time is running out to the speaker. Each speech has a minimum time. When that time is hit, I indicate success with a green background.

The Green Card
The Yellow Card in Toastmasters means you’re met the minimum time requirement

Next, when a speaker is half-way through her or his allotted time, I flash the yellow background.

The Yellow Card
The Yellow Card in Toastmasters means you’re half-way through your allotted, acceptable time

Finally, when the speaker is out of time, I flash the red background. At this point, the speaker has thirty seconds to wrap up or be disqualified because his or her speech ran too long.

The Red Card
The Red Card in Toastmasters means you’re out of time

I time all speeches, which range from 5–7 minutes for a standard speech, 4–6 minutes for an Ice Breaker speech, 1–2 minutes for a Table Topic speech, and 2–3 minutes for Evaluations.

It all happens tonight. Stand up straight and deliver my friends!

Electric Cars from near and far

At today’s EVA/DC meeting, we used Zoom to connect with our fellow Electric Car enthusiasts both news and old. I’ve been part of the EVA/DC for ten years and there have been many friends I’ve made through my time there. It was great seeing so many longtime friends once again thanks to the EVA/DC Zoom chat.

Some on Facebook complained that using Zoom for the EVA/DC meetings was insecure. But, as I’m literally a professional white hat hacker, I knew all too well the early and unfounded FUD against Zoom and what it is and is not appropriate for, and how it’s improved. Though I’ve written about it at length, the short answer is: secure enough for EVA/DC, not secure enough for COMSEC TS/NOFORN. Nobody is talking about issues of national security, so please, come join us on Zoom!

My good friend and fellow Eclipse enthusiast, Scott Wilson, shared with us an invitation to the Drive Electric Earth Day event with Plug-In America. The Drive Electric Earth Day Tribute: EVs Making a Difference will occur on 22 April, at 14:00 EDT / 11:00 PDT. You can RSVP here. I should put a disclaimer here that I have asked to be nominated for Plug-In America’s Drive Electric Awards this year, but to be honest, I don’t think anyone nominated me so no worries about a conflict of interest.

Then my longtime friend Eric Cardwell in Tennessee showed us his burgeoning Drive Electric Tennessee page and his new logo. Of course, we wish him well and hope when he’s got it set up to maybe attend one of his meeting on Zoom. Your logo’s looking sharp, my friend!

But the pièce de résistance has to be seeing my longtime friend and first Smart Electric Drive (Smart ED) owner in the US, LTC Mindy Kimball. She shared with us this classic clip from Dan Rather Reports.

Brava Mindy! Was wonderful seeing you again and getting a glimpse of this blast from the past. And you know, though it’s not exactly the same Smart ED owner, her current Smart ED is now driven by the young man in the videos. Look how far we’ve come!

Finally, I would be remiss if I didn’t invite all of you to join me this Saturday on Secure Zoom where I will be presenting #CO2Fre. Please, come cruise the cloud with me.

Dusting off the Résumé, Part 1: The Schema

I wrote my current Résumé in eXtensible Markup Language (XML), back in 2002, just before I started work at the US Naval Research Laboratory. Of course, XML, being a rather free-form markup style, it works best when constrained. At first, I did this via a Document Type Definition (DTD).

<!-- Entities -->
<!ENTITY  nbsp         "&#x00A0;">
<!ENTITY  chi2         "&#x03C7;&#x00B2;">
<!ENTITY  endash       "&#x2013;">
<!ENTITY  eacute       "&#x00E9;">
<!-- Pure (PCDATA) Elements -->
<!-- Names and Addresses -->
<!ELEMENT name         (#PCDATA)>
<!ELEMENT oldname      (#PCDATA)>
<!ELEMENT street       (#PCDATA)>
<!ELEMENT apartment    (#PCDATA)>
  <!ATTLIST apartment    spellout     (true|false) "false">
<!ELEMENT city         (#PCDATA)>
<!ELEMENT state        (#PCDATA)>
<!ELEMENT province     (#PCDATA)>
<!ELEMENT postal       (#PCDATA)>
<!ELEMENT country      (#PCDATA)>
<!ELEMENT e-mail       (#PCDATA)>
<!ELEMENT phone        (#PCDATA)>
<!ELEMENT mobile       (#PCDATA)>
<!-- Keys and Headings -->
<!ELEMENT heading      (#PCDATA)>
<!ELEMENT key          (#PCDATA)>
<!ELEMENT value        (#PCDATA)>
<!-- Date and Time -->
<!ELEMENT year         (#PCDATA)>
<!ELEMENT month        (#PCDATA)>
<!ELEMENT day          (#PCDATA)>
<!ELEMENT hour         (#PCDATA)>
<!ELEMENT minute       (#PCDATA)>
<!ELEMENT second       (#PCDATA)>
<!-- Descriptive Elements -->
<!ELEMENT position     (#PCDATA)>
<!ELEMENT product      (#PCDATA)>
<!ELEMENT degree       (#PCDATA)>
<!ELEMENT course       (#PCDATA)>
<!ELEMENT author       (#PCDATA)>
<!ELEMENT publisher    (#PCDATA)>
<!ELEMENT subject      (#PCDATA)>
<!ELEMENT isbn         (#PCDATA)>
<!-- deprecated -->
<!ELEMENT deprecated   (#PCDATA)>
<!-- Compound Elements -->
<!-- Compound Time -->
<!ELEMENT date         (year?,(month,day?)?,(hour,(minute,second?)?)?)>
<!ELEMENT from         (date)>
<!ELEMENT to           (date)>
<!ELEMENT period       ((from,to)|date)>
<!-- Simple Compound Elements -->
<!ELEMENT address      ((street,apartment?)?,city?,(state|province)?,postal?,country?,e-mail?,phone?,mobile?)>
<!ELEMENT book         (name,author*,(address?,publisher)?,date?,subject?,isbn?)>
  <!ATTLIST book         name         ID           #IMPLIED>
<!ELEMENT institution  (name,oldname*,address?)>
  <!ATTLIST institution  name         ID           #IMPLIED>
  <!ATTLIST institution  url          CDATA        #IMPLIED>
  <!ATTLIST institution  deprecated   (true|false) "false">
<!-- Reference Elements -->
<!ELEMENT bookref      EMPTY>
<!ATTLIST bookref      ref          IDREF        #REQUIRED>
<!ELEMENT instref      EMPTY>
<!ATTLIST instref      ref          IDREF        #REQUIRED>
<!-- Simple Compound Markups -->
<!ELEMENT buzzword     (#PCDATA|deprecated)*>
  <!ATTLIST buzzword     deprecated   (true|false) "false">
  <!ATTLIST buzzword     url          CDATA        #IMPLIED>
<!ELEMENT language     (#PCDATA)>
  <!ATTLIST language     level        (native|fluent|conversational|good|fair|poor) #REQUIRED>
  <!ATTLIST language     deprecated   (true|false) "false">
<!-- Hashing Elements -->
<!-- Should I make the Key and Attribute?? -->
<!ELEMENT status       (key,value)>
  <!ATTLIST status       spanning     (true|false) "false">
<!ELEMENT skill        (key,(buzzword*|language*))>
  <!ATTLIST skill        deprecated   (true|false) "false">
<!ELEMENT curriculum   (key?,course*)>
  <!ATTLIST curriculum   deprecated   (true|false) "false">
<!-- Compound Text Elements -->
<!ELEMENT tools        (buzzword*)>
<!ELEMENT task         (#PCDATA|buzzword|instref)*>
<!ELEMENT interest     (#PCDATA|buzzword|bookref|instref|deprecated)*>
  <!ATTLIST interest     url          CDATA        #IMPLIED>
<!ELEMENT para         (#PCDATA|buzzword|product|instref|deprecated)*>
<!-- Compound Paragraph Elements -->
<!ELEMENT objective    (para*)>
<!ELEMENT summary      (para*,task*)>
<!ELEMENT achievement  (tools?,para*)>
<!-- Complex Compound Elements -->
<!ELEMENT title        (name,oldname*,address)>
<!ELEMENT bibliography (heading?,book*,institution*)>
  <!ATTLIST bibliography deprecated   (true|false) "false">
<!ELEMENT job          (institution,period,position?,achievement*)>
  <!ATTLIST job          deprecated   (true|false) "false">
<!ELEMENT school       (institution,period?,degree,achievement*,curriculum*)>
  <!ATTLIST school       deprecated   (true|false) "false">
<!ELEMENT reference    (name,(position,instref?)?,e-mail?,phone?,mobile?)>
  <!ATTLIST reference    deprecated   (true|false) "false">
<!ELEMENT section      (heading,(objective|status*|summary|skill*|job*|school*|interest*|reference*))>
  <!ATTLIST section      deprecated   (true|false) "false">
<!-- Top Level Tag -->
<!ELEMENT resume       (title,bibliography?,section*)>
  <!ATTLIST resume       url          CDATA        #IMPLIED>

Résumé.dtd, the Résumé Document Type Definition

These days, most DTDs have been thrown by the wayside in favor of XML defining itself through an XML Schema. A few years ago, I upgraded my DTD to a Schema to make it more compatible with common XML editors—though I’ve yet to find a descent (Xmplify was a total failure in that respect with no ability to context-complete based on an associated scheme).

<?xml version="1.0" encoding="UTF-8"?>
<!-- Character Entities -->
<!DOCTYPE xs:schema [
  <!ENTITY  nbsp         "&#x00A0;">
  <!ENTITY  chi2         "&#x03C7;&#x00B2;">
  <!ENTITY  endash       "&#x2013;">
  <!ENTITY  eacute       "&#x00E9;">
]>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.timehorse.com"
           xmlns="http://www.timehorse.com"
           elementFormDefault="qualified">

  <!-- Common Attributes -->
  <xs:attribute name="url" type="xs:anyURI"/>
  <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
  <xs:attribute name="spanning" type="xs:boolean" default="false"/>
  <xs:attribute name="name" type="xs:string"/>
  <xs:attribute name="ref" type="xs:string"/>
  <xs:attribute name="level">
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:enumeration value="native"/>
        <xs:enumeration value="fluent"/>
        <xs:enumeration value="conversational"/>
        <xs:enumeration value="good"/>
        <xs:enumeration value="fair"/>
        <xs:enumeration value="poor"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:attribute>

  <!-- Attribute Groupings -->
  <xs:attributeGroup name="depricatedUrlGroup">
    <xs:attribute ref="url"/>
    <xs:attribute ref="deprecated"/>
  </xs:attributeGroup>

  <!-- Simple Types -->
  <!-- Names and Addresses -->
  <xs:element name="name" type="xs:string"/> <!-- nameType -->
  <xs:element name="oldname" type="xs:string"/> <!-- nameType -->
  <xs:element name="street" type="xs:string"/>
  <xs:element name="city" type="xs:string"/> <!-- nameType -->
  <xs:element name="state" type="xs:string"/> <!-- regionType -->
  <xs:element name="province" type="xs:string"/> <!-- regionType -->
  <xs:element name="postal" type="xs:string"/> <!-- postalType -->
  <xs:element name="country" type="xs:string"/>
  <xs:element name="e-mail" type="xs:string"/> <!-- emailType -->
  <xs:element name="phone" type="phoneType"/>
  <xs:element name="mobile" type="phoneType"/>

  <!-- Keys and Headings -->
  <xs:element name="heading" type="xs:string"/>
  <xs:element name="key" type="xs:string"/>
  <xs:element name="value" type="xs:string"/>

  <!-- Date and Time -->
  <xs:element name="year" type="xs:gYear"/>
  <xs:element name="month" type="monthType"/>
  <xs:element name="day" type="dayType"/>
  <xs:element name="hour" type="hourType"/>
  <xs:element name="minute" type="minuteType"/>
  <xs:element name="second" type="secondType"/>

  <!-- Descriptive Elements -->
  <xs:element name="position" type="xs:string"/>
  <xs:element name="product" type="xs:string"/>
  <xs:element name="degree" type="xs:string"/>
  <xs:element name="course" type="xs:string"/>
  <xs:element name="author" type="xs:string"/> <!-- nameType -->
  <xs:element name="publisher" type="xs:string"/>
  <xs:element name="subject" type="xs:string"/>
  <xs:element name="isbn" type="isbnType"/>

  <!-- Deprecated -->
  <xs:element name="deprecated" type="xs:string"/>

  <!-- Complex, Text-Only -->
  <xs:element name="apartment">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:string">
          <xs:attribute name="spellout" type="xs:boolean" default="false"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>

  <!-- Empty Elements (markers) -->
  <xs:element name="author-et-al">
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="xs:anyType"/>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>

  <!-- Compound Elements -->
  <!-- Compound Time -->
  <xs:element name="date">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="year" minOccurs="0" maxOccurs="1"/>
        <xs:sequence minOccurs="0">
          <xs:element ref="month" minOccurs="1" maxOccurs="1"/>
          <xs:element ref="day" minOccurs="0" maxOccurs="1"/>
        </xs:sequence>
        <xs:sequence minOccurs="0">
          <xs:element ref="hour" minOccurs="1" maxOccurs="1"/>
          <xs:sequence minOccurs="0">
            <xs:element ref="minute" minOccurs="1" maxOccurs="1"/>
            <xs:element ref="second" minOccurs="0" maxOccurs="1"/>
          </xs:sequence>
        </xs:sequence>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="from">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="date" minOccurs="1" maxOccurs="1"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="to">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="date" minOccurs="1" maxOccurs="1"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="period">
    <xs:complexType>
      <xs:choice>
        <xs:sequence>
          <xs:element ref="from" minOccurs="1" maxOccurs="1"/>
          <xs:element ref="to" minOccurs="1" maxOccurs="1"/>
        </xs:sequence>
        <xs:element ref="date"/>
      </xs:choice>
    </xs:complexType>
  </xs:element>

  <!-- Simple Compound Elements -->
  <xs:element name="address">
    <xs:complexType>
      <xs:sequence>
        <xs:sequence minOccurs="0">
          <xs:element ref="street" minOccurs="1" maxOccurs="1"/>
          <xs:element ref="apartment" minOccurs="0" maxOccurs="1"/>
        </xs:sequence>
        <xs:element ref="city" minOccurs="0" maxOccurs="1"/>
        <xs:choice minOccurs="0">
          <xs:element ref="state"/>
          <xs:element ref="province"/>
        </xs:choice>
        <xs:element ref="postal" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="country" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="e-mail" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="phone" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="mobile" minOccurs="0" maxOccurs="1"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name" minOccurs="1" maxOccurs="1"/>
        <xs:sequence minOccurs="0">
          <xs:element ref="author" minOccurs="1" maxOccurs="unbounded"/>
          <xs:element ref="author-et-al" minOccurs="0" maxOccurs="1"/>
        </xs:sequence>
        <xs:sequence minOccurs="0">
          <xs:element ref="address" minOccurs="0" maxOccurs="1"/>
          <xs:element ref="publisher" minOccurs="1" maxOccurs="1"/>
        </xs:sequence>
        <xs:element ref="date" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="subject" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="isbn" minOccurs="0" maxOccurs="1"/>
      </xs:sequence>
      <xs:attribute name="name" type="xs:string"/>
      <xs:attribute name="url" type="xs:anyURI"/>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="institution">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name" minOccurs="1" maxOccurs="1"/>
        <xs:element ref="oldname" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element ref="address" minOccurs="0" maxOccurs="1"/>
      </xs:sequence>
      <xs:attribute name="name" type="xs:string"/>
      <xs:attribute name="url" type="xs:anyURI"/>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>

  <!-- Reference Elements -->
  <xs:element name="bookref">
    <xs:complexType>
      <xs:attribute name="ref" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="instref">
    <xs:complexType>
      <xs:attribute name="ref" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>

  <!-- Simple Compound Markups -->
  <xs:element name="buzzword">
    <xs:complexType mixed="true">
      <xs:sequence>
        <xs:element ref="deprecated" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="url" type="xs:anyURI"/>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="language">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:string">
          <xs:attribute name="level">
            <xs:simpleType>
              <xs:restriction base="xs:string">
                <xs:enumeration value="native"/>
                <xs:enumeration value="fluent"/>
                <xs:enumeration value="conversational"/>
                <xs:enumeration value="good"/>
                <xs:enumeration value="fair"/>
                <xs:enumeration value="poor"/>
              </xs:restriction>
            </xs:simpleType>
          </xs:attribute>
          <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>

  <!-- Hashing Elements -->
  <!-- Note: Should I make the Key and Attributes?? -->
  <xs:element name="status">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="key" minOccurs="1" maxOccurs="1"/>
        <xs:element ref="value" minOccurs="1" maxOccurs="1"/>
      </xs:sequence>
      <xs:attribute name="spanning" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="skill">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="key" minOccurs="1" maxOccurs="1"/>
        <xs:choice>
          <xs:element ref="buzzword" minOccurs="0"
                      maxOccurs="unbounded"/>
          <xs:element ref="language" minOccurs="0"
                      maxOccurs="unbounded"/>
        </xs:choice>
      </xs:sequence>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="curriculum">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="key" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="course" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>

  <!-- Compound Text Elements -->
  <xs:element name="tools">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="buzzword" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="task">
    <xs:complexType mixed="true">
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element ref="buzzword"/>
        <xs:element ref="instref"/>
      </xs:choice>
    </xs:complexType>
  </xs:element>
  <xs:element name="interest">
    <xs:complexType mixed="true">
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element ref="buzzword"/>
        <xs:element ref="bookref"/>
        <xs:element ref="instref"/>
        <xs:element ref="deprecated"/>
      </xs:choice>
      <xs:attribute name="url" type="xs:anyURI"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="para">
    <xs:complexType mixed="true">
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element ref="buzzword"/>
        <xs:element ref="product"/>
        <xs:element ref="instref"/>
        <xs:element ref="deprecated"/>
      </xs:choice>
    </xs:complexType>
  </xs:element>

  <!-- Compound Paragraph Elements -->
  <xs:element name="objective">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="para" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="summary">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="para" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element ref="task" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="achievement">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="tools" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="para" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <!-- Complex Compound Elements -->
  <xs:element name="title">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name" minOccurs="1" maxOccurs="1"/>
        <xs:element ref="oldname" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element ref="address" minOccurs="1" maxOccurs="1"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="bibliography">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="heading" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="book" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element ref="institution" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="job">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="institution" minOccurs="1" maxOccurs="1"/>
        <xs:element ref="period" minOccurs="1" maxOccurs="1"/>
        <xs:element ref="position" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="achievement" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="school">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="institution" minOccurs="1" maxOccurs="1"/>
        <xs:element ref="period" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="degree" minOccurs="1" maxOccurs="1"/>
        <xs:element ref="achievement" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element ref="curriculum" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="reference">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name" minOccurs="1" maxOccurs="1"/>
        <xs:sequence minOccurs="0" maxOccurs="1">
          <xs:element ref="position" minOccurs="1" maxOccurs="1"/>
          <xs:element ref="instref" minOccurs="0" maxOccurs="1"/>
        </xs:sequence>
        <xs:element ref="e-mail" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="phone" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="mobile" minOccurs="0" maxOccurs="1"/>
      </xs:sequence>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="section">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="heading" minOccurs="1" maxOccurs="1"/>
        <xs:choice>
          <xs:element ref="objective" minOccurs="0" maxOccurs="unbounded"/>
          <xs:element ref="status" maxOccurs="unbounded"/>
          <xs:element ref="summary" minOccurs="0" maxOccurs="unbounded"/>
          <xs:element ref="skill" maxOccurs="unbounded"/>
          <xs:element ref="job" maxOccurs="unbounded"/>
          <xs:element ref="school" maxOccurs="unbounded"/>
          <xs:element ref="interest" maxOccurs="unbounded"/>
          <xs:element ref="reference" maxOccurs="unbounded"/>
        </xs:choice>
      </xs:sequence>
      <xs:attribute name="deprecated" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>

  <!-- Top Level Tag -->
  <xs:element name="resume">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="title" minOccurs="1" maxOccurs="1"/>
        <xs:element ref="bibliography" minOccurs="0" maxOccurs="1"/>
        <xs:element ref="section" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="url" type="xs:anyURI"/>
    </xs:complexType>
  </xs:element>

  <!-- Simple Patern-based Types -->
  <xs:simpleType name="phoneType">
    <xs:restriction base="xs:string">
      <xs:pattern
          value="(\+[1-9][0-9]{0,2} )?(\([1-9][0-9]*\) )?[#*1-9][-#*0-9.pw]*(x[#*0-9.pw]+)?"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="postalType">
    <xs:restriction base="xs:string">
      <xs:pattern value="([0-9]{5}(-[0-9]{4})?|[0-9A-Z]{3} [0-9A-Z]{3}|[0-9]+)"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="regionType">
    <xs:restriction base="xs:string">
      <xs:pattern value="[A-Z]([A-Z]|[a-zé]*( [A-Z][a-zé]*)?)"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="emailType">
    <xs:restriction base="xs:string">
      <xs:pattern
          value="[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+(\.[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+([A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|asia|jobs|museum)"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="nameType">
    <xs:restriction base="xs:string">
      <xs:pattern value="[A-Z]([a-z']*|\.)( [A-Z]([a-z']*|\.))*"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="monthType">
    <xs:restriction base="xs:string">
      <xs:pattern value="([A-Z][a-z]*|0?[1-9]|1[0-2])*"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="dayType">
    <xs:restriction base="xs:string">
      <xs:pattern value="(0?[1-9]|[12][0-9]|30|31)"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="hourType">
    <xs:restriction base="xs:string">
      <xs:pattern value="([01]?[0-9]|2[0-3])"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="minuteType">
    <xs:restriction base="xs:string">
      <xs:pattern value="(0?[1-9]|[1-5][0-9]|60)"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="secondType">
    <xs:restriction base="xs:string">
      <xs:pattern value="(0?[1-9]|[1-5][0-9]|6[01])"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="isbnType">
    <xs:restriction base="xs:string">
      <xs:pattern value="[- 0-9]{9,}"/>
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

Résumé.xsd, the Résumé XML Schema

One thing of note is the concept of deprecated. Not everything in my Résumé is relevant to today. I like keeping the older elements in the document but when a skill or position becomes no longer relevant to the current job market, it’s marked for deprecation and won’t appear in the final form. How that’s done will be explained in Part 2: Converting to HTML.

With the DTD and XML Schema, I was able to at least verify my Résumé was compliant and ready for publication. And, as always, I am most assuredly available for hire even with my older Résumé.

When Zoom Online fails, phone it in

Today, at Reston Writers Review, we had a major Zoom snafu. One of our writers was having a dickens of a time trying to communicate through the Zoom interface when we were reviewing her piece. We had a similar problem on Sunday with The Hourlings but were able to solve that with the person being reviewed just shutting off her video and only using the microphone.

Today, even that didn’t work. One member had to leave the meeting, the connection was so bad and even when the woman being reviewed turned off her video, her voice was still astoundingly choppy.

The only thing for it was to use the backdoor option provided by Zoom: the telephone interface. I hastily logged into the Zoom account provided to me, copied the full meeting info from the Zoom side—including the dial in numbers for connecting to Zoom on the telephone—and, finally, our author was back in the meeting.

Overall, it took about 10 minutes for us to fix all the difficulties listed above, but fortunately we only had five more folks who wanted to give their review, and we were still done by 21:00, our normal meeting end time.

All in all, it was a great and successful meeting despite the glitch. It’s more than likely Internet bandwidth is getting frayed due to an upswing in online meeting. But we adopted and adapted, and improved, just like the motto of the round table suggests.

Thank you for reading!

And so it begins! The 2020–2021 Science Book Club Poll

As promised, I took the seventy-nine—yes, seventy-nine, as my good friend Nick Harding pointed out, one of the fifty that I nominated on Friday was already scheduled for our June discussion so I eliminated it from the poll and the tallies. Anyway, I set up the poll Friday but didn’t want to put it out until today to make sure I had the right instructions ready.

This year, the poll is a little different again from last year, time for the annual Science Book Club poll. Last year, you got one twelfth of a point, about an 8.3% boost in your score for each meeting you attended in the last year.

This year, to simplify things, I decided to simply give a 50% bonus to anyone who attended at least four (non-fiction science) meetings in the last year, and 100% bonus if you attended all twelve. I can verify this because I require everyone to list their name on Meetup so I can correlate the records. Further, if you’ve not logged into the meetup site in the last year, you will get a 50% diminishment of voting power. Also, if you overuse the max or minimum “veto” scores (currently set to a maximum of ten), then you also suffer a 50% reduction. Finally, if you’re not even a member of the Science Book Club, I will allow you to vote but you will be biased to 10% of normal.

Thus, if you, like one member currently, attended at least four meetings but voted for more than ten books the the maximum or minimum (veto) rank, you would end up with a 75% bias, meaning your votes count for 75% as normal.

The reason I added the penalty for too many “veto” votes is because this year we have a seven point system. The seven point system goes from one to seven with the following relations. If someone doesn’t vote for a given book, it’s score is assumed neutral.

RatingMeaningPoints
1Veto-4
2Super-Dislike-2
3Dislike-1
4Neutral0
5Like+1
6Super-Like+2
7Veto-Override+4
Ranking of Votes in the 2020 Science Book Club Poll

Beyond that, pollsing is pretty much the same as last year, albeit with more choices and more options. As of this writing we have five votes but I hope to have many more by the time the poll closes on or just before 14 June.

The polls can be found at the Science Book Club 2020–2021 Poll. I hope you will join me in voting enthusiastically, my fellow sapiosexuals!

An Electric Ford Model T?

My good friend Charles Gerena is organizing a special Zoom event on Meetup where we get to meet the owner of a Ford Model T. Now, if you know anything about the old Ford cars, their engines were only built for about 25,000 miles. After that, you’d have to rebuild the engine, replacing worn parts from a very limited supply, and build it back up again for the next 25,000 miles. As I do almost 25,000 a year in #CO2Fre, that’s not much driving for me at all.

So, why, you may ask, am I promoting a Model T Meetup? Simply put, this is no ordinary Model T—this Model T has been electrified! Today, we are going to learn how the owner converted his classic Model T into an electric car, complete with batteries and electric motor. I hope you can join us!

1914 Model T Hack
Electric Cars aren’t always OEM, sometimes they’re converted. Here in Virginia, someone has converted a Ford Model T into an electric car!

Although I’ll not be cruising on my cloud to get there, hope to see you today at 14:00 EDT!

50 Science Books I’d like to read

Last year, when we were setting the schedule for the Bowie Bevy of Brainy Books, I went through my Audible back catalog and by my calculations, there are 209 titles in my library that I’ve yet to listen to. Some of these are scheduled in my upcoming meetup events but most are gathering dust as I am busy with the official book club list of titles.

Now that it’s time to chose the 2020–2021 Science Book Club. Although I run that meetup and have run it for longer than the founder Megan Thaler, which still amazes me, I always allow a democratic decision on the series of books we read, always scheduling the top 10–12 to form the cycle for the following 11–13 months, with December reserved for our retro cycle books.

I should explain, the Science Book Club has been running since 2009 and has a tremendous back catalog, and although I didn’t attend every meeting, I have attended every one since I began running it in the Summer of 2013. As such, I have a general rule that we can’t do any book we’ve done before in the group as part of the main eleven month year. Also, I require that books be published within the last ten years. I am a little lenient on this in terms of allowing books technically eleven years old given that I’m planning for books into 2021 but allow books from 2010, but no earlier. But official, the rule is no repeats, no fiction, and no books older than ten years. If a book fails any of those tests, it goes into the December book bin, were I allow anything goes!

After winnowing out all the older books, the Great Courses and Fiction books in my back catalog, I was left with fifty books the Science Book Club has never discussed and are at most ten years old. The are as follows:

  • [Medicine] The Case Against Sugar (Gary Taubes, 2016)📖🕮💻💿🏢/384
  • [Sociology] God, No!: Signs You May Already Be an Atheist and Other Magical Tales (Penn Jillette, 2011)📖🕮💻💿🏢/256
  • [Neurology] The Tale of the Dueling Neurosurgeons: The History of the Human Brain as Revealed by True Stories of Trauma, Madness, and Recovery (Sam Kean, 2014)📖🕮💻💿🏢/416
  • [Neurology] The Tell-Tale Brain: A Neuroscientist’s Quest for What Makes Us Human (V. S. Ramachandran, 2011)📖🕮💻💿🏢/384
  • [Mathematics] Naked Statistics: Stripping the Dread from the Data (Charles Wheelan, 2013)📖🕮💻💿🏢/302
  • [Chemistry] The Girls of Atomic City: The Untold Story of the Women Who Helped Win World War II (Denise Kiernan, 2013)📖🕮💻💿🏢/400
  • [Medicine] Get Well Soon: History’s Worst Plagues and the Heroes Who Fought Them (Jennifer Wright, 2017)🕮💻💿🏢/336
  • [Neurology] Superintelligence: Paths, Dangers, Strategies (Nick Bostrom, 2014)📖🕮💻💿🏢/352
  • [Ecology] Eaarth: Making a Life on a Tough New Planet (Bill McKibben, 2010)📖🕮💻💿🏢/272
  • [Sociology] Attached: The New Science of Adult Attachment and How It Can Help You Find – and Keep – Love (Amir Levine, Rachel Heller, 2010)📖🕮💻💿🏢/304
  • [Chemistry] The Radium Girls: The Dark Story of America’s Shining Women (Kate Moore, 2017)📖🕮💻💿🏢/496
  • [Biology] American Pharoah: The Untold Story of the Triple Crown Winner’s Legendary Rise (Joe Drape, 2016)📖🕮💻💿🏢/304
  • [Technology] Elon Musk: Tesla, SpaceX, and the Quest for a Fantastic Future (Ashlee Vance, 2015)📖🕮💻💿🏢/400
  • [Astronomy] The 4-Percent Universe: Dark Matter, Dark Energy, and the Race to Discover the Rest of Reality (Richard Panek, 2011)📖🕮💻💿🏢/297
  • [Physics] The Hunt for Vulcan: …And How Albert Einstein Destroyed a Planet, Discovered Relativity, and Deciphered the Universe (Thomas Levenson, 2015)📖🕮💻💿🏢/256
  • [Biology] The Hidden Life of Trees: What They Feel, How They Communicate – Discoveries from a Secret World (Peter Wohlleben, 2016)📖🕮💻💿🏢/288
  • [Genetics] A Crack in Creation: Gene Editing and the Unthinkable Power to Control Evolution (Jennifer A. Doudna, Samuel H. Sternberg, 2017)📖🕮💻💿🏢/304
  • [Physics] The Future of Humanity: Terraforming Mars, Interstellar Travel, Immortality, and Our Destiny Beyond Earth (Michio Kaku, 2018)📖🕮💻💿🏢/368
  • [Technology] Soonish: Ten Emerging Technologies That’ll Improve and/or Ruin Everything (Kelly Weinersmith, Zach Weinersmith, 2017)📖🕮💻💿🏢/368
  • [Neurology] Thinking in Bets: Making Smarter Decisions When You Don’t Have All the Facts (Annie Duke, 2018)📖🕮💻💿🏢/288
  • [Mathematics] The Woman Who Smashed Codes: A True Story of Love, Spies, and the Unlikely Heroine who Outwitted America’s Enemies (Jason Fagone, 2017)📖🕮💻💿🏢/464
  • [Medicine] Breasts: A Natural and Unnatural History (Florence Williams, 2012)📖🕮💻💿🏢/352
  • [Technology] Army of None: Autonomous Weapons and the Future of War (Paul Scharre, 2018)📖🕮💻💿🏢/448
  • [Chemistry] Atomic Adventures: Secret Islands, Forgotten N-Rays, and Isotopic Murder – A Journey into the Wild World of Nuclear Science (James Mahaffey, 2017)📖🕮💻💿FALSE/464
  • [Medicine] Pandora’s Lab: Seven Stories of Science Gone Wrong (Paul A. Offit, MD, 2017)🕮💻💿🏢/288
  • [Medicine] Gut: The Inside Story of Our Body’s Most Underrated Organ (Giulia Enders, 2015)📖💻💿🏢/271
  • [Biology] American Wolf: A True Story of Survival and Obsession in the West (Nate Blakeslee, 2017)📖🕮💻💿🏢/320
  • [Biology] Are We Smart Enough to Know How Smart Animals Are? (Frans de Waal, 2016)📖🕮💻💿🏢/352
  • [Biology] The Triumph of Seeds: How Grains, Nuts, Kernels, Pulses & Pips Conquered the Plant Kingdom and Shaped Human History (Thor Hanson, 2015)📖🕮💻💿🏢/304
  • [Mathematics] Superforecasting: The Art and Science of Prediction (Philip E. Tetlock, Dan Gardner, 2015)📖🕮💻💿🏢/352
  • [Medicine] First Bite: How We Learn to Eat (Bee Wilson, 2015)📖🕮💻💿🏢/352
  • [Medicine] Are u ok?: A Guide to Caring for Your Mental Health (Kati Morton LMFT, 2018)🕮💻💿FALSE/256
  • [Medicine] Ten Drugs: How Plants, Powders, and Pills Have Shaped the History of Medicine (Thomas Hager, 2019)📖🕮💻💿🏢/320
  • [Physics] The Second Kind of Impossible: The Extraordinary Quest for a New Form of Matter (Paul Steinhardt, 2019)📖🕮💻💿🏢/400
  • [Medicine] The Mystery of the Exploding Teeth: And Other Curiosities from the History of Medicine (Thomas Morris, 2018)📖🕮💻💿🏢/368
  • [Sociology] Untrue: Why Nearly Everything We Believe About Women, Lust, and Infidelity Is Wrong and How the New Science Can Set Us Free (Wednesday Martin PhD, 2018)📖🕮💻💿🏢/320
  • [Mathematics] Lost in Math: How Beauty Leads Physics Astray (Sabine Hossenfelder, 2018)📖🕮💻💿🏢/304
  • [Neurology] The Upside of Irrationality: The Unexpected Benefits of Defying Logic at Work and at Home (Dan Ariely, 2010)📖🕮💻💿🏢/334
  • [Medicine] Spillover: Animal Infections and the Next Human Pandemic (David Quammen, 2012)📖🕮💻💿🏢/592
  • [Economics] Money: The Unauthorized Biography (Felix Martin, 2014)📖🕮💻💿🏢/336
  • [Physics] The Science of Interstellar (Kip Thorne, 2014)📖💻💿🏢/336
  • [Ecology] The Omnivore’s Dilemma: Young Readers Edition (Michael Pollan, 2015)📖🕮💻💿🏢/400
  • [Mathematics] Code Warriors: NSA’s Codebreakers and the Secret Intelligence War Against the Soviet Union (Stephen Budiansky, 2016)📖🕮💻💿🏢/416
  • [Evolution] Homo Deus: A Brief History of Tomorrow (Yuval Noah Harari, 2017)📖🕮💻💿🏢/464
  • [Medicine] Chernobyl: The History of a Nuclear Catastrophe (Serhii Plokhy, 2018)📖🕮💻💿FALSE/432
  • [Physics] The Order of Time (Carlo Rovelli, 2018)📖🕮💻💿🏢/256
  • [Sociology] The Etymologicon: A Circular Stroll Through the Hidden Connections of the English Language (Mark Forsyth, 2012)📖💻💿FALSE/304
  • [Technology] The Design of Everyday Things: Revised and Expanded Edition (Donald A. Norman, 2013)📖💻💿🏢/368
  • [Medicine] Human Errors: A Panorama of Our Glitches, from Pointless Bones to Broken Genes (Nathan H. Lents, 2018)📖🕮💻💿🏢/256
  • [Ecology] My Planet: Finding Humor in the Oddest Places (Mary Roach, 2013)📖💻💿🏢/160

I should explain here the shorthand I use to indicate the formats supported by each book. Unicode has icons for each of the formats as follows:

  • 📖: Paperback
  • 🕮: Hard Cover (Note, this Unicode Glyph doesn’t appear on all platforms)
  • 💻: eBook, such as Kindle
  • 💿: Audiobook, as in Audible
  • 🏢: The book is in the Library (this glyph, when present, contains a link to its entry in the Fairfax County Public Library card catalog)

The long and short of that is, to enter fifty new books into the nomination queue is a very tedious affair and took me so many hours yesterday, I forgot to post my note about TeslaOS 2020.20.5 on Thursday.

For the record, my fifty entries were appended to the end of the existing seventeen moniations already made or carried forward from the last poll. We are, therefore, in addition to the above, also considering the following books:

  • [Physics] Through Two Doors at Once: The Elegant Experiment That Captures the Enigma of Our Quantum Reality (Anil Ananthaswamy, 2018)🕮💻💿🏢/304
  • [Genetics] Regenesis: How Synthetic Biology Will Reinvent Nature and Ourselves (George M. Church, Ed Regis, 2012)📖🕮💻🏢/304
  • [Genetics] Brave Genius: A Scientist, a Philosopher, and Their Daring Adventures from the French Resistance to the Nobel Prize (Sean B. Carroll, 2013)📖🕮💻🏢/592
  • [Biology] The Invention of Nature: Alexander von Humboldt’s New World (Andrea Wulf, 2015)📖🕮💻💿🏢/496
  • [Evolution] From Bacteria to Bach and Back: The Evolution of Minds (Daniel C. Dennett, 2017)🕮💻💿🏢/496
  • [Technology] Bad Blood: Secrets and Lies in a Silicon Valley Startup (John Carreyrou, 2018)📖🕮💻💿🏢/352
  • [Biology] The Plant Messiah: Adventures in Search of the World’s Rarest Species (Carlos Magdalena, 2018)📖🕮💻💿🏢/272
  • [Sociology] Algorithms to Live By: The Computer Science of Human Decisions (Brian Christian, Tom Griffiths, 2016)📖🕮💻💿🏢/368
  • [Ecology] The Uninhabitable Earth, Life after Warming (David Wallace-Wells, 2019)📖🕮💻💿🏢/320
  • [Astronomy] The Skeptics’ Guide to the Universe: How to Know What’s Really Real in a World Increasingly Full of Fake (Steven Novella, 2018)📖🕮💻💿🏢/512
  • [Health] How to Change Your Mind: What the New Science of Psychedelics Teaches Us About Consciousness, Dying, Addiction, Depression, and Transcendence (Michael Pollan, 2018)📖🕮💻💿🏢/480
  • [Geology] Origins: How the Earth Shaped Human History (Lewis Dartnell, 2019)📖🕮💻💿🏢/320
  • [Geology] The Ends of the World: Volcanic Apocalypses, Lethal Oceans, and Our Quest to Understand Earth’s Past Mass Extinctions (Peter Brannen, 2017)📖🕮💻💿🏢/322
  • [Ecology] The Forest Unseen: A Year’s Watch in Nature (David George Haskell, 2012)📖🕮💻💿🏢/268
  • [Ecology] The Songs of Trees: Stories from Nature’s Great Connectors (David George Haskell, 2017)📖🕮💻💿🏢/304
  • [Biology] Never Home Alone: From Microbes to Millipedes, Camel Crickets, and Honeybees, the Natural History of Where We Live (Rob Dunn, 2018)📖🕮💻💿🏢/323
  • [Genetics] The Gene: An Intimate History (Siddhartha Mukherjee, 2016)📖🕮💻💿🏢/608

Thus, over the weekend, assuming no more last-minute nominations, I will be create a poll with sixty-seven entries, asking my members to rank them on a five-point system and then use those star rankings and member attendance history to calculate the top 10–12 books and then generate our schedule through the summer of 2021—with the exception of December.

As for the December, 2020 meeting, nineteen books from my back catalog didn’t satisfy my ten year or repeat criterion, and so I added them to the three books carried over from last December’s poll. The first three books are the ones carried over, the rest are from my back catalog.

  • Measuring Eternity: The Search for the Beginning of Time (Martin Gorst, 2001)📖🕮💻🏢/352
  • How to Create a Mind: The Secret of Human Thought Revealed (Ray Kurzweil, 2012)📖🕮💻💿🏢/352
  • (Fiction) The Witness Paradox: A Time Traveler Anthology (Martin Wilsey, TR Dillon, Jeffrey C. Jacobs, 2018)📖🕮💻FALSE/246
  • iWoz: How I Invented the Personal Computer and Had Fun Along the Way (Steve Wozniak, 2006)📖🕮💻💿🏢/313
  • How the Mind Works (Steven Pinker, 1998)📖🕮💻💿🏢/660
  • Origins: Fourteen Billion Years of Cosmic Evolution (Neil deGrasse Tyson, 2004)📖🕮💻💿🏢/336
  • Fear Of Physics: A Guide For The Perplexed (Lawrence M. Krauss, 1993)📖🕮💻💿FALSE/224
  • The Elephant Whisperer: My Life with the Herd in the African Wild (Lawrence Anthony, Graham Spence, 2009)📖🕮💻💿🏢/384
  • Mating in Captivity: Reconciling the Erotic & the Domestic (Esther Perel, 2006)📖🕮💻💿🏢/272
  • The Invention of Air: A Story Of Science, Faith, Revolution, And The Birth Of America (Steven Johnson, 2008)📖🕮💻💿🏢/272
  • Freakonomics [Revised and Expanded]: A Rogue Economist Explores the Hidden Side of Everything (Steven D. Levitt, Stephen J. Dubner, 2006)📖🕮💻💿🏢/336
  • Parallel Worlds: A Journey Through Creation, Higher Dimensions, and the Future of the Cosmos (Michio Kaku, 2004)📖🕮💻💿FALSE/428
  • The Earth Moved: On the Remarkable Achievements of Earthworms (Amy Stewart, 2004)📖🕮💻💿🏢/256
  • The Pleasure of Finding Things Out: The Best Short Works of Richard P. Feynman (Richard P. Feynman, 1999)📖🕮💻💿🏢/270
  • Stiff: The Curious Lives of Human Cadavers (Mary Roach, 2003)📖🕮💻💿🏢/303
  • Spook: Science Tackles the Afterlife (Mary Roach, 2005)📖🕮💻💿🏢/311
  • Our Magnificent Bastard Tongue: The Untold History of English (John McWhorter, 2008)📖🕮💻💿🏢/230
  • Apollo: The Race to the Moon (Charles Murray, Catherine Bly Cox, 1989)📖🕮💻💿FALSE/506
  • Silent Spring (Rachel Carson, 2002)📖🕮💻💿🏢/400
  • Song for the Blue Ocean (Carl Safina, 1998)📖🕮💻💿FALSE/458
  • Billions & Billions: Thoughts on Life and Death at the Brink of the Millennium (Carl Sagan, 1997)📖🕮💻💿🏢/244
  • The Demon-Haunted World: Science as a Candle in the Dark (Carl Sagan, 2008)📖🕮💻💿🏢/457

So much reading, so little time! Can’t wait to hear what y’all want to read, my sapiosexual friends!

UPDATE 2020-04-10 21:30: I do encourage my Science Readers to retrieve all the information above, such the full title, all authors and their full names, what formats the books are in, a link to the library listing, the publication year and the page count, and post all this to the Meetup Message Board. I do this because I get an email notification every time someone posts there. It’s hard to get to, to be sure, but when I send the email reminding folks to nominate things, I do provide a direct link to the Message Board discussion.

It’s therefore sad that most of my members used the new Meetup Discussion list instead. I get no notifications of any kind when people post here so I was shocked to see, when I posted a link to this article, that in fact a lot of my members posted sketchy book information to that list. A few of the nominations were in the list, but fourteen were new, as far as I could tell.

Of course, not wanting to ignore my member’s wishes, I spent a few more hours today trying to add all their nominations to the list. There are now eighty nominations, thirteen more added.

  • [Medicine] The Body: A Guide for Occupants (Bill Bryson, 2019)📖🕮💻💿🏢/464
  • [Technology] The Book of Why: The New Science of Cause and Effect (Judea Pearl, Dana Mackenzie, 2018)📖🕮💻💿🏢/432
  • [Medicine] The Epigenetics Revolution: How Modern Biology Is Rewriting Our Understanding of Genetics, Disease, and Inheritance (Nessa Carey, 2012)📖🕮💻💿🏢/352
  • [Evolution] Lamarck’s Revenge: How Epigenetics Is Revolutionizing Our Understanding of Evolution’s Past and Present (Peter Ward, 2018)🕮💻🏢/288
  • [Biology] Aliens: The World’s Leading Scientists on the Search for Extraterrestrial Life (Jim Al-Khalili, 2017)🕮💻💿FALSE/240
  • [Physics] The World According to Physics (Jim Al-Khalili, 2020)🕮💻💿FALSE/336
  • [Physics] Paradox: The Nine Greatest Enigmas in Physics (Jim Al-Khalili, 2012)📖🕮💻💿🏢/239
  • [Technology] What the Future Looks Like: Scientists Predict the Next Great Discoveries―and Reveal How Today’s Breakthroughs Are Already Shaping Our World (Jim Al-Khalili, 2018)📖💻💿FALSE/240
  • [Technology] The House of Wisdom: How Arabic Science Saved Ancient Knowledge and Gave Us the Renaissance (Jim Al-Khalili, 2011)📖🕮💻💿🏢/336
  • [Medicine] Life on the Edge: The Coming of Age of Quantum Biology (Jim Al-Khalili, 2015)📖🕮💻💿🏢/368
  • [Technology] An Optimist’s Tour of the Future: One Curious Man Sets Out to Answer What’s Next? (Mark Stevenson, 2011)📖🕮💻🏢/384
  • [Technology] We Do Things Differently: The Outsiders Rebooting Our World (Mark Stevenson, 2018)📖🕮💻FALSE/304
  • [Physics] Our Mathematical Universe: My Quest for the Ultimate Nature of Reality (Max Tegmark, 2014)📖🕮💻💿🏢/432

In addition to these thirteen, one more nomination was added to the December list because it’s a book we discussed in the group before.

  • The Hidden Reality: Parallel Universes and the Deep Laws of the Cosmos (Brian Greene, 2011)📖🕮💻💿🏢/384

Exhausted but still sapiosexual.

TeslaOS 2020.12.5, or so I’m told

Thursday, I was so busy with updating my Science Book Club book nominations list, which I will discuss Friday, that I forgot to transmit this piece on time. I have, however, fixed the release date to correspond to when it was relevant.

That out of the way, I am very excited about TeslaOS 2020.12.5. Well, I would be if I could access #CO2Fre. Unfortunately, I am disallowed from entering #CO2Fre because of fears that, after the scam service from last week because someone things everything in the world has SARS-CoV-2.

One thing I hear is that you can now view your Dash Cam on the big screen. So, what? I’ve been struggling to find the right and reliable USB storage device that can allow me to sync my recordings to my capacious Google Drive but am completed paralyzed when it comes to finding a replacement for my original, failed device that stopped working many months ago. I just wish there was an out-of-the-box solution that didn’t require external DC Power.

And I still haven’t figured out what Game Controller to get. If only there was an official list of supported devices.

As for actually stopping at Stop Signs and Stop Lights, that’s yet to be seen. I’ve not seen it mentioned on any sites about TeslaOS 2020.12.5, so I’m dubious it has that.

But, I guess I’ll find out next week.

All this is bad enough it it weren’t for the fact that I was originally scheduled to give a demonstration of my Tesla #P㆔D and all the new capabilities of the vehicle this weekend with the RVA Electric Vehicle association meetup on Saturday. I’m really hoping to switch this to next week as if we hold it on Saturday, I will be presenting #CO2Fre without #CO2Fre!

But at least there’s TeslaOS 2020.12.5, in theory.

TeslaOS 2020.12.5
Apparently, #CO2Fre is now running TeslaOS 2020.12.5. Unfortunately, I have no way of verifying this in the automobile or see what’s in the update as I’m prohibited from entering my vehicle until next Saturday because of unfounded fear of SARS-CoV-2 infection.

Still waiting to return to cruising on a cloud…

A Brief History of Everyone Who Ever Lived: The Human Story Retold Through Our Genes

When I was given permission to Telework, I was worried without the ninety or so minutes of time commuting each working day I’d never be able to read all twenty or more books I normally read in a year, or for that matter the next book following The Thing with Feathers: The Surprising Lives of Birds and What They Reveal About Being Human. But, rest assured, cleary I did and now I’m here to talk about it.

Adam Rutherford, no relation to Ernest, weaves an interesting survey of what Deoxyribonucleic Acid has contributed to our modern understanding of biology. He starts off talking about how humanity is like a braided stream, with genetic lines splitting and then re-emerging between Homo Neanderthalis, Homo Denisova, Homo Floresiensis, and other potential Hominin people lurking around Eurasia around the Wisconsin Glacial Period.

He then talks about how in ancient times, Europe was united under various different tribes, some coming from the East, some coming from the South, and how Europe was transformed by these migrations and that most Europeans today are descended from those Eastern Invaders and in that sense Europe was united long ago, when we were still in the Upper Paleolithic, until the advent of Agriculture in the Neolithic Age.

Next, Rutherford investigates the origin of the American Indian cultures. He tells the story of Kennewick Man in much detail and why it’s so hard to get American Indians to consent to being genetically sequenced. Despite these difficulties, he does show that American Indians all probably descent from a single migration over the Bering Strait and how the Inuit have genetic modifications for low oxygen environments, similar to the Tibetans.

The next part gets a little heady. The idea that we are all descended from Charlemagne isn’t too hard to believe but the idea that we could be descended from folks from the Andaman Islands or Australian Aborigines seems to be pushing it. When you think about it, the base logic is correct. Going back twenty generations you have over a million man great grandparents, and over thirty you have over a billion. Clearly, if each generation averages twenty years, in six thousand years time you do have in theory one billion ancestors, but as there wasn’t a billion people six thousand years ago, clearly there must be some inbreeding. Not necessarily first cousin inbreeding, but maybe seventh or eighth cousin a remove or two would be commonplace.

The problem is when you think that this implies that everyone alive back then who had a child must be your ancestor is a false premise. One can guess the amount of inbreeding, but in truth, it’s possible, and even probable, that the inbreeding is even tighter than the whole population of six thousand years ago. It seems more logical, even if the clusters of today are different than the population clusters from back then, that the Australians at least were isolated until 1606, when Europeans started coming there. With only four hundred years contact, I’m highly dubious I’m descended from a single Aborigine from six thousand years ago, despite many of those Aborigines having descendants alive today. Charlemagne, maybe, but not everyone who ever lived six thousand years ago.

I did, however, like the story of Richard Ⅲ‘s discovery and it’s comparison to the insane idea that we could find Jack the Ripper in a used hankie. Great presentation of how to do bad and good science. The discussion of Francis Galton was also interesting, as there is stuff to admire the statistical genius with so much racism in his heart.

The topic of Race was an interesting one As Rutherford is half-South-Asian, I know that he would have suffered discrimination in the United Kingdom and of course feel for him. As a half-Jew, I have noted very little Jewish discrimination in the United States, apart from tourists from Europe, but when I do go to Europe, especially the farther East I go, I do notice a distinct hint of Anti-Semitism there. Nothing to write home about, just the random bloke who clearly has a problem with my nose.

However, I will say I think it’s excellent the way Rutherford points out there are more differences within race than there are distinguishing genetic characteristics within a race. I would, though, love to have red hair—well, to be honest, I’d love to have any hair, but that’s another story.

The discussion of SNPs and GWAS. There’s a great discussion of why it’s so hard to find the causes of diseases. After all, it’s very unlikely a SNP change in a single protein expression will change a behavior. And even the known genetic defects can have gene modifiers. The discussion of how heritable certain characteristics are was also fascinating. And the definition of epigenetics was a great new wrinkle. The only element missing is the influence of the bacterial flora that also influences our behavior.

Finally, it was nice to ground us in what evolution can and cannot do. The HOX Genes discussion was fun, as I do like the idea of a HOX d2 gene added to make a great story. And also, it’s interesting that GWAS can’t find an evil gene. I still blame testosterone for much of the evil in the world, but clearly even that hormone can’t be the only element at the root of modern violence. Indeed, if we could eliminate child abuse, we would go a very long way to solving many of what ills our society.

In summary, genetics is a wonderful tool in the development of biological understanding, but I wonder just what our current trends in slow evolution will bring. Only time will tell.

A Brief History of Everyone Who Ever Lived: The Human Story Retold Through Our Genes
A Brief History of Everyone Who Ever Lived: The Human Story Retold Through Our Genes

Next up, The Remedy: Robert Koch, Arthur Conan Doyle, and the Quest to Cure Tuberculosis, another book without a commute, with three weeks to complete…

Hope to see you in person soon, my sapiosexual friends!