<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/assets/rss.xsl" type="text/xsl"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"

>
<channel>
	<title>Sacha Chua - tag - automation</title>
	<atom:link href="https://sachachua.com/blog/tag/automation/feed/index.xml" rel="self" type="application/rss+xml" />
	<atom:link href="https://sachachua.com/blog/tag/automation" rel="alternate" type="text/html" />
	<link>https://sachachua.com/blog/tag/automation/feed/index.xml</link>
	<description>Emacs, sketches, and life</description>
  
	<lastBuildDate>Wed, 13 May 2026 14:32:18 GMT</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>daily</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>11ty</generator>
  <item>
		<title>Scripting and the grocery store flyer</title>
		<link>https://sachachua.com/blog/2015/12/scripting-grocery-store-flyer/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Fri, 18 Dec 2015 17:21:00 GMT</pubDate>
    <category>geek</category>
		<guid isPermaLink="false">https://sachachua.com/blog/?p=28518</guid>
		<description><![CDATA[<p>We plan the week&#8217;s meals around the grocery store flyers, taking advantage of what&#8217;s on sale (chicken, ground beef, etc.) and stocking up when the opportunity presents itself (for example, diced tomatoes or cream of mushroom soup).</p>
<p>The flyers are usually delivered on Thursdays. We do most of our grocery shopping at No Frills because it&#8217;s convenient and almost always a good price, but sometimes we&#8217;ll go to Freshco or Metro if there&#8217;s a particularly good sale. I might flip through the other flyers if we&#8217;re looking for something in particular, but most of the time, we just toss them out. I&#8217;d love to opt out of paper flyers, but that doesn&#8217;t seem to be an option in our neighbourhood.</p>
<p>It doesn&#8217;t take a lot of time to review the flyers, but I figured it would be fun to write a script that highlights specific items for us. Now I have a script that parses the output of the No Frills accessible flyer to create a table like this:</p>
<table border="2" frame="hsides" rules="groups" cellspacing="0" cellpadding="6">
<colgroup>
<col class="org-left">
<col class="org-left">
<col class="org-right">
<col class="org-right">
<col class="org-left"> </colgroup>
<tbody>
<tr>
<td class="org-left">Y</td>
<td class="org-left">Clementines</td>
<td class="org-right">2.47</td>
<td class="org-right"></td>
<td class="org-left">2 lb bag product of Spain $2.47</td>
</tr>
<tr>
<td class="org-left">Y</td>
<td class="org-left">Smithfield Bacon</td>
<td class="org-right">3.97</td>
<td class="org-right"></td>
<td class="org-left">500 g selected varieties $3.97</td>
</tr>
<tr>
<td class="org-left">Y</td>
<td class="org-left">Thomas&#8217; Cinnamon Raisin Bread</td>
<td class="org-right">2.5</td>
<td class="org-right"></td>
<td class="org-left">675 g or Weston Kaisers 12&#8217;s selected varieties $5.00 or $2.50 ea.</td>
</tr>
<tr>
<td class="org-left">Y</td>
<td class="org-left">Unico Tomatoes</td>
<td class="org-right">0.97</td>
<td class="org-right"></td>
<td class="org-left">796 mL or Beans 540 mL selected varieties $0.97</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Fresh Boneless Skinless Chicken Breast</td>
<td class="org-right">3.33</td>
<td class="org-right">2.78</td>
<td class="org-left">BIG Pack!™ DECEMBER 18TH &#8211; 24TH ONLY! $3.33 lb/$7.34/kg save $2.78/lb</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Purex</td>
<td class="org-right">3.97</td>
<td class="org-right">2.02</td>
<td class="org-left">2.03 L $3.97 save $2.02</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Frozen Steelhead Trout Fillets</td>
<td class="org-right">5.97</td>
<td class="org-right">2.0</td>
<td class="org-left">filets de truite $5.97 lb/$13.16/kg save $2.00/lb</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Heinz Tomato Juice</td>
<td class="org-right">0.97</td>
<td class="org-right">1.52</td>
<td class="org-left">1.36 L selected varieties $0.97 save $1.52</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Nestlé Multi-Pack Chocolate or Bagged Chocolate</td>
<td class="org-right">2.88</td>
<td class="org-right">0.61</td>
<td class="org-left">45-246 g selected varieties $2.88 save 61¢</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Source</td>
<td class="org-right">4.97</td>
<td class="org-right">0.5</td>
<td class="org-left">16 x 100 g selected varieties $4.97 save 50 ¢</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Franco Gravy</td>
<td class="org-right">0.67</td>
<td class="org-right">0.32</td>
<td class="org-left">284 mL selected varieties $0.67 save 32¢</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Ocean Spray Cranberry Sauce</td>
<td class="org-right">1.67</td>
<td class="org-right">0.32</td>
<td class="org-left">348 mL whole or jellied $1.67 save 32¢</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">10 lb Bag Yellow Potatoes, 5 lb Bag Carrots or 10 lb Bag Yellow Cooking Onions</td>
<td class="org-right">1.87</td>
<td class="org-right"></td>
<td class="org-left">product of Ontario, Canada no. 1 grade or 5 lb Bag Rutabaga product of Canada, no. 1 grade $1.87</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Betty Crocker Hamburger Helper</td>
<td class="org-right">1.5</td>
<td class="org-right"></td>
<td class="org-left">158-255 g or Mashed or Scallop Potatoes 141-215 g selected varieties $3.00 or $1.50 ea.</td>
</tr>
<tr>
<td class="org-left"></td>
<td class="org-left">Blackberries</td>
<td class="org-right">1.25</td>
<td class="org-right"></td>
<td class="org-left">6 oz product of U.S.A. or Mexico DECEMBER 18TH &#8211; 24TH ONLY! 4/$5.00 or $1.25 ea.</td>
</tr>
</tbody>
</table>
<p>(more lines omitted)</p>
<p>The table is sorted by whether the item name matches one of the things we usually buy (first column: Y), then how much the sale is for, and then the name of the item. Over time, I&#8217;ll add more things to the priority list, and the script will get smarter and smarter.</p>
<p>I can use Org commands to move the rows up or down or remove the rows I&#8217;m not interested in. Then I can take the second column of the script&#8217;s output with Emacs&#8217; <code>copy-rectangle-as-kill</code> command (<code>C-x r M-w</code>), and paste it into <a href="http://ourgroceries.com/">OurGroceries</a>&#8216; import dialog. That builds a shopping list that&#8217;s sorted by the aisles I&#8217;ve previously set up, and this list is synchronized with our phones.</p>
<p>I&#8217;ve added the script to <a href="https://github.com/sachac/scripts/blob/master/check-grocery-flyer.js">https://github.com/sachac/scripts/blob/master/check-grocery-flyer.js</a>, so you can check there for updates. The flyer URL and the list of staples are defined in a separate configuration file that I haven&#8217;t included in the repository, but you can probably come up with your own if you want to adapt the idea. =)</p>
<p>Here&#8217;s the source, just in case:</p>
<div class="org-src-container">
<pre class="src src-js2">#!/usr/bin/env node

/*
  Creates a prioritized list based on the flyers, like this:

Y Clementines 2.47    2 lb bag product of Spain $2.47
Y Smithfield Bacon  3.97    500 g selected varieties $3.97
Y Thomas' Cinnamon Raisin Bread 2.50    675 g or Weston Kaisers 12's selected varieties $5.00 or $2.50 ea.
Y Unico Tomatoes  0.97    796 mL or Beans 540 mL selected varieties $0.97
  Fresh Boneless Skinless Chicken Breast  3.33  2.78  BIG Pack!™ DECEMBER 18TH - 24TH ONLY! $3.33 lb/$7.34/kg save $2.78/lb
  Purex 3.97  2.02  2.03 L $3.97 save $2.02
  Frozen Steelhead Trout Fillets  5.97  2.00  filets de truite $5.97 lb/$13.16/kg save $2.00/lb
  Heinz Tomato Juice  0.97  1.52  1.36 L selected varieties $0.97 save $1.52
  Nestlé Multi-Pack Chocolate or Bagged Chocolate 2.88  0.61  45-246 g selected varieties $2.88 save 61¢
  ...
  */

var rp = require('request-promise');
var cheerio = require('cheerio');
var homeDir = require('home-dir');
var config = require(homeDir() + '/.secret');
var staples = config.grocery.staples; // array of lower-case text to match against flyer items
var flyerURL = config.grocery.flyerURL; // accessible URL

function parseValue(details) {
  var matches;
  var price;
  if ((matches = details.match(/\$([\.0-9]+)( |&amp;nbsp;)+(ea|lb|\/kg)/i))) {
    price = matches[1];
  }
  else if ((matches = details.match(/\$([\.0-9]+)/i))) {
    price = matches[1];
  }
  else if ((matches = details.match(/([0-9]+) *¢/))) {
    price = parseInt(matches[1]) / 100.0;
  }
  return price;
}

function getFlyer(url) {
  return rp.get(url).then(function(response) {
    var $ = cheerio.load(response);
    var results = [];
    $('table[colspan="2"]').each(function() {
      var cells = $(this).find('td');
      // $0.67  or  2/$3.00 or $1.25ea
      var item = $(cells[0]).text().replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, '');
      var details = $(cells[1]).text().replace(/([ \t\r\n\u00a0\u0000]|&amp;nbsp;)+/g, ' ').replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, '');
      var matches;
      var save = '';
      var price = parseValue(details);
      details = details.replace(/ \/ [^A-Z$]+/, ' ');
      if (details.match(/To Our Valued Customers/)) {
        details = details.replace(/To Our Valued Customers.*/, 'DELAYED');
      }
      if ((matches = details.match(/save .*/))) {
        save = parseValue(matches[0]);
      }
      results.push({item: item,
                    details: details,
                    price: price,
                    save: save});
    });
    return results;
  });
}

