<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>hi, it&#39;s mike</title>
    <link>https://mike.puddingtime.org/tags/prm/</link>
    <description>Recent content on hi, it&#39;s mike</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>mike@puddingtime.org (mike)</managingEditor>
    <webMaster>mike@puddingtime.org (mike)</webMaster>
    <copyright>© 2026, mike</copyright>
    <lastBuildDate>Wed, 03 May 2023 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://mike.puddingtime.org/tags/prm/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Daily Notes for 2023-05-03</title>
      <link>https://mike.puddingtime.org/posts/2023-05-03-daily-notes/</link>
      <pubDate>Wed, 03 May 2023 00:00:00 +0000</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2023-05-03-daily-notes/</guid>
      <description>A Ruby/CLI-based plaintext PRM, Robert DeNiro on exporting org-mode to JSON, blogging with ox-hugo, that Royal Enfield Himalayan</description>
      <content:encoded><![CDATA[<h2 id="friends-a-plaintext-ruby-based-prm">&ldquo;friends&rdquo; - a plaintext, Ruby-based PRM</h2>
<p>Looking around for other people who have done CRM/PRM-ish things in plaintext, I found <a href="https://github.com/JacobEvelyn/friends">JacobEvelyn/friends</a>. It&rsquo;s written in Ruby, uses Markdown for its home format, and gives you a command line interface to a record of your friends and activities.  I appreciate how thoroughly it thinks about what it is trying to do, and I sense a set of concerns similar to mine about the &ldquo;keeping up with personal contacts&rdquo; challenge.</p>
<p>Some things I like about it:</p>
<ul>
<li>Simple CLI data entry syntax</li>
<li>Some &ldquo;habit tracking&rdquo; style reporting to help you understand if you&rsquo;re keeping up your practice.</li>
<li>Clean reporting with a lot of flexibility that would let you build more reporting.</li>
<li>Simple use of Markdown, with no elaborate syntactical overlay.  If you gave up on friends, your data would be easily readable.</li>
</ul>
<p>It&rsquo;s probably good to note that friends isn&rsquo;t a full contact management system. It&rsquo;s better to think of it as a sort of journaling and habit tracking tool with a tight focus on keeping up with people, not a way to manage all your contact details. If I could extend one thing about it, it would probably be to be able to store email addresses with contacts: Email addresses aren&rsquo;t <em>great</em> keys, but also they&rsquo;re fine keys sometimes, and they&rsquo;d open friends up to interacting with other tools.</p>
<h2 id="ox-json-and-the-wisdom-of-neil-mccauley">ox-json and the wisdom of Neil McCauley</h2>
<p>As I was looking through the docs for friends and waming up to it some I wondered how readily I could migrate my org-contacts information. My home language is Ruby, so I tend to start there when I&rsquo;m looking for a library. There&rsquo;s one org-mode gem I&rsquo;m aware of, but its primary preoccupation is converting org-mode to HTML or Textile for presentation purposes.</p>
<p>Another way to come at the problem is to get the org markup into something more universally parseable, which is where <a href="https://github.com/jlumpe/ox-json">ox-json</a> could help. Does what it says on the tin: Converts an org-mode file into JSON, including, crucially, the data stored in the  <code>:PROPERTIES:</code>  drawer. Currently it passes by the <code>:LOGBOOK:</code> drawer, so that limits what you can do with it, but it still opens up possibilities.</p>
<p>Why?</p>
<div style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/rGPWW9Pjzto" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
<h2 id="ox-hugo-update">ox-hugo update</h2>
<p>I started blogging with <a href="https://github.com/kaushalmodi/ox-hugo">ox-hugo</a> several weeks ago, going into it a little warily.</p>
<p>Recap:</p>
<ul>
<li>You write all your posts in a monolithic org-mode file.</li>
<li>Each heading is a post.</li>
<li>Heading tags become post tags.</li>
<li>Headings in a <code>TODO</code> state are drafts.</li>
<li>Metadata can be stored in the <code>:PROPERTIES:</code> drawer (tidy, but the templating syntax gets cluttery if you&rsquo;re not a lisp native) or additional metadata src blocks (more visually cluttered when writing, but easier to read the template  if you&rsquo;re a YAML native)</li>
<li>You can set it up to automatically export the Markdown version into your Hugo content hierarchy whenever you save the buffer.</li>
</ul>
<p>Why would you want to do this?</p>
<p>As someone who does a lot of digest posts, I like having my pre-publication notes, links, etc. in the org-mode ecosystem, with all of its text manipulation affordances.  If a topic I&rsquo;m working on isn&rsquo;t ready when it&rsquo;s time to publish that day, I just <code>refile</code> the subheading under my <code>* Daily Post Overflow</code>  heading and keep going. I also like org-mode&rsquo;s structure editing features. It&rsquo;s simple to move headings and their content around within a post.</p>
<p>I thought the &ldquo;all-in-one-file&rdquo; thing would annoy me, and there is part of me that still doesn&rsquo;t like seeing all the surrounding context, but that&rsquo;s what <code>subtree to indirect buffer</code> is for. I drop into an indirect buffer for the long-haul writing, then pop back out of it if I need to pull things in from the overflow area or check on something from a previous post.</p>
<p>I did stub my toe on one thing, which was that the org-capture template I found to make the post setup simpler was setting <code>:EXPORT-HUGO-DATE:</code>, which updates dynamically when you save a post heading. I went back to make some edits to a post, saved my work, and it altered the date metadata in the Markdown output and jumbled my post order. The answer seemed to be to switch that to <code>:EXPORT_DATE:</code>, and now it behaves.</p>
<p>I also put off cleaning up my capture template so all the metadata could go in the <code>:PROPERTIES:</code> drawer. At first It was easier  to just embed some YAML at the top of the post body with <code>#+begin_src yaml :front_matter_extra t</code> rather than working out the Lispier syntax for post image and cover image in the context of writing a capture template.  It just took a few minutes to fix once I decided to bother with it, and the template now outputs <code>:PROPERTIES:</code> metadata:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-org" data-lang="org"><span class="line"><span class="cl">:EXPORT_HUGO_CUSTOM_FRONT_MATTER<span class="gd">+: :cover &#39;((image . &#34;&#34; ) (caption . &#34;&#34; ))
</span></span></span><span class="line"><span class="cl"><span class="gd">:EXPORT_HUGO_CUSTOM_FRONT_MATTER+</span>: :images &#39;(/momo-logo.jpg)
</span></span><span class="line"><span class="cl">:EXPORT_DESCRIPTION:</span></span></code></pre></div>
<p>I only sometimes use cover images, but I like to include my site logo in social posts, etc. when I don&rsquo;t have some other image to show, so the template defaults to an empty cover image and <code>image</code> metadata that Hugo&rsquo;s OpenGraph templating can pull in.</p>
<p>Several weeks in, I like the workflow. One tiny part of my soul is troubled that I have org source and Markdown output, but on the other hand the org source overwrites the Markdown output on edit, so the two don&rsquo;t drift. Realistically, the Markdown would be the more migratable content were I to shift off of Hugo, and it&rsquo;s simply better to author in org-mode.  So there&rsquo;s no associated toil and each format gets to be useful in the way it is best suited to be so.</p>
<h2 id="that-royal-enfield-himalayan">That Royal Enfield Himalayan</h2>
<p>I complained a little about my Royal Enfield Himalayan a few days ago: a little big for the power it has, and it had some QA problems that took some time to track down  I am pretty sure I am going to sell it to fund something similar.  But I did swap in a fresh battery and cleaned it up from winter storage, and rode it up to St. Johns for lunch yesterday, which meant a few dozen miles. It ran pretty well!</p>
<p>Last year, after dealing with rough idling and stalls, I finally broke down and installed a <a href="https://www.boosterplug.com/shop/frontpage.html">BoosterPlug</a>. Himalayans run too lean out of the factory, and the difference after installing one was pretty amazing. It was about a five-minute operation and it made the difference between a very rough first five minutes and &ldquo;let it idle for 30 seconds and it&rsquo;ll be fine.&rdquo; The machine never stalls now. I do think it still idles a little low, but that&rsquo;s a fine-tuning thing.</p>
<p>Anyhow, it was nice to ride around. Yeah, it&rsquo;s a little big, but it&rsquo;s not a big bike. There&rsquo;s plenty of pep for the city. Running up HWY 30, it did fine with the lunch crowd and there was plenty of power to overtake or squeeze out of spots at urban parkway speeds. I&rsquo;d do exit-to-exit on the Portland bypasses with it.</p>
<p>I was also glad to see <a href="https://www.sabatinomoto.com">an RE dealership up in St. John.</a>  Wasn&rsquo;t a fan of the Harley dealership I was getting service at and had to do a lot of research on my own to get help when it was suffering from factory QA problems.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Finding an org-contact record&#39;s emails in MailMate and events in Google Calendar</title>
      <link>https://mike.puddingtime.org/posts/2023-05-02-finding-an-org-contact-record-s-emails-in-mailmate/</link>
      <pubDate>Tue, 02 May 2023 00:00:00 -0700</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2023-05-02-finding-an-org-contact-record-s-emails-in-mailmate/</guid>
      <description>Looking up email histories and past Google Calendar events from org-contacts, and a few ideas about how to schedule time with people.</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve been digging <a href="https://freron.com">MailMate</a>, but missing a function I&rsquo;d added to my Doom Emacs setup that let me search mu4e for mails from a contact in my <code>contacts.org</code> file.  I set out to fix that this evening thinking it&rsquo;d probably be an AppleScript thing, but <a href="https://manual.mailmate-app.com/extended_url_scheme">it turns out MailMate has its own URL scheme</a> (<code>mlmt:</code>) that includes queries. From the command line, for instance, you&rsquo;d just do something like <span class="inline-src language-sh" data-lang="sh"><code>open mlmt:quicksearch?string=&quot;foo@bar.com&quot;</code></span>  to search for that address.</p>
<p>(I learned about that from this post by James Sulak (another Emacs person, as it turns out), who shared <a href="https://boxkitemachine.net/posts/mailmate-and-alfred/">a set of helpful Alfred workflows for working with MailMate</a>.)</p>
<p>This function grabs the <code>EMAIL</code> property of a given org-contacts heading and runs the <code>open</code> shell command:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">mph/open-mlmt-quicksearch</span> <span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Open a quicksearch URL for the email address at point.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">email</span> <span class="p">(</span><span class="nv">org-entry-get</span> <span class="p">(</span><span class="nf">point</span><span class="p">)</span> <span class="s">&#34;EMAIL&#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nv">not</span> <span class="p">(</span><span class="nv">string-empty-p</span> <span class="nv">email</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nv">shell-command</span> <span class="p">(</span><span class="nf">format</span> <span class="s">&#34;open &#39;mlmt:quicksearch?string=%s&#39;&#34;</span> <span class="nv">email</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;No email address found&#34;</span><span class="p">))))</span></span></span></code></pre></div>
<p>MailMate search is very fast. The results are there in an eyeblink.</p>
<p>&hellip; and that sort of led to this, which searches Google Calendar for an email address from a contact:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">mph/open-gcal-search-for-email</span> <span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Open a Google Calendar search page for the email address at point.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">email</span> <span class="p">(</span><span class="nv">org-entry-get</span> <span class="p">(</span><span class="nf">point</span><span class="p">)</span> <span class="s">&#34;EMAIL&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nv">search-url</span> <span class="s">&#34;https://calendar.google.com/calendar/u/0/r/search?q=%s&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nv">not</span> <span class="p">(</span><span class="nv">string-empty-p</span> <span class="nv">email</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nb">progn</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;Searching Google Calendar for events with email %s...&#34;</span> <span class="nv">email</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">           <span class="p">(</span><span class="nv">browse-url</span> <span class="p">(</span><span class="nf">format</span> <span class="nv">search-url</span> <span class="nv">email</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;No email address found&#34;</span><span class="p">))))</span></span></span></code></pre></div>
<p>&hellip; and that suggested this one, which gets a date from the interactive org date picker and creates an all-day  Google Calendar event with the contact as an invitee:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">mph/create-gcal-all-day-appointment-with-contact</span> <span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Create a new all-day appointment in Google Calendar and invite the contact at point.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">date</span> <span class="p">(</span><span class="nv">org-read-date</span> <span class="no">nil</span> <span class="no">t</span> <span class="no">nil</span> <span class="s">&#34;Date: &#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="nv">formatted-date</span> <span class="p">(</span><span class="nf">format-time-string</span> <span class="s">&#34;%Y%m%d&#34;</span> <span class="nv">date</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="nv">next-day</span> <span class="p">(</span><span class="nf">format-time-string</span> <span class="s">&#34;%Y%m%d&#34;</span> <span class="p">(</span><span class="nf">time-add</span> <span class="nv">date</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">24</span> <span class="mi">60</span> <span class="mi">60</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="nv">contact-email</span> <span class="p">(</span><span class="nv">org-entry-get</span> <span class="p">(</span><span class="nf">point</span><span class="p">)</span> <span class="s">&#34;EMAIL&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="nv">url</span> <span class="p">(</span><span class="nf">concat</span> <span class="s">&#34;https://calendar.google.com/calendar/u/0/r/eventedit?dates=&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="nv">formatted-date</span>
</span></span><span class="line"><span class="cl">                      <span class="s">&#34;/&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="nv">next-day</span>
</span></span><span class="line"><span class="cl">                      <span class="s">&#34;&amp;pli=1&amp;sf=true&amp;action=TEMPLATE&amp;add=&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="nv">contact-email</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nv">browse-url</span> <span class="nv">url</span><span class="p">)))</span></span></span></code></pre></div>
<p>I preferred just setting it to all-day, because I&rsquo;ve learned a few things about scheduling time to catch up with people in a non-business context:</p>
<ol>
<li>It&rsquo;s usually gonna be a few weeks out. PTO, busy, etc.</li>
<li>Setting a day is pretty easy, but setting a good time can be hard when it&rsquo;s that far out. Schedules do things, or we know a given day is <em>usually</em> our good day, but specifics can shift around.</li>
<li>Setting an all-day item and a reminder to lock down the details several days out makes it easier to agree to <em>something</em> and work out the details when calendars are a little more clear. No constant shuffling if one party or the other isn&rsquo;t in complete control of their own calendar.</li>
</ol>
<p>I wonder if I should go read a book about how to stay in touch with people. I know there are several.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Were you to attempt something like this in AppleScript</title>
      <link>https://mike.puddingtime.org/posts/2023-04-27-were-you-to-attempt-this-with-applescript/</link>
      <pubDate>Thu, 27 Apr 2023 00:00:00 +0000</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2023-04-27-were-you-to-attempt-this-with-applescript/</guid>
      <description>&lt;p&gt;I started down the path of &lt;a href=&#34;https://mike.puddingtime.org/posts/20230413-making-a-plaintext-personal-crm-with-org-contacts/&#34;&gt;building some sort of PRM in org-mode&lt;/a&gt; because I couldn&amp;rsquo;t find anything that worked the way I wanted. I did briefly look at Apple&amp;rsquo;s Contacts app, and also at &lt;a href=&#34;https://flexibits.com/cardhop&#34;&gt;Cardhop&lt;/a&gt;, which builds on top of your Contacts database but still makes some assumptions about how good you are at all at remembering to reach out to people.&lt;/p&gt;
&lt;p&gt;I also looked at &lt;a href=&#34;https://monicahq.com&#34;&gt;Monica&lt;/a&gt;, an open source PRM. The promising part of Monica is its API. The web UI itself shows comprehensive data for each contact, but does not do anything in the way of bulk editing and has no automation at all. It&amp;rsquo;s laborious to bootstrap.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I started down the path of <a href="https://mike.puddingtime.org/posts/20230413-making-a-plaintext-personal-crm-with-org-contacts/">building some sort of PRM in org-mode</a> because I couldn&rsquo;t find anything that worked the way I wanted. I did briefly look at Apple&rsquo;s Contacts app, and also at <a href="https://flexibits.com/cardhop">Cardhop</a>, which builds on top of your Contacts database but still makes some assumptions about how good you are at all at remembering to reach out to people.</p>
<p>I also looked at <a href="https://monicahq.com">Monica</a>, an open source PRM. The promising part of Monica is its API. The web UI itself shows comprehensive data for each contact, but does not do anything in the way of bulk editing and has no automation at all. It&rsquo;s laborious to bootstrap.</p>
<p>In the process of trying to figure out how I could write some automation to move Contacts information beyond the basic vCard fields into Monica I did end up having to learn about how macOS Contacts work and realized you can create custom labels for date fields then add them to your card editing template in Contacts&rsquo; preferences.</p>
<p>The data entry widget for these fields expects a date and is tolerant of not entering a year (which helps it support, er, &ldquo;polite&rdquo; birthdays). You can, in turn, use it somewhat opaquely in a Contacts smart list: There&rsquo;s a generic &ldquo;Date&rdquo; field you can filter on that looks at date fields in the card.  Paired with a &ldquo;within/not within,&rdquo; or &ldquo;in the next&rdquo; parameter, you can make a smart list of &ldquo;people not contacted in the past 30 days,&rdquo; etc.</p>
<p>If Contacts smart lists could also use groups, you could do a lot by just setting a &ldquo;last contacted&rdquo; date field and making a set of smart lists based on group membership. Contacts smart lists <em>can&rsquo;t</em> use groups, though, which is a strange oversight.</p>
<p>As I was trying to figure out, though, how to get my contacts uploaded to Monica in a way that would let me use its API to add tags to them once they were imported, I worked out some AppleScript that let me prepend a contact&rsquo;s group into its note as a hashtag. Contacts smart lists <em>can</em> filter on the contents of notes.</p>
<p>As a solution goes, it&rsquo;s in the category of &ldquo;cheap and cheerful.&rdquo; If you wanted to use macOS Contacts to keep track of your most recent touchpoint with someone, and drive a little automation to surface contacts you haven&rsquo;t reached out to in a while, you could do it with one custom field and adopting a simple convention for notes. You&rsquo;re also well into the territory of things AppleScript can do to help out, too: It is trivial to write scripts that automate logging, etc. or even write reminders or make events in a contacts calendar. In fact, here&rsquo;s a script that operates on the selected contact and lets you log activity in its note:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-applescript" data-lang="applescript"><span class="line"><span class="cl"><span class="k">set</span> <span class="nv">theDate</span> <span class="k">to</span> <span class="nb">current date</span>
</span></span><span class="line"><span class="cl"><span class="k">set</span> <span class="nv">noteDate</span> <span class="k">to</span> <span class="nb">do shell script</span> <span class="s2">&#34;date &#39;+%Y-%m-%d&#39;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&#34;Contacts&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="k">set</span> <span class="nv">selectedPeople</span> <span class="k">to</span> <span class="nv">selection</span>
</span></span><span class="line"><span class="cl">	<span class="k">repeat</span> <span class="k">with</span> <span class="nv">thePerson</span> <span class="k">in</span> <span class="nv">selectedPeople</span>
</span></span><span class="line"><span class="cl">		<span class="k">set</span> <span class="nv">customDates</span> <span class="k">to</span> <span class="nv">custom</span> <span class="nv">dates</span> <span class="k">of</span> <span class="nv">thePerson</span>
</span></span><span class="line"><span class="cl">		<span class="k">set</span> <span class="nv">lastContactedExists</span> <span class="k">to</span> <span class="no">false</span>
</span></span><span class="line"><span class="cl">		<span class="k">repeat</span> <span class="k">with</span> <span class="nv">aCustomDate</span> <span class="k">in</span> <span class="nv">customDates</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="na">label</span> <span class="k">of</span> <span class="nv">aCustomDate</span> <span class="ow">is</span> <span class="s2">&#34;last contacted&#34;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">				<span class="k">set</span> <span class="nv">value</span> <span class="k">of</span> <span class="nv">aCustomDate</span> <span class="k">to</span> <span class="nv">theDate</span>
</span></span><span class="line"><span class="cl">				<span class="k">set</span> <span class="nv">lastContactedExists</span> <span class="k">to</span> <span class="no">true</span>
</span></span><span class="line"><span class="cl">			<span class="k">end</span> <span class="k">if</span>
</span></span><span class="line"><span class="cl">		<span class="k">end</span> <span class="k">repeat</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="ow">not</span> <span class="nv">lastContactedExists</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nv">length</span> <span class="k">of</span> <span class="nv">customDates</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">				<span class="k">set</span> <span class="nv">firstCustomDate</span> <span class="k">to</span> <span class="nb">first</span> <span class="nb">item</span> <span class="k">of</span> <span class="nv">customDates</span>
</span></span><span class="line"><span class="cl">				<span class="k">set</span> <span class="nv">newCustomDate</span> <span class="k">to</span> <span class="nb">make</span> <span class="nb">new</span> <span class="nv">custom</span> <span class="nv">date</span> <span class="nb">at</span> <span class="nb">after</span> <span class="nv">firstCustomDate</span>
</span></span><span class="line"><span class="cl">			<span class="k">else</span>
</span></span><span class="line"><span class="cl">				<span class="nb">make</span> <span class="nb">new</span> <span class="nv">custom</span> <span class="nv">date</span> <span class="nb">at</span> <span class="k">end</span> <span class="k">of</span> <span class="nv">custom</span> <span class="nv">dates</span> <span class="k">of</span> <span class="nv">thePerson</span> <span class="k">with</span> <span class="na">properties</span> <span class="p">{</span><span class="na">label</span><span class="p">:</span><span class="s2">&#34;last contacted&#34;</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span><span class="nv">theDate</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">			<span class="k">end</span> <span class="k">if</span>
</span></span><span class="line"><span class="cl">		<span class="k">end</span> <span class="k">if</span>
</span></span><span class="line"><span class="cl">		<span class="k">set</span> <span class="nv">theNote</span> <span class="k">to</span> <span class="nv">note</span> <span class="k">of</span> <span class="nv">thePerson</span> <span class="k">as </span><span class="nc">string</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nv">theNote</span> <span class="ow">is</span> <span class="s2">&#34;missing value&#34;</span> <span class="k">then</span> <span class="k">set</span> <span class="nv">theNote</span> <span class="k">to</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">		<span class="k">set</span> <span class="nv">prependText</span> <span class="k">to</span> <span class="na">text returned</span> <span class="k">of</span> <span class="p">(</span><span class="nb">display dialog</span> <span class="s2">&#34;Enter text to prepend to the note of &#34;</span> <span class="o">&amp;</span> <span class="na">name</span> <span class="k">of</span> <span class="nv">thePerson</span> <span class="o">&amp;</span> <span class="s2">&#34;:&#34;</span> <span class="nv">default</span> <span class="nv">answer</span> <span class="s2">&#34;&#34;</span> <span class="nb">buttons</span> <span class="p">{</span><span class="s2">&#34;Cancel&#34;</span><span class="p">,</span> <span class="s2">&#34;OK&#34;</span><span class="p">}</span> <span class="nv">default</span> <span class="nb">button</span> <span class="s2">&#34;OK&#34;</span> <span class="nv">cancel</span> <span class="nb">button</span> <span class="s2">&#34;Cancel&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">set</span> <span class="nv">noteUpdated</span> <span class="k">to</span> <span class="s2">&#34;[&#34;</span> <span class="o">&amp;</span> <span class="nv">noteDate</span> <span class="o">&amp;</span> <span class="s2">&#34;] &#34;</span> <span class="o">&amp;</span> <span class="nv">prependText</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nv">theNote</span> <span class="ow">is not</span> <span class="s2">&#34;&#34;</span> <span class="k">then</span> <span class="k">set</span> <span class="nv">noteUpdated</span> <span class="k">to</span> <span class="nv">noteUpdated</span> <span class="o">&amp;</span> <span class="no">return</span> <span class="o">&amp;</span> <span class="nv">theNote</span> <span class="o">&amp;</span> <span class="no">return</span> <span class="o">&amp;</span> <span class="s2">&#34; &#34;</span> <span class="o">&amp;</span> <span class="no">return</span>
</span></span><span class="line"><span class="cl">		<span class="k">set</span> <span class="nv">note</span> <span class="k">of</span> <span class="nv">thePerson</span> <span class="k">to</span> <span class="nv">noteUpdated</span>
</span></span><span class="line"><span class="cl">		<span class="nv">save</span> <span class="nv">thePerson</span>
</span></span><span class="line"><span class="cl">	<span class="k">end</span> <span class="k">repeat</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> <span class="k">tell</span></span></span></code></pre></div>
<p>So, looking at that, would you <em>want</em> to glue all this together with Applescript?</p>
<p>I don&rsquo;t think so. I don&rsquo;t, anyhow. It just took me a morning to figure that out.</p>
<p>In the process of roughing out automation for creating reminders, for instance, I managed to get Reminders.app to beachball on every run with a simple five-liner. Why? I don&rsquo;t know. Stack Overflow didn&rsquo;t know. But after a good 15 years of using AppleScript for jobs large and small, I know that sometimes you find its weird little corner cases and that&rsquo;s all there is to it. I&rsquo;d rather <a href="https://www.jwz.org/blog/2003/05/no-good-deed-goes-unpunished/">tell Jamie Zawinski that I don&rsquo;t know who the author of XScreensaver is</a> than ask Apple to fix it.</p>
<p>When I ask myself &ldquo;would I want to build something on top of this ecosystem that I mean to use forever?&rdquo; I can&rsquo;t even figure out three scripts I would need to write then get to the end of debugging the second one before I know the answer is &ldquo;no.&rdquo;</p>
<p>I mean, what does <em>forever</em> mean? Because my personal belief is that we have to assess the foreverness of the competing candidates, and probably think about how amenable data kept in the least forever of those formats is to being migrated to a more forever format/system:</p>
<ul>
<li>macOS Contacts</li>
<li>org-mode</li>
<li>vCard</li>
<li>plain text with moderately elaborate markup amenable to some automated processing</li>
</ul>
<p>(I put that list in ascending order of longevity/permanence, feeling very appreciative that org-mode allowed me to reorder it with <code>opt arrow</code>).</p>
<p>I trust macOS Contacts a bit. I don&rsquo;t how much money I would be willing to risk on a series of bets about it working as it does today in 2, 5, or 10 years. While trying to understand my AppleScript options with it I was brought face-to-face with changes to the underlying scripting model several times. In all fairness, those changes played out over decades and it&rsquo;s only because I&rsquo;m at a point in life where I can still think &ldquo;OS X is new&rdquo; that they even seem mentionable. It&rsquo;s also completely possible to get Contacts info out into some other format. There&rsquo;s also just the whole &ldquo;ramming your head against AppleScript&rdquo; aspect of the problem.</p>
<p>Moving up the Pyramid of Forever, I trust org-mode to be around for a very long time, but can also see how the API is still subject to change. Functions come and go so automation can break and make it hard to keep a contacts list maintained. On the other hand, I&rsquo;m not doing much now that couldn&rsquo;t be done by hand until I figured it out, and the things I&rsquo;ve bumped into are pretty small so far: changes in the namespace, functional replacements, etc.</p>
<p>Moving on:</p>
<p>I trust the vCard standard to stay how it is a bit more. It&rsquo;s on &hellip; version 3 or 4? &hellip; of the spec? There&rsquo;s a spec. There are a lot of stakeholders interested in that spec. Even Apple quietly crams a whole vCard property into each contact, even if it has its own version of each property you&rsquo;d find in a vCard anyhow.</p>
<p>So, moving on to the top of the pyramid:</p>
<p>I trust structured plain text to be useful for the rest of my life.</p>
<p>So something built on org-mode seems like the smart play for data longevity? Even if all my automation broke, core org-mode makes it easy to do the things I do: change todo states, add values to the <code>PROPERTIES</code> drawer, add tags, log changes, etc.</p>
<p>The one thing that I&rsquo;ve <em>mostly</em> decided not to worry about is the disconnect between my contacts list and my org-contacts file.</p>
<p>One useful feature of contacts via Fastmail is the dynamic &ldquo;Autosaved&rdquo; group. If I write someone, they go into that group. Periodically moving contacts that surface there into one of my permanent groups then removing them from &ldquo;Autosaved&rdquo; provides a simple, organic workflow for keeping up with new people. There is also a bunch of useful automation present in macOS/iOS for surfacing things about contacts from other apps, so you&rsquo;re constantly being offered the opportunity to pull in new information. This morning, for instance, going through my Contacts list, I noticed that something somewhere in the bowels of macOS or iOS was beginning to notice connections between my contacts and Ivory, the Mastodon client. That&rsquo;s pretty cool.</p>
<p>On the other hand, <em>this</em> is well within AppleScript&rsquo;s wheelhouse:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-applescript" data-lang="applescript"><span class="line"><span class="cl"><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&#34;Contacts&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="k">set</span> <span class="nv">selectedContacts</span> <span class="k">to</span> <span class="nv">selection</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nv">length</span> <span class="k">of</span> <span class="nv">selectedContacts</span> <span class="ow">is not</span> <span class="mi">1</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">		<span class="nb">display alert</span> <span class="s2">&#34;Please select exactly one contact.&#34;</span>
</span></span><span class="line"><span class="cl">		<span class="no">return</span>
</span></span><span class="line"><span class="cl">	<span class="k">end</span> <span class="k">if</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">set</span> <span class="nv">theContact</span> <span class="k">to</span> <span class="nb">first</span> <span class="nb">item</span> <span class="k">of</span> <span class="nv">selectedContacts</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">set</span> <span class="nv">theName</span> <span class="k">to</span> <span class="na">name</span> <span class="k">of</span> <span class="nv">theContact</span> <span class="k">as </span><span class="nc">text</span>
</span></span><span class="line"><span class="cl">	<span class="k">set</span> <span class="nv">theEmails</span> <span class="k">to</span> <span class="nv">value</span> <span class="k">of</span> <span class="nv">emails</span> <span class="k">of</span> <span class="nv">theContact</span> <span class="k">as </span><span class="nc">text</span>
</span></span><span class="line"><span class="cl">	<span class="k">set</span> <span class="nv">thePhones</span> <span class="k">to</span> <span class="nv">value</span> <span class="k">of</span> <span class="nv">phones</span> <span class="k">of</span> <span class="nv">theContact</span> <span class="k">as </span><span class="nc">text</span>
</span></span><span class="line"><span class="cl">	<span class="k">set</span> <span class="nv">theNote</span> <span class="k">to</span> <span class="nv">note</span> <span class="k">of</span> <span class="nv">theContact</span> <span class="k">as </span><span class="nc">text</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c">-- Format the contact data as an org contacts string</span>
</span></span><span class="line"><span class="cl">	<span class="k">set</span> <span class="nv">theContactString</span> <span class="k">to</span> <span class="s2">&#34;** &#34;</span> <span class="o">&amp;</span> <span class="nv">theName</span> <span class="o">&amp;</span> <span class="s2">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">:PROPERTIES:
</span></span></span><span class="line"><span class="cl"><span class="s2">:EMAIL: &#34;</span> <span class="o">&amp;</span> <span class="nv">theEmails</span> <span class="o">&amp;</span> <span class="s2">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">:PHONE: &#34;</span> <span class="o">&amp;</span> <span class="nv">thePhones</span> <span class="o">&amp;</span> <span class="s2">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">:NOTE: &#34;</span> <span class="o">&amp;</span> <span class="nv">theNote</span> <span class="o">&amp;</span> <span class="s2">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">:NAME: &#34;</span> <span class="o">&amp;</span> <span class="nv">theName</span> <span class="o">&amp;</span> <span class="s2">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">:END:&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c">-- Copy the contact string to the clipboard</span>
</span></span><span class="line"><span class="cl">	<span class="nb">set the clipboard to</span> <span class="nv">theContactString</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> <span class="k">tell</span></span></span></code></pre></div>
<p>&ldquo;Look through new contacts, run that script from a keyboard shortcut, paste the new contact into my <code>contacts.org</code> file.&rdquo;</p>
<p>I could go one step further and <em>append</em> the text to my <code>contacts.org</code> file, but I don&rsquo;t like operating on busy files like that.</p>
<p>AppleScript was plainly built to do little things like that. You have to learn its sort of crabbed, verbose way of doing things, but it&rsquo;s not too hard (and Shortcuts is getting pretty good if you can deal with the sudden drop in functionality that can appear out of nowhere when you hit the limits of some Apple engineer&rsquo;s imagination or time).</p>
<p>Anyhow, it all just comes down to aesthetics and preferences, right?</p>
<p>I&rsquo;ve got a draft heading sitting in my blog.org file with a title of &ldquo;Plain text is calming.&rdquo; I&rsquo;m not sure where that little essay is going, but I know where it started: When I&rsquo;m staring at a text editor I feel much better than when I&rsquo;m staring at a web or app UI. I might have some challenges with discoverability, or a lack of forgiveness for little slips of the finger, or whatever. But I still feel better because I&rsquo;ve been a happy citizen of Plaintext Land for over 30 years, and there is a governing mentality there that does not exist in other parts of the technology world. I&rsquo;m not saying the <a href="https://www.puppet.com/docs/puppet/8/http_api/pson.html">occasional wheel doesn&rsquo;t get reinvented,</a> but I am saying that with most plaintext stuff you get to choose your tools, or make them for yourself if there are no good choices. So it&rsquo;s calming because I don&rsquo;t have that feeling of always looking for the exit when I encounter a plaintext system. I know it&rsquo;s there.  That&rsquo;s the preference, anyhow. I made money for a few years being really, really good at turning CMS databases into plaintext and massaging them into other CMSes, so my patience for finding the structure and working with it is high. I don&rsquo;t worry about where the exit is because in my 40-year history with computers, it has never eluded me in the plaintext world.</p>
<p>The aesthetics are another kettle of fish, and plaintext people run a weird gamut from &ldquo;text editors are like samurai swords&rdquo; to &ldquo;mastery of a plaintext interface is a kind of performance art.&rdquo; Right now I&rsquo;m sort of luxuriating in Evil mode, because I finally get the emphasis on ruthless elimination of motion vi engenders. I&rsquo;ve  <a href="/img/Joy04.pdf">made terrible fun of people over this in the distant past</a> and feel a little bad about that, but less than two months into this particular experiment, I don&rsquo;t want to go back. Evil mode is like the end of the first Star Trek movie as far as I&rsquo;m concerned.</p>
<figure><img src="/img/decker_ilia.gif"
    alt="Will Decker and Ilia merging into some sort of computer overmind in Star Trek: TMP">
</figure>

<p>Anyhow, I do want to get back to the whole &ldquo;plaintext is calming&rdquo; idea and do some more writing about it. Today&rsquo;s jaunt into &ldquo;what if I could make what I wanted in the macOS ecosystem?&rdquo; was one of those processes I go through when I&rsquo;ve gone so far with an idea and wonder if I really want to commit &ndash; if I&rsquo;m not making life a little harder on myself than I need to, or if there&rsquo;s not some simpler way to do it (even if still in the DIY mode,) and the answer came back &ldquo;don&rsquo;t think there is.&rdquo; I&rsquo;m at home in what I made.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