function prioritizeFlyer(data) {
  for (var i = 0; i &lt; data.length; i++) {
    var name = data[i].item.toLowerCase();
    for (var j = 0; j &lt; staples.length; j++) {
      if (name.match(staples[j])) {
        data[i].priority = true;
      }
    }
  }
  return data.sort(function(a, b) {
    if (a.priority &amp;&amp; !b.priority) return -1;
    if (!a.priority &amp;&amp; b.priority) return 1;
    if (a.save &gt; b.save) return -1;
    if (a.save &lt; b.save) return 1;
    if (a.item &lt; b.item) return -1;
    if (a.item &gt; b.item) return 1;
  });
}

function displayFlyerData(data) {
  for (var i = 0; i &lt; data.length; i++) {
    var o = data[i];
    console.log((o.priority ? 'Y' : '') + '\t' + o.item + "\t" + o.price + "\t" + o.save + "\t" + o.details);
  }
}

getFlyer(flyerURL).then(prioritizeFlyer).then(displayFlyerData);
</pre>
</div>
<p>I&#8217;ll check next week to see if the accessible flyer URL changes each time, or if I can determine the correct publication ID by going to a stable URL. Anyway, this was fun to write!</p>

<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2015%2F12%2Fscripting-grocery-store-flyer%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Scripting and the Toronto Public Library&#8217;s movie collection</title>
		<link>https://sachachua.com/blog/2015/12/scripting-toronto-public-librarys-movie-collection/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Wed, 09 Dec 2015 05:00:00 GMT</pubDate>
    <category>geek</category>
		<guid isPermaLink="false">https://sachachua.com/blog/?p=28509</guid>
		<description><![CDATA[<p>We hardly ever watch movies in the theatre now, since we prefer watching movies with subtitles and the ability to pause. Fortunately, the Toronto Public Library has a frequently updated collection of DVDs. The best time to grab a movie is when it&#8217;s a new release, since DVDs that have been in heavy circulation can get pretty scratched up from use. However, newly released movies can&#8217;t be reserved. You need to find them at the library branches they&#8217;re assigned to, and then you can borrow them for seven days. You can check the status of each movie online to see if it&#8217;s in the library or when it&#8217;s due to be returned.</p>
<p>Since there are quite a few movies on our watch list, quite a few library branches we can walk to, and some time flexibility as to when to go, checking all those combinations is tedious. I wrote a script that takes a list of branches and a list of movie URLs, checks the status of each, and displays a table sorted by availability and location. My code gives me a list like this:</p>
<table border="2" frame="hsides" rules="groups" cellspacing="0" cellpadding="6">
<colgroup>
<col class="org-left">
<col class="org-left">
<col class="org-left">
<col class="org-left"> </colgroup>
<tbody>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Annette Street</td>
<td class="org-left">Mad Max Fury Road</td>
<td class="org-left">M 10-8:30 T 12:30-8:30 W 10-6 Th 12:30-8:30 F 10-6 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Bloor/Gladstone</td>
<td class="org-left">Inside Out</td>
<td class="org-left">M 9-8:30 T 9-8:30 W 9-8:30 Th 9-8:30 F 9-5 Sat 9-5 Sun 1:30-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Bloor/Gladstone</td>
<td class="org-left">Match</td>
<td class="org-left">M 9-8:30 T 9-8:30 W 9-8:30 Th 9-8:30 F 9-5 Sat 9-5 Sun 1:30-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Jane/Dundas</td>
<td class="org-left">Avengers: Age of Ultron</td>
<td class="org-left">M 9-8:30 T 9-8:30 W 9-8:30 Th 9-8:30 F 9-5 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Jane/Dundas</td>
<td class="org-left">Ant-Man</td>
<td class="org-left">M 9-8:30 T 9-8:30 W 9-8:30 Th 9-8:30 F 9-5 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Jane/Dundas</td>
<td class="org-left">Mad Max Fury Road</td>
<td class="org-left">M 9-8:30 T 9-8:30 W 9-8:30 Th 9-8:30 F 9-5 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Jane/Dundas</td>
<td class="org-left">Minions</td>
<td class="org-left">M 9-8:30 T 9-8:30 W 9-8:30 Th 9-8:30 F 9-5 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Perth/Dupont</td>
<td class="org-left">Chappie</td>
<td class="org-left">T 12:30-8:30 W 10-6 Th 12:30-8:30 F 10-6 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Runnymede</td>
<td class="org-left">Ant-Man</td>
<td class="org-left">M 9-8:30 T 9-8:30 W 9-8:30 Th 9-8:30 F 9-5 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Runnymede</td>
<td class="org-left">Minions</td>
<td class="org-left">M 9-8:30 T 9-8:30 W 9-8:30 Th 9-8:30 F 9-5 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">St. Clair/Silverthorn</td>
<td class="org-left">Kingsman: the Secret Service</td>
<td class="org-left">T 12:30-8:30 W 10-6 Th 12:30-8:30 F 10-6 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">St. Clair/Silverthorn</td>
<td class="org-left">Mad Max Fury Road</td>
<td class="org-left">T 12:30-8:30 W 10-6 Th 12:30-8:30 F 10-6 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Swansea Memorial</td>
<td class="org-left">Ant-Man</td>
<td class="org-left">T 10-6 W 1-8 Th 10-6 Sat 10-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Swansea Memorial</td>
<td class="org-left">Chappie</td>
<td class="org-left">T 10-6 W 1-8 Th 10-6 Sat 10-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Swansea Memorial</td>
<td class="org-left">Kingsman: the Secret Service</td>
<td class="org-left">T 10-6 W 1-8 Th 10-6 Sat 10-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Swansea Memorial</td>
<td class="org-left">Kingsman: the Secret Service</td>
<td class="org-left">T 10-6 W 1-8 Th 10-6 Sat 10-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Swansea Memorial</td>
<td class="org-left">Mad Max Fury Road</td>
<td class="org-left">T 10-6 W 1-8 Th 10-6 Sat 10-5</td>
</tr>
<tr>
<td class="org-left">In Library</td>
<td class="org-left">Swansea Memorial</td>
<td class="org-left">Minions</td>
<td class="org-left">T 10-6 W 1-8 Th 10-6 Sat 10-5</td>
</tr>
<tr>
<td class="org-left">2015-12-08</td>
<td class="org-left">Perth/Dupont</td>
<td class="org-left">Terminator Genisys</td>
<td class="org-left">T 12:30-8:30 W 10-6 Th 12:30-8:30 F 10-6 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">2015-12-08</td>
<td class="org-left">Perth/Dupont</td>
<td class="org-left">Mad Max Fury Road</td>
<td class="org-left">T 12:30-8:30 W 10-6 Th 12:30-8:30 F 10-6 Sat 9-5</td>
</tr>
<tr>
<td class="org-left">2015-12-09</td>
<td class="org-left">Swansea Memorial</td>
<td class="org-left">Avengers: Age of Ultron</td>
<td class="org-left">T 10-6 W 1-8 Th 10-6 Sat 10-5</td>
</tr>
</tbody>
</table>
<p>… many more rows omitted. =)</p>
<p>With this data, I can decide that Swansea Memorial has a bunch of things I might want to check out, and pick that as the destination for my walk. Sure, there&#8217;s a chance that someone else might check out the movies before I get there (although I can minimize that by getting to the library as soon as it opens), or that the video has been misfiled or misplaced, but overall, the system tends to work fine.</p>
<p>It&#8217;s easy for me to send the output to myself by email, too. I just select the part of the table I care about and use Emacs&#8217; <code>M-x shell-command-on-region</code> (<code>M-|</code>) to mail it to myself with the command <code>mail -s "Videos to check out" sacha@sachachua.com</code>.</p>
<p>The first time I ran my script, I ended up going to Perth/Dupont to pick up seven movies in addition to the two I picked up from Annette Library. Many of the movies had been returned but not yet shelved, so the librarian retrieved them from his bin and gave them to me. When I got back, W- looked at the stack of DVDs by the television and said, &#8220;You know that&#8217;s around 18 hours of viewing, right?&#8221; It&#8217;ll be fine for background watching. =)</p>
<p>Little things like this make me glad that I can write scripts and other tiny tools to make my life better. Anything that involves multiple steps or combining information from multiple sources might be simpler with a script. I wrote this script as a command-line tool with NodeJS, since I&#8217;m comfortable with the HTML request and parsing libraries available there.</p>
<p>Anyway, here&#8217;s the code, in case you want to build on the idea. Have fun!</p>
<div class="org-src-container">
<pre class="src src-js2">/* Shows you which videos are available at which libraries.

   Input: A json filename, which should be a hash of the form:
   {"branches": {"Branch name": "Additional branch details (ex: hours)", ...},
   "videos": [{"Title": "URL to library page"}, ...]}.

   Example: {
   "branches": {
   "Runnymede": "M 9-8:30 T 9-8:30 W 9-8:30 Th 9-8:30 F 9-5 Sat 9-5"
   },
   "videos": [
   {"title": "Avengers: Age of Ultron", "url": "http://www.torontopubliclibrary.ca/detail.jsp?Entt=RDM3350205&amp;R=3350205"}
   ]}

   Output:
   Status,Branch,Title,Branch notes
*/

var rp = require('request-promise');
var moment = require('moment');
var async = require('async');
var cheerio = require('cheerio');
var q = require('q');
var csv = require('fast-csv');
var fs = require('fs');

if (process.argv.length &lt; 3) {
  console.log('Please specify the JSON file to read the branches and videos from.');
  process.exit(1);
}

var config = JSON.parse(fs.readFileSync(process.argv[2]));
var branches = config.branches;
var videos = config.videos;

/*
  Returns a promise that will resolve with an array of [status,
  branch, movie, info], where status is either the next due date, "In
  Library", etc. */
function checkStatus(branches, movie) {
  var url = movie.url;
  var matches = url.match(/R=([0-9]+)/);
  return rp.get(
    'http://www.torontopubliclibrary.ca/components/elem_bib-branch-holdings.jspf?print=&amp;numberCopies=1&amp;itemId='
      + matches[1]).then(function(a) {
        var $ = cheerio.load(a);
        var results = [];
        var lastBranch = '';              
        $('tr.notranslate').each(function() {
          var row = $(this);
          var cells = row.find('td');
          var branch = $(cells[0]).text().replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, '');
          var due = $(cells[2]).text().replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, '');
          var status = $(cells[3]).text().replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, '');
          if (branch) { lastBranch = branch; }
          else { branch = lastBranch; }
          if (branches[branch]) {
            if (status == 'On loan' &amp;&amp; (matches = due.match(/Due: (.*)/))) {
              status = moment(matches[1], 'DD/MM/YYYY').format('YYYY-MM-DD');
            }
            if (status != 'Not Available - Search in Progress') {
              results.push([status, branch, movie.title, branches[branch]]);
            }
          }
        });
        return results;
      });
}

function checkAllVideos(branches, videos) {
  var results = [];
  var p = q.defer();
  async.eachLimit(videos, 5, function(video, callback) {
    checkStatus(branches, video).then(function(result) {
      results = results.concat(result);
      callback();
    });
  }, function(err) {
    p.resolve(results.sort(function(a, b) {
      if (a[0] == 'In Library') {
        if (b[0] == 'In Library') {
          if (a[1] &lt; b[1]) return -1;
          if (a[1] &gt; b[1]) return 1;
          if (a[2] &lt; b[2]) return -1;
          if (a[2] &gt; b[2]) return 1;
          return 0;
        } else {
          return -1;
        }
      }
      if (b[0] == 'In Library') { return 1; }
      if (a[0] &lt; b[0]) { return -1; }
      if (a[0] &gt; b[0]) { return 1; }
      return 0;
    }));
  });
  return p.promise;
}

checkAllVideos(branches, videos).then(function(result) {
  csv.writeToString(result, {}, function(err, data) {
    console.log(data);
  });
});
</pre>
</div>
<p>P.S. Okay, I&#8217;m <em>really</em> tempted to walk over to Swansea Memorial, but W- reminds me that we&#8217;ve got a lot of movies already waiting to be watched. So I&#8217;ll probably just walk to the supermarket, but I&#8217;m looking forward to running this script once we get through our backlog of videos!</p>

<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2015%2F12%2Fscripting-toronto-public-librarys-movie-collection%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Developing Emacs micro-habits: Abbreviations and templates</title>
		<link>https://sachachua.com/blog/2015/01/developing-emacs-micro-habits-text-automation/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Wed, 21 Jan 2015 13:00:00 GMT</pubDate>
    <category>emacs</category>
		<guid isPermaLink="false">https://sachachua.com/blog/?p=27772</guid>
		<description><![CDATA[<p>When it comes to improving how you use Emacs, picking one small change and paying close attention seems to work well.&#xA0;Little things make a lot of difference, especially when frequently repeated over a long period of time. It reminded me of this quote I came across on <a href="http://irreal.org/blog/?p=3529">Irreal</a>:</p>
<blockquote class="twitter-tweet" lang="en"><p><a href="https://twitter.com/hashtag/Emacs?src=hash">#Emacs</a>: using transpose-chars is a litmus test. If you care enough to save keystrokes to internalize C-t, then you must be a power user.</p>
<p>&#x2014; Fredrik Appelberg (@appelberg) <a href="https://twitter.com/appelberg/status/538282100911529984">November 28, 2014</a></p></blockquote>
<p><script src="https://platform.twitter.com/widgets.js" async="" charset="utf-8"></script></p>
<p>I&apos;ve gotten the hang of basic <code>multiple-cursors-mode</code> and I&apos;m making gradual progress towards internalizing <code>smart-parens</code> by the simple approach of focusing on one tiny habit at a time. For example, I spent a week reminding myself to use <code>mc/mark-all-like-this-dwim</code> or <code>mc/mark-lines</code> instead of using keyboard macros.</p>
<p>Inspired by the <a href="http://qiita.com/advent-calendar/2014/emacs">Emacs Advent Calendar</a>, I wanted to start a 52-week series on micro-habits for more effective Emacs use.&#xA0;I brain-dumped an outline of four sets (basic Emacs, Org, programming, meta-habits) of thirteen small tips each. Looking at my list, I realized there were many ideas there that I hadn&apos;t quite gotten the hang of myself. I figured that this might be more of a project for 2016; in the meantime, I could learn by doing.</p>
<p>The first micro-habit I wanted to dig into was that of <b>automating text:</b> abbreviations, templates, and other ways to expand or transform text. I&apos;d used Yasnippet before. I sometimes accidentally expanded keywords instead of indenting lines if my cursor happened to be at the wrong spot. But I hadn&apos;t yet drilled the instinct of automation or the familiarity with templates into my fingers.</p>
<p>This blog post isn&apos;t the easy-to-understand guide to automating text. I&apos;ll write that later, when I&apos;ve figured more things out. In the meantime, I&apos;ll share what I&apos;ve been learning and thinking so far, and maybe you can help me make sense of it.</p>
<p>Emacs has a separate manual for <a href="http://www.gnu.org/software/emacs/manual/html_mono/autotype.html">autotyping</a>, which I had never read before. The short manual covers abbrev, skeleton, auto-insert, copyright messages, timestamps, and tempo. Did you know that <code>define-skeleton</code> lets you create a template that accepts multiple interregions if you call your skeleton function with a negative argument? (Interregions? What are those?) It took me an embarrassing amount of time to figure out how to mark interregions and use them. It turns out they have to be contiguous. It might be easier to think of marking the beginning of the region, marking some points in the middle, and then calling the command when your point is at the end &#8211; which is probably how most people would interpret the diagrams, but I was trying to mark discontinuous regions because that would be super-cool, and that totally didn&apos;t work. And then I forgot that using <code>helm-M-x</code> means you need to specify numeric arguments <i>after</i> typing <code>M-x</code> instead of before. (I wrote about that very point in one of my blog posts, but it slipped my mind.) Once I got past that, I was delighted to find that it worked as advertised. I still haven&apos;t imagined a situation where I would use it, but it seems like a good sort of thing to know.</p>
<p>What <i>are</i> the practical situations where text automation can help people work more effectively? I looked around to see how other people were using it. <a href="http://cupfullofcode.com/blog/2013/02/26/snippet-expansion-with-yasnippet/index.html">Coding</a>, of course &#8211; especially if you use Emacs Lisp to <a href="http://gregorygrubbs.com/wordpress/snippets-with-emacs-lisp-power/">transform the text</a>. <a href="http://rdwr.org/jekyll/emacs/yasnippet/2014/05/18/using-yasnippets-to-template-new-blog-postings/">Debugging, too</a>. <a href="http://www.mostlymaths.net/2009/09/quick-latex-ing-with-emacs.html">Marking up text</a>. <a href="http://tonyballantyne.com/tech/yasnippet-and-babel/">Remembering parameters</a>. <a href="http://walterhiggins.net/blog/xanadb-emacs-20031126">Wrapping regions.</a> <a href="http://blog.binchen.org/posts/how-to-use-yasnippets-to-produce-email-templates-in-emacs.html">Writing e-mails</a>. <a href="http://rdwr.org/jekyll/emacs/yasnippet/2014/05/18/using-yasnippets-to-template-new-blog-postings/">Adding blog post metadata</a>. <a href="http://kitchingroup.cheme.cmu.edu/blog/2014/03/08/Using-yasnippet-to-get-completion-in-ref-links/">Citing references</a>. <a href="http://lifehacker.com/5611210/how-to-use-text-expansion-to-save-yourself-hours-of-typing-every-day">Lifehacker has a long list, too.</a></p>
<p>I came up with several categories I&apos;m going to focus on so that I can more easily recognize opportunities to work better:</p>
<p><a href="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-05-Seeing-opportunities-for-abbreviations-and-text-automation-index-card.png"><img loading="lazy" class="alignnone size-medium wp-image-27773" src="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-05-Seeing-opportunities-for-abbreviations-and-text-automation-index-card-640x383.png" alt="2015-01-05 Seeing opportunities for abbreviations and text automation &#45;&#45; index card" width="640" height="383" srcset="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-05-Seeing-opportunities-for-abbreviations-and-text-automation-index-card-640x383.png 640w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-05-Seeing-opportunities-for-abbreviations-and-text-automation-index-card-280x167.png 280w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-05-Seeing-opportunities-for-abbreviations-and-text-automation-index-card.png 1502w" sizes="(max-width: 640px) 100vw, 640px"></a></p>
<p><a href="https://sketches.sachachua.com/id/2015-01-05">2015.01.05 Seeing opportunities for abbreviations and text automation &#x2013; index card</a></p>
<ul class="org-ul">
<li><b>Abbreviations</b> are about typing long words with fewer keystrokes. For example, you might shorten &quot;description&quot; to <code>desc</code>.</li>
<li><b>Phrases</b> are like word abbrevations, but longer. You might want to be able to expand <code>btw</code> to &quot;by the way.&quot;</li>
<li><b>Code</b> benefits from expansion in multiple ways:
<ul class="org-ul">
<li>Automatically inserting characters that are harder to reach on a keyboard, like <code>{</code> and <code>}</code></li>
<li>Being consistent about coding style, like the way many people like adding a comment after the closing brace of an <code>if</code></li>
<li>Transforming text that shows up in multiple places, such as variable names that need getters and setters</li>
<li>Filling in the blanks: parameters, comments, etc.</li>
<li>Reducing the cognitive load of switching between languages by establishingq a common vocabulary. For example, I sometimes need to look up the syntax of <code>for</code> or the proper way to display a debugging statement when I switch to a language I haven&apos;t used in a while</li>
</ul>
</li>
<li><b>Templates</b> are also useful for consistency in writing, planning, and other areas</li>
<li><b>Text transformation</b> can save time and minimize error.</li>
</ul>
<p><img loading="lazy" class="alignnone size-medium wp-image-27777" src="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-04-Automating-text-index-card-640x383.png" alt="2015-01-04 Automating text - index card" width="640" height="383" srcset="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-04-Automating-text-index-card-640x383.png 640w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-04-Automating-text-index-card-280x168.png 280w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-04-Automating-text-index-card.png 1502w" sizes="(max-width: 640px) 100vw, 640px"></p>
<p><a href="https://sketches.sachachua.com/id/2015-01-04">2015.01.04 Automating text &#x2013; index card</a></p>
<p>Translating the examples I&apos;d seen to my personal interests, I could probably find plenty of opportunities to automate text while coding, debugging, writing, planning, or publishing. To dig deeper, I looked at each of the categories in detail.</p>
<div id="outline-container-unnumbered-1" class="outline-3">
<h3 id="unnumbered-1">Abbreviations</h3>
<div id="text-unnumbered-1" class="outline-text-3">
<p><a href="https://sketches.sachachua.com/id/2015-01-06"><img loading="lazy" class="alignnone size-medium wp-image-27778" src="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-06-Abbreviations-index-card-640x380.png" alt="2015-01-06 Abbreviations &#45;&#45; index card" width="640" height="380" srcset="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-06-Abbreviations-index-card-640x380.png 640w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-06-Abbreviations-index-card-280x166.png 280w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-06-Abbreviations-index-card.png 1504w" sizes="(max-width: 640px) 100vw, 640px"></a></p>
<p><a href="https://sketches.sachachua.com/id/2015-01-06">2015.01.06 Abbreviations &#x2013; index card</a></p>
<p>When I was curious about typing faster, I read forum posts from people who had increased their speed by developing their own form of digital shorthand. The trick works on paper, too. When I need to write quickly or in limited space, I use abbreviations like <code>bc</code> for &quot;because&quot; and <code>w/o</code> for &quot;without.&quot; Why not on the computer as well?</p>
<p>I often take advantage of dynamic abbreviations when I know I&apos;ve recently typed the word I want. To trigger those, I just have to type the beginning of the word and then use <code>dabbrev-expand</code>. I haven&apos;t set up my own static abbreviations, though. Main obstacles:</p>
<ul class="org-ul">
<li>I want to write shorter words instead of longer ones</li>
<li>In the beginning, it&apos;s faster to type the word instead of thinking of the abbreviation and expanding it</li>
<li>If I have to undo or backspace, that makes me slower</li>
<li>If I burn this into my muscle memory, I might be more frustrated on other computers or in other apps (then again, I already customize Emacs extensively, so I guess I&apos;m okay with the tradeoff)</li>
</ul>
<p>Anyway, here&apos;s a short list I&apos;m trying out with <code>define-global-abbrev</code> and <code>hippie-expand</code>:</p>
<table border="2" frame="hsides" rules="groups" cellspacing="0" cellpadding="6">
<colgroup>
<col class="left">
<col class="left"> </colgroup>
<tbody>
<tr>
<td class="left">hw</td>
<td class="left">however</td>
</tr>
<tr>
<td class="left">bc</td>
<td class="left">because</td>
</tr>
<tr>
<td class="left">wo</td>
<td class="left">without</td>
</tr>
<tr>
<td class="left">prob</td>
<td class="left">probably</td>
</tr>
<tr>
<td class="left">st</td>
<td class="left">sometimes</td>
</tr>
</tbody>
</table>
<p>Hmm. Let&apos;s say that it takes me two keystrokes to trigger the expansion, whether it&apos;s the <code>xx</code> keychord I&apos;ve just set up or the <code>M-/</code> I&apos;ve replaced with <code>hippie-expand</code>. (Hmm, maybe a double-space keychord is a good candidate for expansion too.) Is the retraining worth a ~50% possible reduction in keystrokes? Probably not.</p>
<p>How about text with punctuation, so I can minimize reaching for symbols?</p>
<table border="2" frame="hsides" rules="groups" cellspacing="0" cellpadding="6">
<colgroup>
<col class="left">
<col class="left"> </colgroup>
<tbody>
<tr>
<td class="left">blog</td>
<td class="left"><a href="https://sachachua.com/blog/">https://sachachua.com/blog/</a></td>
</tr>
<tr>
<td class="left">mail</td>
<td class="left">sacha@sachachua.com</td>
</tr>
</tbody>
</table>
<p>Maybe it&apos;s better to look at the words I frequently misspell, or that I tend to slow down then typing. I&apos;ll keep an eye out for those.</p>
</div>
</div>
<div id="outline-container-unnumbered-2" class="outline-3">
<h3 id="unnumbered-2">Phrases</h3>
<div id="text-unnumbered-2" class="outline-text-3">
<p><a href="https://sketches.sachachua.com/id/2015-01-06"><img loading="lazy" class="alignnone size-medium wp-image-27779" src="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-06-Phrases-index-card-640x379.png" alt="2015-01-06 Phrases &#45;&#45; index card" width="640" height="379" srcset="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-06-Phrases-index-card-640x379.png 640w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-06-Phrases-index-card-280x166.png 280w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-06-Phrases-index-card.png 1507w" sizes="(max-width: 640px) 100vw, 640px"></a></p>
<p><a href="https://sketches.sachachua.com/id/2015-01-06">2015.01.06 Phrases &#x2013; index card</a></p>
<p>Phrases are an easier sell. Still, I&apos;m trying not to settle into the rut of set phrases. I should cut those mercilessly or avoid writing them from the beginning.</p>
<table border="2" frame="hsides" rules="groups" cellspacing="0" cellpadding="6">
<colgroup>
<col class="left">
<col class="left"> </colgroup>
<tbody>
<tr>
<td class="left">co</td>
<td class="left">check out</td>
</tr>
<tr>
<td class="left">iti</td>
<td class="left">I think I</td>
</tr>
<tr>
<td class="left">otoh</td>
<td class="left">on the other hand,</td>
</tr>
<tr>
<td class="left">mean</td>
<td class="left">in the meantime,</td>
</tr>
<tr>
<td class="left">fe</td>
<td class="left">for example</td>
</tr>
<tr>
<td class="left">fi</td>
<td class="left">for instance,</td>
</tr>
<tr>
<td class="left">oc</td>
<td class="left">of course</td>
</tr>
<tr>
<td class="left">ip</td>
<td class="left">in particular</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="outline-container-unnumbered-3" class="outline-3">
<h3 id="unnumbered-3">Code insertion</h3>
<div id="text-unnumbered-3" class="outline-text-3">
<p>This is, fortunately, well-trodden ground. The yasnippet package comes with a large collection of snippets for many programming languages. You can start by familiarizing yourself with the pre-defined snippets for the modes that you use. For example, in my installation, they&apos;re under <code>~/.emacs.d/elpa/yasnippet-20141117.327/snippets</code>. You can use the filename (or keywords defined with <code>key</code>, if specified) as the abbreviation, and you can expand them with <code>yas-expand</code> (which should be bound to <code>TAB</code> if you have <code>yas-global-mode</code> on).</p>
<p>I mostly work with HTML, CSS, Javascript, Ruby on Rails, and Emacs Lisp, so this is the cheat sheet I&apos;ve made for myself:</p>
<p><img loading="lazy" class="alignnone size-medium wp-image-27776" src="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Code-insertion-index-card-640x384.png" alt="2015-01-07 Code insertion &#45;&#45; index card" width="640" height="384" srcset="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Code-insertion-index-card-640x384.png 640w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Code-insertion-index-card-280x168.png 280w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Code-insertion-index-card.png 1487w" sizes="(max-width: 640px) 100vw, 640px"></p>
<p><a href="https://sketches.sachachua.com/id/2015-01-07">2015.01.07 Code insertion &#x2013; index card</a></p>
<p>For HTML, I need to remember that the tags are generally expandable, and that there are a few Lorem Ipsum abbreviations triggered by <code>lorem.1</code> through <code>.5</code>. CSS has a <code>v</code> abbreviation that sets up a bunch of rules with vendor prefixes. For Javascript, I&apos;ll probably start with <code>f</code> to define a function and <code>log</code> to output something to <code>console.log</code>. Rails has a bunch of iterators like <code>eai</code> that look interesting. As for Emacs Lisp, the pre-defined templates generally add parentheses around common functions so you don&apos;t have to type them, and there are a few shortcuts like <code>bs</code> for <code>buffer-string</code> and <code>cc</code> for <code>condition-case</code>. I think I&apos;ll modify the default snippets to make better use of Yasnippet&apos;s field support, though, so that I don&apos;t have to delete and replace text.</p>
</div>
</div>
<div id="outline-container-unnumbered-4" class="outline-3">
<h3 id="unnumbered-4">Templates</h3>
<div id="text-unnumbered-4" class="outline-text-3">
<p>In addition to using text expansion for code, you can use it for planning and writing other text. I saw <a href="https://sachachua.com/blog/2014/12/emacs-chat-karl-voit-2/">Karl Voit</a> use it to great effect in my Emacs Chat with him (around the 44:00 mark), and I&apos;ve been gradually refining some templates of my own.</p>
<p><img loading="lazy" class="alignnone size-medium wp-image-27774" src="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Templates-index-card-640x385.png" alt="2015-01-07 Templates &#45;&#45; index card" width="640" height="385" srcset="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Templates-index-card-640x385.png 640w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Templates-index-card-280x168.png 280w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Templates-index-card.png 1484w" sizes="(max-width: 640px) 100vw, 640px"></p>
<p><a href="https://sketches.sachachua.com/id/2015-01-07">2015.01.07 Templates &#x2013; index card</a></p>
<p>For example, here&apos;s the template I&apos;ve been using for sketched books. Note: If you use Yasnippet for Org Mode properties, you may want to set <code>yas-indent-line</code> to <code>fixed</code> or the fields will get confused.</p>
<p><a href="https://gist.github.com/7df841b1ba53a32541d2">Gist: sbook</a></p>
<div class="org-src-container">
<pre class="src src-org"># key: sbook
# name: Sketched Book
# &#45;&#45;

**** TOSKETCH ${1:short title}
      :PROPERTIES:
      :TITLE: ${2:long title}
      :SHORT_TITLE: $1
      :AUTHOR: ${3:authors}
      :YEAR: ${4:year}
      :BUY_LINK: ${5:Amazon link}
      :BASENAME: ${6:`(org-read-date nil nil &quot;.&quot;)`} Sketched Book - ${2:$(sacha/convert-sketch-title-to-filename yas-text)} - ${3:$(sacha/convert-sketch-title-to-filename yas-text)}
      :ISBN: ${7:ISBN}
      :BLOG_POST: https://sachachua.com/blog
      :END:

$0

***** TODO Sketchnote $1
:PROPERTIES:
:Effort: 2:00
:QUANTIFIED: Drawing
:END:

[[elisp:sacha/prepare-sketchnote-file][Prepare the file]]

***** TODO Write personal reflection for $1
:PROPERTIES:
:Effort: 1:00
:QUANTIFIED: Writing
:END:

[[https://sachachua.com/blog/wp-admin/edit.php?page=cal][View in calendar]]

****** Sketched Book - $2 - $3

$3&apos;s /$2/ ($4) ...

I&#xE2;&#x20AC;&#x2122;ve sketched the key points of the book below to make it easier to remember and share. Click on the image for a larger version that you can print if you want.

Haven&apos;t read the book yet? You can [[$5][buy it from Amazon]] (affiliate link) or get it from your favourite book sources.

Like this sketch? Check out [[http://sketchedbooks.com][sketchedbooks.com]] for more. Feel free to share &#xE2;&#x20AC;&#x201C; it&#xE2;&#x20AC;&#x2122;s under the Creative Commons Attribution License, like the rest of my blog.

***** TODO Post $1 to blog
:PROPERTIES:
:Effort: 1:00
:QUANTIFIED: Packaging
:END:


***** TODO Update sketched books collection
:PROPERTIES:
:Effort: 1:00
:QUANTIFIED: Packaging
:END:

1. [[elisp:sacha/index-sketched-book][Index sketched book]]
   - [[file:~/Dropbox/Packaging/sketched-books/index.org][Edit index]]
   - [[file:~/Dropbox/Packaging/sketched-books/ebook.org][Edit ebook]]
2. [[elisp:sacha/package-sketched-book][Compile]]
3. Update [[https://gumroad.com/products/pBtS/edit]]

***** TODO Tweet sneak peek of $1 with attached picture

[[elisp:(progn (kill-new (format &quot;Sneak peek: Sketched Book: %s - %s %s&quot; (org-entry-get-with-inheritance &quot;SHORT_TITLE&quot;) (org-entry-get-with-inheritance &quot;AUTHOR&quot;) (org-entry-get-with-inheritance &quot;BLOG_POST&quot;))) (browse-url &quot;http://twitter.com&quot;))][Copy text and launch Twitter]]</pre>
</div>
</div>
<p>It&apos;s a lot of code and I keep tweaking it as I come across rough corners, but it&apos;s handy to have that all captured in a template that I can easily expand. =)</p>
</div>
<div id="outline-container-unnumbered-5" class="outline-3">
<h3 id="unnumbered-5">Text transformation</h3>
<div id="text-unnumbered-5" class="outline-text-3">
<p>One of the advantages of tweaking text expansion inside Emacs instead of using a general-purpose text expansion program is that you can mix in some Emacs Lisp to transform the text along the way. I&apos;m still thinking about how to make the most of this, as you can see from this half-filled note-card:</p>
<p><img loading="lazy" class="alignnone size-medium wp-image-27775" src="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Text-transformation-as-part-of-expansion-index-card-640x383.png" alt="2015-01-07 Text transformation as part of expansion &#45;&#45; index card" width="640" height="383" srcset="https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Text-transformation-as-part-of-expansion-index-card-640x383.png 640w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Text-transformation-as-part-of-expansion-index-card-280x168.png 280w, https://sachachua.com/blog/wp-content/uploads/2015/01/2015-01-07-Text-transformation-as-part-of-expansion-index-card.png 1486w" sizes="(max-width: 640px) 100vw, 640px"></p>
<p><a href="https://sketches.sachachua.com/id/2015-01-07">2015.01.07 Text transformation as part of expansion &#x2013; index card</a></p>
<p>For example, this snippet makes it easier to share source code on my blog while also linking to a Gist copy of the code, in case I revise it or people want to comment on the code snippet itself. It doesn&apos;t use any of the built-in text expansion capabilities, but I think of it as a text expander and transformer because it replaces work I used to do manually. You&apos;ll need the <code>gist</code> package for this one.</p>
<p><a href="https://gist.github.com/e4e6efdb737fb6526ff2">Gist: Sacha</a> (updated to clean up region code)</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp"><span class="org-rainbow-delimiters-depth-1">(</span><span class="org-keyword">defun</span> <span class="org-function-name">sacha/copy-code-as-org-block-and-gist</span> <span class="org-rainbow-delimiters-depth-2">(</span>beg end<span class="org-rainbow-delimiters-depth-2">)</span>
  <span class="org-rainbow-delimiters-depth-2">(</span>interactive <span class="org-string">&quot;r&quot;</span><span class="org-rainbow-delimiters-depth-2">)</span>
  <span class="org-rainbow-delimiters-depth-2">(</span><span class="org-keyword">let</span> <span class="org-rainbow-delimiters-depth-3">(</span><span class="org-rainbow-delimiters-depth-4">(</span>filename <span class="org-rainbow-delimiters-depth-5">(</span>file-name-base<span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span>
        <span class="org-rainbow-delimiters-depth-4">(</span>mode <span class="org-rainbow-delimiters-depth-5">(</span>symbol-name major-mode<span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span>
        <span class="org-rainbow-delimiters-depth-4">(</span>contents
         <span class="org-rainbow-delimiters-depth-5">(</span><span class="org-keyword">if</span> <span class="org-rainbow-delimiters-depth-6">(</span>use-region-p<span class="org-rainbow-delimiters-depth-6">)</span> <span class="org-rainbow-delimiters-depth-6">(</span>buffer-substring beg end<span class="org-rainbow-delimiters-depth-6">)</span> <span class="org-rainbow-delimiters-depth-6">(</span>buffer-string<span class="org-rainbow-delimiters-depth-6">)</span><span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span>
        <span class="org-rainbow-delimiters-depth-4">(</span>gist <span class="org-rainbow-delimiters-depth-5">(</span><span class="org-keyword">if</span> <span class="org-rainbow-delimiters-depth-6">(</span>use-region-p<span class="org-rainbow-delimiters-depth-6">)</span> <span class="org-rainbow-delimiters-depth-6">(</span>gist-region beg end<span class="org-rainbow-delimiters-depth-6">)</span> <span class="org-rainbow-delimiters-depth-6">(</span>gist-buffer<span class="org-rainbow-delimiters-depth-6">)</span><span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span><span class="org-rainbow-delimiters-depth-3">)</span>
    <span class="org-rainbow-delimiters-depth-3">(</span>kill-new
     <span class="org-rainbow-delimiters-depth-4">(</span>format <span class="org-string">&quot;\n[[%s][Gist: %s]]\n#+begin_src %s\n%s\n#+end_src\n&quot;</span>
             <span class="org-rainbow-delimiters-depth-5">(</span>oref <span class="org-rainbow-delimiters-depth-6">(</span>oref gist <span class="org-builtin">:data</span><span class="org-rainbow-delimiters-depth-6">)</span> <span class="org-builtin">:html-url</span><span class="org-rainbow-delimiters-depth-5">)</span> filename
             <span class="org-rainbow-delimiters-depth-5">(</span>replace-regexp-in-string <span class="org-string">&quot;-mode$&quot;</span> <span class="org-string">&quot;&quot;</span> mode<span class="org-rainbow-delimiters-depth-5">)</span>
             contents<span class="org-rainbow-delimiters-depth-4">)</span><span class="org-rainbow-delimiters-depth-3">)</span><span class="org-rainbow-delimiters-depth-2">)</span><span class="org-rainbow-delimiters-depth-1">)</span>
</pre>
</div>
<p>Both Yasnippet and Skeleton allow you to use Lisp expressions in your template. If you don&apos;t have all the data yet, you might consider writing another Lisp function that you can call later when you do. For example, in the sketched books code above, I have an Emacs Lisp link that composes a tweet with a link, puts it in the clipboard, and then opens up a web browser. (I do this instead of posting directly because I also want to attach an image to that tweet, and I haven&apos;t figured out how to modify any of the Emacs Twitter clients to do that.)</p>
<p>So that&apos;s what I&apos;ve learned so far about automating text in Emacs. It&apos;ll take me more than a week to get the hang of the abbreviations I&apos;ve just set up, and I&apos;ll probably need to add even more before adding and using abbreviations become true habits. But hey, maybe this will help you pay closer attention to repetitive text and editing actions in Emacs so that you can automate them too, and we can swap notes on useful abbreviations. What kind of text do you expand?</p>
<p>For more information, see:</p>
<ul class="org-ul">
<li><a href="http://www.emacswiki.org/emacs/CategoryTemplates">EmacsWiki:CategoryTemplates</a></li>
<li><a href="http://emacsrocks.com/e06.html">Emacs Rocks: Yasnippet</a></li>
<li><a href="https://sachachua.com/blog/2015/01/thinking-make-better-use-yasnippet-emacs-workflow/?shareadraft=baba27681_54adb5a67d3ed">Thinking about how to make better use of Yasnippet in my Emacs workflow</a></li>
</ul>
</div>
</div>
<p></p>

<p>You can <a href="https://sachachua.com/blog/2015/01/developing-emacs-micro-habits-text-automation/#comment">view 10 comments</a> or <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2015%2F01%2Fdeveloping-emacs-micro-habits-text-automation%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Automating bulk web stuff with iMacros</title>
		<link>https://sachachua.com/blog/2013/12/automating-bulk-web-stuff-with-imacros/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Tue, 17 Dec 2013 01:19:00 GMT</pubDate>
    <category>geek</category>
		<guid isPermaLink="false">https://sachachua.com/blog/?p=26537</guid>
		<description><![CDATA[<p>I found myself needing to download a whole bunch of JSON data from a server that had a weird authentication thing that Chrome could deal with but wget/curl/Ruby couldn’t. Since my Firefox was on the fritz, I couldn’t use Selenium IDE or the Selenium Webdriver. iMacros to the rescue! (<a href="https://chrome.google.com/webstore/detail/imacros-for-chrome/cplklnmnlbnpmjogncfgfijoopmnlemp?hl=en">Chrome</a>, <a href="https://addons.mozilla.org/en-US/firefox/addon/imacros-for-firefox/">Firefox</a>)</p>
<p><a href="https://sachachua.com/blog/wp-content/uploads/2013/11/2013-11-21-Geek-notes-automating-bulk-web-stuff-with-iMacros.png"><img loading="lazy" title="2013-11-21 Geek notes - automating bulk web stuff with iMacros" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="2013-11-21 Geek notes - automating bulk web stuff with iMacros" src="https://sachachua.com/blog/wp-content/uploads/2013/11/2013-11-21-Geek-notes-automating-bulk-web-stuff-with-iMacros_thumb.png" width="640" height="487"></a></p>
<p>That plus lots of keyboard macros and text manipulation in Emacs, plus a little parsing and regexp substitution in Ruby, plus more Emacs munging got me the data I wanted. Hooray for bubblegum and string scripting!</p>

<p>You can <a href="https://sachachua.com/blog/2013/12/automating-bulk-web-stuff-with-imacros/#comment">view 2 comments</a> or <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2013%2F12%2Fautomating-bulk-web-stuff-with-imacros%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Back to the joys of coverage testing: Vagrant, Guard, Spork, RSpec, Simplecov</title>
		<link>https://sachachua.com/blog/2013/06/back-to-the-joys-of-coverage-testing-vagrant-guard-spork-rspec-simplecov/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Thu, 06 Jun 2013 12:00:00 GMT</pubDate>
    <category>rails</category>
		<guid isPermaLink="false">https://sachachua.com/blog/?p=24946</guid>
		<description><![CDATA[<p>Tests are important because programmers are human.  I <em>know</em> that I’m going to forget, make mistakes, and change things that I didn’t mean to change. Testing lets me improve my chances of at least noticing. Coverage tools show me how much of my code is covered by tests, improving my chances of seeing where my blind spots are.</p>
<p>In one of my last projects at IBM, I convinced my team to use coverage testing tools on a Rails development project. It was great watching the coverage numbers inch up, and we actually reached 100% (at least in terms of what rcov was looking at). I occasionally had to fix things when people broke the build, but sometimes people added and updated tests too. Although coverage testing has its weaknesses (are you testing for edge cases?), it’s better than nothing, and can catch some embarrassing bugs before they make it to the outside world.</p>
<p>Although I’m not currently taking on any web development work (I’m saving brainspace for other things), I have a few personal projects that I enjoy working on. For example, <a href="http://quantifiedawesome.com">QuantifiedAwesome.com</a> lets me track different measurements such as time. I had written some tests for it before, but since then, I’d been adding features and making changes without updating the tests. Like the way that unpaid credit card balances turn into financial debt (very bad habit, try to avoid this if possible), unwritten or out-of-date tests contribute to <strong>technical debt.</strong></p>
<p>It was a little daunting to sit down and slowly work my way through the knots of out-of-date tests that had accumulated from haphazard coding. Here’s how I made the task more manageable:</p>
<ul>
<li><strong>I set up a virtual Linux development environment using Vagrant and Puppet. </strong>Many tools are much easier to set up on Linux than they are on Windows, and using a virtual machine for development means that I can easily isolate changes from the rest of my system. <a href="http://vagrantup.com">Vagrant</a> allows you to specify the configuration of a VirtualBox machine and quickly set up a new instance of it. I copied the Puppet configuration from <a href="https://github.com/rails/rails-dev-box">rails-dev-box</a> and modified it to match my requirements. I also compiled and installed Emacs 24 from source, along with a few Ruby-related tools like haml-mode and rinari. (If you’re going to do a lot of development, you might as well do it with your favourite tools!) I copied the Vagrant private keys and imported them into Pageant (an ssh agent for Windows) for easy logins, and I forwarded the ports too.</li>
<li><strong>I used Guard and Spork to efficiently run subsets of my tests. </strong><a href="https://github.com/guard/guard">Guard</a> is a tool that re-runs specific tests when it detects that files have changed, while <a href="https://github.com/sporkrb/spork-rails">Spork</a> lets you run tests without reloading your entire Rails application. Instead of running <strong>rake spec </strong>(which runs the whole test suite) or <strong>rspec ./spec/path/to/file</strong> (and  having to copy and paste the failing test’s filename), I could let Guard take care of rerunning failed tests for me.</li>
<li><strong>I worked on getting the old tests to pass. </strong>I wanted to get those cleared up before writing new tests. Good thing too – found a few bugs and some old code along the way.</li>
<li><strong>I reviewed the recommendations for better tests. </strong><a href="http://betterspecs.org">Better Specs</a> has tips on using RSpec with Ruby.</li>
<li><strong>Then I wrote new tests to cover the rest of my models.</strong> I used <a href="https://github.com/colszowka/simplecov"><strong>Simplecov</strong></a><strong> </strong>for coverage testing. Since Vagrant uses shared folders, I could use Google Chrome to view the coverage webpages from my host computer. I still have some technical debt left – I need to write integration tests, and there’s more beyond that – but it’s satisfying to know that the models (data + logic) are covered. For some reason, my Guard+Spork combination doesn’t result in cumulative Simplecov scores, so I occasionally regenerate my coverage stats with <strong>rake spec. </strong>These tests will be useful as I write new features or move to Rails 4.</li>
</ul>
<p><a href="https://sachachua.com/blog/wp-content/uploads/2013/06/image.png"><img loading="lazy" style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border: 0px;" title="image" alt="image" src="https://sachachua.com/blog/wp-content/uploads/2013/06/image_thumb.png" width="640" height="215" border="0"></a></p>
<p>(… 21.5 hours of coding/development/infrastructure…)</p>
<p>New things I learned:</p>
<p>I use Mechanize to work with the Toronto Public Library’s webpage system so that I can retrieve the list of due books, renew items, or request holds. I wanted to test these as well. <strong><a href="https://github.com/chrisk/fakeweb">FakeWeb</a></strong> lets you intercept web requests and return your own responses, so I saved sample web pages to my spec/fixtures/files directory and used FakeWeb to return them. (ex: <a href="https://github.com/sachac/quantified/blob/master/spec/models/toronto_library_spec.rb">toronto_library_spec.rb</a>)</p>
<p>Paperclip also needed some tweaking. I replaced my Paperclip uploads with File assignments so that I could test the image processing. (ex: <a href="https://github.com/sachac/quantified/blob/master/spec/models/clothing_spec.rb">clothing_spec.rb</a>)</p>
<p>Always remember to wrap your RSpec tests in an <strong>it &#8220;…&#8221;</strong> instead of just <strong>context &#8220;when …&#8221; </strong>or <strong>describe &#8220;…&#8221;</strong>. I forgot to do this a few times and got confused about why stuff got left in my database and why the error messages weren’t formatted like they normally are.</p>
<p>Progress! Next step: Update my Cucumber tests and add some more integration tests…</p>
<p>I like this part of being a developer, even though writing new code is more fun. Testing is one of those time-consuming but powerful tools that can reduce frustration in the long run. I’ve experimented with <a href="https://sachachua.com/blog/2011/11/quantified-awesome-development-driven-behaviour/">applying automated testing techniques to everyday life</a> before. I wonder what it would be like to take that even further… Any thoughts?</p>

<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2013%2F06%2Fback-to-the-joys-of-coverage-testing-vagrant-guard-spork-rspec-simplecov%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Sketchnotes: The Very Versatile Drip&ndash;Mathew Sweezey (Pardot)</title>
		<link>https://sachachua.com/blog/2012/12/sketchnotes-the-very-versatile-dripmathew-sweezey-pardot/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Thu, 06 Dec 2012 20:24:34 GMT</pubDate>
    <category>sketchnotes</category>
		<guid isPermaLink="false">https://sachachua.com/blog/?p=24140</guid>
		<description><![CDATA[<p>UPDATE: Dec 13, 2012 Want to watch the webinar? <a href="http://www.pardot.com/webinars/recorded-webinar-versatile-drip">Here&#8217;s the video recording.</a></p>
<p>In this marketing webinar hosted by <a href="http://www.pardot.com/resources/webinars-and-videos">Pardot</a>, <strong>Mathew Sweezey </strong>shared tips on setting up a drip nurturing program for marketing and sales support. Click on the image to view a larger size, and feel free to share this with attribution!</p>
<p><a href="https://sachachua.com/blog/wp-content/uploads/2012/12/20121206-Pardot-The-Very-Versatile-Drip-Mathew-Sweezey.png"><img loading="lazy" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="20121206 Pardot - The Very Versatile Drip - Mathew Sweezey" alt="20121206 Pardot - The Very Versatile Drip - Mathew Sweezey" src="https://sachachua.com/blog/wp-content/uploads/2012/12/20121206-Pardot-The-Very-Versatile-Drip-Mathew-Sweezey_thumb.png" width="580" height="435" border="0"></a></p>
<p>Pardot has <a href="http://www.pardot.com/resources/webinars-and-videos">many other webinars and recordings</a>, so check them out if you’re curious about marketing automation.</p>
<p>Like this? <a href="https://sachachua.com/blog/sketchnotes">Browse through my other sketchnotes</a>, including my visual summary of <a href="https://sachachua.com/blog/2012/11/sketchnotes-the-5-key-elements-of-a-better-b2b-content-marketing-strategynolin-lechasseur/">The 5 Key Elements of a Better B2B Content Marketing Strategy</a> by Nolin LeChasseur. I sketchnote technology/business conferences and presentations – if that sounds interesting, <a href="https://sachachua.com/blog/contact">get in touch</a>!</p>
<p></p>
<p>Text:</p>
<p>THE VERY VERSATILE DRIP<br>
Mathew Sweezey<br>
Dec 6 2012<br>
Pardot</p>
<p>Email marketing<br>
Drip nurturing<br>
one-to-one conversations!</p>
<p>#1 LOOK AT MARKETING LIKE SALES LOOKS AT DEALS</p>
<p>Unidentified need -> Identified need not yet ready -> Starting to evaluate</p>
<p>competitive vs greenfield</p>
<p>Figure out your stages and buying cycles</p>
<p>#2 RELEVANT</p>
<p>context intelligence -> communication (must be relevant!)</p>
<p>Not just automation!<br>
Relevance is key.</p>
<p>#3 NOT E-MAIL MARKETING AS YOU KNOW IT<br>
HTML? marketer</p>
<p>One-to-one relationship<br>
inbox = battlefield<br>
Make it feel like a one-to-one email (rich text)</p>
<p>#4 HAVE A GOAL, THEN STICK TO IT!</p>
<p>(I feel bad not working with this guy.. He&#8217;s so ATTENTIVE)<br>
Lost a deal? Nurture the relationship!</p>
<p>#5 USE THAT COLD DATABASE</p>
<p>Value per lead x Size of database = $$$<br>
* maximize your database<br>
* Find leads that slipped through cracks</p>
<p>#6 GIVE SALES AN EXCUSE.</p>
<p>Don&#8217;t get overwhelmed!<br>
Sales drip = great ROI</p>
<p>Marketing &#8211; Sales<br>
Bridge the gap</p>
<p>expecially for prospects that are challenging</p>
<p>engaged -> excuse to call (Whew!)</p>
<p>PROBLEMS/GOALS</p>
<p>cold database<br>
automate lead nurturing<br>
event pre-/post-follow ups<br>
cold marketing/cold sales<br>
competitive<br>
lost deal</p>
<p>TYPES</p>
<p>3..2..1<br>
start with stage 3, then 2, then 1<br>
Good for cold databases, tradeshow lists..</p>
<p>Why reverse?<br>
Engage HOT prospects right away!</p>
<p>Event-specific<br>
straightforward</p>
<p>Stage-specific<br>
Give people &#8220;carrots&#8221; to encourage them to move stages</p>
<p>Straight<br>
simplest drip goal: engage, excuse to reach out</p>
<p>(Give me your best emails)</p>
<p>(Thought you might enjoy this..)<br>
..doesn&#8217;t have to be your stuff<br>
Nurture relationship</p>
<p>Q&#038;A:<br>
Q: Whitepaper vs video for scoring?<br>
A: Topic, sales readiness<br>
Q: Rich text vs images?<br>
A: Images often make people think &#8220;marketing!&#8221; Images okay if fake-forwarded.<br>
FWD:&#8230;<br>
Hey take a look at this<br>
Q: # of emails?<br>
A: Start small. 3 emails, ~12-18 days. Then more. Iterate.<br>
Test List. at least 1 day before. ALWAYS.<br>
Q: Length?<br>
A: 1 goal, 1-2 actions<br>
Q: Replace e-mail blasts?<br>
A: Nope.<br>
Q: Bcc?<br>
A: Not needed &#8211; CRM, reply.<br>
Q: Timing?<br>
A: Overanalyzed<br>
Q: Timing?<br>
A: Ask your salespeople. 6-45 days is good. -> not twice in a week<br>
Q: From?<br>
A: Depends, can be dynamic.<br>
Q: Not engaging?<br>
A: Don&#8217;t remove the, keep on going.</p>

<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2012%2F12%2Fsketchnotes-the-very-versatile-dripmathew-sweezey-pardot%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Sketchnotes: Marketing Automation, Jeffrey Yee (#torontob2b)</title>
		<link>https://sachachua.com/blog/2012/05/sketchnotes-marketing-automation-jeffrey-yee-torontob2b/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Fri, 04 May 2012 02:24:15 GMT</pubDate>
    <category>sketchnotes</category>
		<guid isPermaLink="false">https://sachachua.com/blog/?p=23427</guid>
		<description><![CDATA[<p>UPDATE 2012-11-15: <a href="http://www.brainrider.com/better-b2b-marketing/torontob2b-marketers-meetup-vi-video-recap/">Here&#8217;s the video recap!</a></p>
<p><strong>Marketing Automation<br>
</strong>Jeffrey Yee, Eloqua</p>
<p><strong><a href="https://sachachua.com/blog/2012/05/sketchnotes-marketing-automation-jeffrey-yee-torontob2b/20120503-torontob2b-marketing-automation-jeffrey-yee/" rel="attachment wp-att-23428"><img loading="lazy" class="aligncenter size-medium wp-image-23428" title="20120503-torontob2b-marketing-automation-jeffrey-yee" src="https://sachachua.com/blog/wp-content/uploads/2012/05/20120503-torontob2b-marketing-automation-jeffrey-yee1-580x439.jpg" alt="" width="580" height="439" srcset="https://sachachua.com/blog/wp-content/uploads/2012/05/20120503-torontob2b-marketing-automation-jeffrey-yee1-580x439.jpg 580w, https://sachachua.com/blog/wp-content/uploads/2012/05/20120503-torontob2b-marketing-automation-jeffrey-yee1-40x30.jpg 40w, https://sachachua.com/blog/wp-content/uploads/2012/05/20120503-torontob2b-marketing-automation-jeffrey-yee1-940x712.jpg 940w, https://sachachua.com/blog/wp-content/uploads/2012/05/20120503-torontob2b-marketing-automation-jeffrey-yee1.jpg 1056w" sizes="(max-width: 580px) 100vw, 580px"></a></strong></p>
<p>Like these? Check out my other <a href="https://sachachua.com/blog/category/sketchnotes">sketchnotes</a>, <a href="https://sachachua.com/blog/category/visual-book-notes">visual book notes/reviews,</a> and <a href="https://sachachua.com/blog/category/metaphor">visual metaphors</a>.</p>
<p><em>Here&#8217;s the text from the sketchnotes to improve people&#8217;s ability to search for it:</em></p>
<p><strong>Marketing automation</strong></p>
<p>Marketing Automation<br>
Jeffrey Yee, Eloqua</p>
<p>leads small<br>
list management<br>
forms<br>
scoring<br>
analytics<br>
events<br>
challenge<br>
-Too expensive<br>
-Not fully used<br>
-Not implemented correctly<br>
-Did not address business needs</p>
<p>1. Focus<br>
one thing! business need!<br>
2. Identify<br>
Look for what your top performers are already doing<br>
3. Start small, then build for mass adoption<br>
-Target the second-tier salespeople!<br>
4. Wait patiently for the lift.<br>
incremental improvement</p>
<p>Best practices from client side<br>
Dun &amp; Bradstreet<br>
credit risk management sales &amp; marketing supply risk management</p>
<p>1. Focus<br>
Example<br>
Retention trigger-based e-mail<br>
one need<br>
40.1% opens<br>
13.4% click through<br>
10% increase in retention rates<br>
2. Identify before you automate<br>
Focus group?<br>
Study top performers<br>
How are we achieving this today?<br>
Can we automate and scale this?</p>
<p>Repurpose</p>
<p>Think linear, it&#8217;s easier that way</p>
<p>Get personal and add value<br>
plaint text e-mail from sales, not marketing<br>
3. Mass adoption (but start very small)<br>
advocates get others on board</p>
<p>Look for the people who are close to their quotas:<br>
Tier 2 segmenting your salespeople!</p>
<p>Have reps vet leads before adding to program</p>
<p>3rd party data<br>
4. Wait patiently for the lift. Set expectations.<br>
Ex results<br>
-6 months<br>
pipeline value *19%<br>
# of yes 14%<br>
average upsize 3%<br>
ops won 25%</p>
<p>Budget 12+ months</p>
<p>Like low-hanging fruit<br>
Scaling up what already works<br>
Notes by Sacha Chua, @sachac, LivingAnAwesomeLife.com</p>
<p>&nbsp;</p>

<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2012%2F05%2Fsketchnotes-marketing-automation-jeffrey-yee-torontob2b%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item>
	</channel>
</rss>