Category Archives: org

Publishing Emacs News as plain text, HTML, and attached Org file

Update 2016-02-05: Since @ThierryStoehr linked to this post about Emacs News-related code, I figured I'd add a link to the other support functions I've been using to help me with Emacs News summarization. There's also this bit:
(let ((date (org-read-date nil nil "-mon")))
    (concat
     (my/org-list-from-rss "http://planet.emacsen.org/atom.xml" date) "\n"
     (shell-command-to-string (concat "~/bin/list-reddit-links.coffee emacs " date)) "\n"
     (shell-command-to-string (concat "~/bin/list-reddit-links.coffee org-mode " date)) "\n"
     "- New packages:\n"
     (my/list-new-packages) 
     "\n"))
Handy little things! ------ I've been publishing these weekly summaries of Emacs-related links on my blog and to the emacs-tangents mailing list / newsgroup. I started by posting plain text from Org Mode's ASCII export, and people asked for Org Mode and HTML formats. So here's some code that prepares things for pasting into a Gnus message buffer. It turns out that order matters for multipart/alternative - start with plain text, then include richer alternatives. First time around, I put the HTML version first, so people didn't end up seeing it. Anyway, here's something that shows up properly now: text/plain, then text/html, with text/x-org attached. The heavy lifting is done with org-export-string-as, which exports into different formats.
  (defun my/share-emacs-news ()
    "Prepare current subtree for yanking into post."
    (interactive)
    ;; Draft Gnus article
    (save-restriction
      (org-narrow-to-subtree)
      (let ((org-export-html-preamble nil)
            (org-html-toplevel-hlevel 3)
            output)
        (setq output
              (apply
               'format
               "<#multipart type=alternative>
<#part type=\"text/plain\" disposition=inline>
%s
<#/part>
<#part type=\"text/html\" disposition=inline>
%s
<#/part>
<#/multipart>
<#part type=\"text/x-org\" disposition=attachment name=\"emacs-news.org\">
%s
<#/part>
"
               (mapcar
                (lambda (format)
                  (org-export-string-as (buffer-substring (point-min) (point-max)) format t))
                '(ascii html org))))
        (kill-new output))))
Howard Abrams showed me something like this in June 2015's Emacs Hangout (~1:18:26) using org-mime-org-buffer-htmlize, which probably does the job in a much cooler way. =) I thought he had a blog post about it, but I can't seem to find it. Anyway, there's my little hack above!

Making my to-do list more detailed; process versus outcome

Some time ago, I wrote some code to make it easier for me to update my web-based Quantified Awesome time logs from Org Mode in Emacs, clocking into specific tasks or quickly selecting routine tasks with a few keyboard shortcuts. I've been refining my/org-clock-in-and-track, my/org-clock-in-and-track-by-name, and defhydra my/quantified-hydra, and I've been getting used to the new workflow. The more I smooth out the workflow, the more possibilities open up. Because I've set it up to prompt me for a time estimate before I start a task, I can see a running clock and timer in my modeline, and Emacs lets me know if I'm running over my estimate. Come to think of it, this makes it even easier to track at the detailed task level than to track at just the medium-level categories available through my web or mobile shortcuts. (If you're curious about the Emacs Lisp code, you can check out my Emacs configuration.)

I've also been sorting out my workflow for quickly adding tasks. C-c r t (org-capture, with the t template I defined in org-capture-templates) displays a buffer where I can type in the task information and set a time estimate. From there, I can file it under the appropriate project with C-c C-w (org-refile), or maybe schedule it with C-c C-s (org-schedule).

Since both creating and tracking tasks are now easier, I've been gradually adding small, routine tasks to my task list. This includes household tasks such as vacuuming and quick computer-based tasks such as checking for replies to @emacs. These tasks are in my routines.org file or tagged with the :routine: tag, so I can sort them in my Org agenda view or filter them out if I want.

It might be interesting to bring that data from Emacs to my mobile phone, but it's not particularly important at the moment. I'm usually home, so I can just check my org-agenda throughout the day. If I'm out for some errands, my errand list is short enough to remember (or quickly note somewhere), and I can use my phone to quickly jot short notes to add to my to-do list when I get back.

The next step for that workflow would probably be to improve my views of unscheduled tasks, choosing new things to work on based on their time estimates, contexts, or projects. I already have a few org-agenda-custom-commands for these, although I still need to tweak them so that they feel like they make sense. Project navigation works out pretty well, though, and it'll get better as I gradually clean up my Org files.

It feels a little odd to use my to-do list this much throughout the day, compared to the less-structured approach of deciding at each moment. The day feels less leisurely and expansive. Still, there's a certain satisfaction in crossing things off and knowing I'm taking care of the little things. I'll find a new balance between the number of items on my list and the time I want to use to follow the butterflies of my interest or energy. Maybe I'll use tags or priorities to highlight energizing tasks, the dessert tasks to my vegetable tasks. (Ooh, I wonder how I can get different colours in my org-agenda.) In the meantime, I think that fleshing out my to-do list even more – capturing the little routines that might get forgotten if I get more fuzzy-brained or distracted – may help me in the long run.

I think one of the things about working with a list of small, varied tasks is that there's less of that feeling of accomplishing a big, non-routine chunk. One way I can work around this is to pick a dessert-y project focus for the morning and finish several tasks related to it, before getting through the rest of the routine tasks. There's also a different approach: focusing on the process instead of the outcome, cultivating the satisfaction of steady progress instead of the exhilaration of a win. If I keep on improving my workflow for managing tasks, ideas, and reviews, I think it will pay off even as circumstances change.

2015-12-04e Process versus outcome -- index card #productivity #mindset #perspective #stoicism #philosophy 2015-12-04c Preparing for steady progress -- index card #productivity #fuzzy #preparation

Org Mode tables and fill-in quizzes – Latin verb conjugation drills in Emacs

I was looking for a Latin verb conjugation drill similar to these ones for and nouns and pronouns. I liked the instant feedback and the ability to quickly get hints. I couldn't find an online drill I liked, though, so I made my own with Emacs and Org. (Because… why not?) I wrote some code that would take a table like this:
present - 1st sing. - ago / agere agO
present - 2nd sing. - ago / agere agis
present - 3rd sing. - ago / agere agit
present - 1st plu. - ago / agere agimus
present - 2nd plu. - ago / agere agitis
present - 3rd plu. - ago / agere agunt
imperfect - 1st sing. - ago / agere agEbam
imperfect - 2nd sing. - ago / agere agEbAs
imperfect - 3rd sing. - ago / agere agEbat
imperfect - 1st plu. - ago / agere agEbAmus
imperfect - 2nd plu. - ago / agere agEbAtis
imperfect - 3rd plu. - ago / agere agEbant
future - 1st sing. - ago / agere agam
future - 2nd sing. - ago / agere agEs
future - 3rd sing. - ago / agere agEt
future - 1st plu. - ago / agere agEmus
future - 2nd plu. - ago / agere agEtis
future - 3rd plu. - ago / agere agent
I can call my/make-fill-in-quiz to get a quiz buffer that looks like this. If I get stuck, ? shows me a hint in the echo area. latin-verb-drills-0 To make it easier, I've left case-fold-search set to nil so that I don't have to match the case (uppercase vowels = macrons), but I can set case-fold-search to t if I want to make sure I've got the macrons in the right places. Here's the code to display the quiz buffer.
     (require 'widget)
     (defun my/check-widget-value (widget &rest ignore)
       "Provide visual feedback for WIDGET."
       (cond
        ((string= (widget-value widget) "?")
         ;; Asking for hint
         (message "%s" (widget-get widget :correct))
         (widget-value-set widget ""))
        ;; Use string-match to obey case-fold-search 
        ((string-match 
          (concat "^"
                  (regexp-quote (widget-get widget :correct))
                  "$")
          (widget-value widget))
         (message "Correct")
         (goto-char (widget-field-start widget))
         (goto-char (line-end-position))
         (insert "✓")
         (widget-forward 1)
         )))

   (defun my/make-fill-in-quiz (&optional quiz-table)
     "Create an fill-in quiz for the Org table at point.
The Org table's first column should have the questions and the second column 
should have the answers."
     (interactive (list (org-babel-read-table)))
     (with-current-buffer (get-buffer-create "*Quiz*")
       (kill-all-local-variables)
       (let ((inhibit-read-only t))
         (erase-buffer))
       (remove-overlays)
       (mapc (lambda (row)
               (widget-insert (car row))
               (widget-insert "\t")
               (widget-create 'editable-field
                              :size 15
                              :correct (cadr row)
                              :notify 'my/check-widget-value)
               (widget-insert "\n"))    
             quiz-table)
       (widget-create 'push-button
                      :table quiz-table
                      :notify (lambda (widget &rest ignore)
                                (my/make-fill-in-quiz (widget-get widget :table))) 
                      "Reset")
       (use-local-map widget-keymap)
       (widget-setup)
       (goto-char (point-min))
       (widget-forward 1)
       (switch-to-buffer (current-buffer))))
Incidentally, I generated the table above from a larger table of Latin verb conjugations in the appendix of Wheelock's Latin, specified like this:
#+NAME: present-indicative-active
| laudO    | moneO   | agO    | audiO   | capiO   |
| laudAs   | monEs   | agis   | audIs   | capis   |
| laudat   | monet   | agit   | audit   | capit   |
| laudAmus | monEmus | agimus | audImus | capimus |
| laudAtis | monEtis | agitis | audItis | capitis |
| laudant  | monent  | agunt  | audiunt | capiunt |

#+NAME: imperfect-indicative-active
| laudAbam   | monEbam   | agEbam   | audiEbam   | capiEbam   |
| laudAbas   | monEbas   | agEbAs   | audiEbAs   | capiEbas   |
| laudAbat   | monEbat   | agEbat   | audiEbat   | capiEbat   |
| laudAbAmus | monEbAmus | agEbAmus | audiEbAmus | capiEbAmus |
| laudAbAtis | monEbAtis | agEbAtis | audiEbAtis | capiEbAtis |
| laudAbant  | monEbant  | agEbant  | audiEbant  | capiEbant  |

#+NAME: future-indicative-active
| laudAbO    | monEbO    | agam   | audiam     | capiam     |
| laudAbis   | monEbis   | agEs   | audiEs     | capiEs     |
| laudAbit   | monEbit   | agEt   | audiet     | capiet     |
| laudAbimus | monEbimus | agEmus | audiEmus   | capiEmus   |
| laudAbitis | monEbitis | agEtis | audiEtis   | capiEtis   |
| laudAbunt  | monEbunt  | agent  | audient    | capient    |
with the code:
#+begin_src emacs-lisp :var present=present-indicative-active :var imperfect=imperfect-indicative-active :var future=future-indicative-active
  (defun my/label-latin-with-verbs (table verbs persons tense)
    (apply 'append
           (-zip-with (lambda (row person) 
                        (-zip-with (lambda (word verb)
                                     (list word (format "%s - %s - %s" tense person verb)))
                                   row verbs))
                      table (-cycle persons))))
  (apply 'append 
         (mapcar (lambda (tense)
                   (my/label-latin-with-verbs 
                    (symbol-value tense)
                    '("laudo / laudare" "moneo / monEre" "ago / agere" "audiO / audIre" "capiO / capere")
                    '("1st sing." "2nd sing." "3rd sing." "1st plu." "2nd plu." "3rd plu.")
                    (symbol-name tense)))
                 '(present imperfect future)))

#+end_src
This uses dash.el for the -zip-with and -cycle functions. There's probably a much better way to process the lists, but I'm still getting the hang of thinking properly functionally… =) Anyway, I'm sure it will be handy for a number of other quiz-like things. org-drill and org-drill-table will probably come in handy for flashcards, too!

Capturing links quickly with emacsclient, org-protocol, and Chrome Shortcut Manager on Microsoft Windows 8

UPDATE 2015-11-30: Well, that bitrotted quickly! Chrome Shortcut Manager is no longer available, but maybe Shortkeys will work instead. Since I'll be snipping lots of Emacs-related resources and organizing them into Emacs news roundups, I figured it was time to get org-protocol working. Step 1: Get emacsclient to work I was getting the error "No connection could be made because the target machine actively refused it." I needed to change my Windows Firewall rules. From the Windows Firewall screen, I clicked on Advanced settings and chose Inbound Rules. On the Programs and Services tab, I confirmed that the right Emacs binary was selecI looked for the rules for GNU Emacs, consolidating them down to two rules (UDP and TCP). I limited the scope to local/remote 127.0.0.1. On the advanced tab, I selected all the profiles and changed edge traversal to blocked. I was still getting the error despite a fresh M-x server-start. After I deleted the contents of ~/.emacs.d/server and did another M-x server-start. When I ran emacsclient test.txt from the command-line, it correctly opened the file in my existing Emacs instance. Hooray! Step 2: Load org-protocol I added org-protocol to the org-modules variable so that Org would load it when Emacs reaches the (org-load-modules-maybe t) in my config. Since I didn't want to restart Emacs, I also evaluated (load-library "org-protocol") to load it. Step 3: Register the protocol I ran an org-protocol.reg that set up the appropriate org protocol entry:
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\org-protocol]
"URL Protocol"=""
@="URL:Org Protocol"

[HKEY_CLASSES_ROOT\org-protocol\shell]

[HKEY_CLASSES_ROOT\org-protocol\shell\open]

[HKEY_CLASSES_ROOT\org-protocol\shell\open\command]
@="\"c:\\Program Files (x86)\\GNU Emacs 24.4\\bin\\emacsclientw.exe\"  \"%1\""
You can find a similar one in the org-protocol documentation. Step 4: Add support to Chrome I wanted something a bit different from the org-capture extensions available for Chrome. In particular, I wanted:
  • a keyboard-friendly way to quickly store a link
  • a keyboard-friendly way to capture a link with some notes
The Shortcut Manager extension for Chrome lets you specify your own keyboard shortcuts for running short Javascript. Inline Javascript doesn't work on all sites. For example, Github blocks it with the following error: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src assets-cdn.github.com". Either the 'unsafe-inline' keyword, a hash ('...'), or a nonce ('nonce-...') is required to enable inline execution. Still, it works for many sites, so it's a start. Here are the shortcuts I put together.
l Store link
L Store link (prompt for title, default to selection or document title)
c Capture link (prompt for template)
You can import them by going to Chrome's More Tools > Extensions screen and choosing the Options link for Shortcut Manager. From there, use Import settings.
// ==UserScript==
// @ShortcutManager
// @name Store link
// @namespace XPrUJhE4wRsC
// @key l
// @include *
// ==/UserScript==
var storeLink = function(){
  var selection = window.getSelection().toString();
  var uri = 'org-protocol://store-link://' +
        encodeURIComponent(window.location.href) + '/' +
        encodeURIComponent(selection || document.title);
  window.location = uri;
  return uri;
};
storeLink();

// ==UserScript==
// @ShortcutManager
// @name Capture link
// @namespace XPrUJhE4wRsC
// @key c
// @include *
// ==/UserScript==
var captureLink =function(){
  var uri = 'org-protocol://capture://' +
        encodeURIComponent(window.location.href) + '/' +
        encodeURIComponent(document.title) + '/' +
        encodeURIComponent(window.getSelection().toString());
  window.location = uri;
  return uri;
};
captureLink();


// ==UserScript==
// @ShortcutManager
// @name Store link with prompt
// @namespace XPrUJhE4wRsC
// @key Shift+l
// @include *
// ==/UserScript==
var storeLinkWithPrompt = function(){
  var selection = window.getSelection().toString();
  var uri = 'org-protocol://store-link://' +
        encodeURIComponent(window.location.href) + '/' +
        encodeURIComponent(window.prompt('Title', selection || document.title));
  window.location = uri;
  return uri;
};
storeLinkWithPrompt();
Shortcut Manager looks like a really useful extension. Here are some other shortcuts I set up:
x close the current tab
r reload (cacheless)
t open a new tab
n select the right tab
p select the left tab
b back
f forward
Step 5: Add shortcuts for managing stored links I added my/org-insert-link and org-insert-last-stored-link to my main hydra, which is on my hh keychord. my/org-insert-link is like org-insert-link, except it adds a newline if the cursor is at an Org link so that we don't trigger org-insert-link's behaviour of editing links.
(defun my/org-insert-link ()
  (interactive)
  (when (org-in-regexp org-bracket-link-regexp 1)
    (goto-char (match-end 0))
    (insert "\n"))
  (call-interactively 'org-insert-link))

(key-chord-define-global "hh"
                         (defhydra my/key-chord-commands ()
                           "Main"
                           ;; ...
                           ("L" my/org-insert-link)
                           ("l" org-insert-last-stored-link)
                           ;; ...
                           ))
This lets me quickly insert a bunch of links with a key sequence like h h l l l l or select a link to insert with h h L. C-y (yank) pulls in the URL of the last stored link, too. Let's see how this works out!

Wow, literate devops with Emacs and Org does actually work on Windows

Since I persist in using Microsoft Windows as my base system, I'm used to not being able to do the kind of nifty tricks that other people do with Emacs and shell stuff. So I was delighted to find that the literate devops that Howard Abrams described – running shell scripts embedded in an Org Mode file on a remote server – actually worked with Plink. Here's my context: The Toronto Public Library publishes a list of new books on the 15th of every month. I've written a small Perl script that parses the list for a specified category and displays the Dewey decimal code, title, and item ID. I also have another script (Ruby on Rails, part of quantifiedawesome.com) that lets me request multiple items by pasting in text containing the item IDs. Tying these two together, I can take the output of the library new releases script, delete the lines I'm not interested in, and feed those lines to my library request script. Instead of starting Putty, sshing to my server, and typing in the command line myself, I can now use C-c C-c on an Org Mode block like this:
#+begin_src sh :dir [email protected]:~
perl library-new.pl Business
#+end_src
That's in a task that's scheduled to repeat monthly, for even more convenience, and I also have a link there to my web-based interface for bulk-requesting files. But really, now that I've got it in Emacs, I should add a #+NAME: above the #+RESULTS: and have Org Mode take care of requesting those books itself. On a related note, I'd given up on being able to easily use TRAMP from Emacs on Windows before, because Cygwin SSH was complaining about a non-interactive terminal.
ssh -l sacha  -o ControlPath=c:/Users/Sacha/AppData/Local/Temp/tramp.13728lpv.%r@%h:%p -o ControlMaster=auto -o ControlPersist=no -e none direct.sachachua.com && exit || exit
Pseudo-terminal will not be allocated because stdin is not a terminal.
ssh_askpass: exec(/usr/sbin/ssh-askpass): No such file or directory
Permission denied, please try again.
ssh_askpass: exec(/usr/sbin/ssh-askpass): No such file or directory
Permission denied, please try again.
ssh_askpass: exec(/usr/sbin/ssh-askpass): No such file or directory
Permission denied (publickey,password).
As it turns out, setting the following made it work for me.
(setq tramp-default-method "plink")
Now I can do things like the following:
(find-file "[email protected]:~/library-new.pl")
… which is, incidentally, this file (edited to remove my credentials):
#!/usr/bin/perl
# Displays new books from the Toronto Public Library
#
# Author: Sacha Chua ([email protected])
#
# Usage:
# perl library-new.pl <category> - print books
# perl library-new.pl <file> <username> <password> - request books
#
use Date::Parse;

#!/usr/bin/perl -w
use strict;
use URI::URL;
use WWW::Mechanize;
use WWW::Mechanize::FormFiller;
use WWW::Mechanize::TreeBuilder;
use HTML::TableExtract;
use Data::Dumper;
sub process_account($$$);
sub trim($);
sub to_renew($$);
sub clean_string($);

# Get the arguments
if ($#ARGV < 0) {
    print "Usage:\n";
    print "perl library-new.pl <category/filename> [username] [password]\n";
    exit;
}

my $agent = WWW::Mechanize->new( autocheck => 1 );
my $formfiller = WWW::Mechanize::FormFiller->new();
if ($#ARGV > 0) {
  my $filename = shift(@ARGV);
  my $username = "NOT ACTUALLY MY USERNAME";
  my $password = "NOT ACTUALLY MY PASSWORD";
  print "Requesting books\n";
  request_books($agent, $username, $password, $filename);
} else {
  my $category = shift(@ARGV);
  WWW::Mechanize::TreeBuilder->meta->apply($agent);
  print_new_books($agent, $category);
}


## FUNCTIONS ###############################################

# Perl trim function to remove whitespace from the start and end of the string
sub trim($)
{
  my $string = shift;
  $string =~ s/^\s+//;
  $string =~ s/\s+$//;
  return $string;
}

sub request_books($$$$)
{
  my $agent = shift;
  my $username = shift;
  my $password = shift;
  my $filename = shift;

  # Read titles and IDs
  open(DATA, $filename) or die("Couldn't open file.");
  my @lines = <DATA>;
  close(DATA);

  my %requests = ();

  my $line;
  my $title;
  my $id;
  foreach $line (@lines) {
    ($title, $id) = split /\t/, $line;
    chomp $id;
    $requests{$id} = $title;
  }

  # Log in
  log_in_to_library($agent, $username, $password);
  print "Retrieving checked-out and requested books...";
  # Retrieve my list of checked-out and requested books
  my $current_ids = get_current_ids($agent);

  # Retrieve a stem URL that I can use for requests
  my $base_url = 'https://www.torontopubliclibrary.ca/placehold?itemId=';
  my @already_out;
  my @success;
  my @failure;
  # For each line in the file
  while (($id, $title) = each(%requests)) {
    # Do I already have it checked out or on hold? Skip.
    if ($current_ids->{$id}) {
      push @already_out, $title . " (" . $current_ids->{$id} . ")";
    } else {
      # Request the hold
      my $url = $base_url . $id;
      $agent->get($url);
      $agent->form_name('form_place-hold');
      $agent->submit();
      if ($agent->content =~ /The hold was successfully placed/) {
        # print "Borrowed ", $title, "\n";
        ## Did it successfully get checked out? Save title in success list
        push @success, $title;
      } else {
        # Else, save title and ID in fail list
        push @failure, $title . "\t" . $id;
      }
    }
  }
  # Print out success list
  if ($#success > 0) {
    print "BOOKS REQUESTED:\n";
    foreach my $title (@success) {
      print $title, "\n";
    }
    print "\n";
  }
  # Print out already-out list
  if ($#already_out > 0) {
    print "ALREADY REQUESTED/CHECKED OUT:\n";
    foreach my $s (@already_out) {
      print $s, "\n";
    }
    print "\n";
  }
  # Print out fail list
  if ($#failure > 0) {
    print "COULD NOT REQUEST:\n";
    foreach my $s (@failure) {
      print $s, "\n";
    }
    print "\n";
  }
}

sub get_current_ids($)
{
  my $agent = shift;
  my %current_ids = ();
  my $string = $agent->content;
  while ($string =~ m/TITLE\^([0-9]+)\^/g) {
    $current_ids{$1} = 'requested';
  }
  while ($string =~ m/RENEW\^([0-9]+)\^/g) {
    $current_ids{$1} = 'checked out';
  }
  return \%current_ids;
}

sub print_new_books($$)
{
  my $agent = shift;
  my $category = shift;
  $agent->env_proxy();
  $agent->get('http://oldcatalogue.torontopubliclibrary.ca');
  $agent->follow_link(text_regex => qr/Our Newest Titles/);
  $agent->follow_link(text_regex => qr/$category/i);

  my $continue = 1;
  while ($continue) {
    print_titles_on_page($agent);
    if ($agent->form_with_fields('SCROLL^F')) {
      $agent->click('SCROLL^F');
    } else {
      $continue = 0;
    }
  }
}

# Print out all the entries on this page
sub print_titles_on_page($)
{
  my $agent = shift;
  my @titles = $agent->look_down(sub {
                                $_[0]->tag() eq 'strong' and
                                $_[0]->parent->attr('class') and
                                $_[0]->parent->attr('class') eq 'itemlisting'; });
  foreach my $title (@titles) {
    my $hold = $title->parent->parent->parent->parent->look_down(sub {
                                                          $_[0]->attr('alt') and
                                                          $_[0]->attr('alt') eq 'Place Hold'; });
    my $id = "";
    my $call_no = "";
    if ($hold && $hold->parent && $hold->parent->attr('href') =~ /item_id=([^&]+)&.*?callnum=([^ "&]+)/) {
      $id = $1;
      $call_no = $2;
    }
    print $call_no . "\t" . $title->as_text . "\t" . $id . "\n";
  }
}

sub clean_string($)
{
    my $string = shift;
    $string =~ s#^.*?(<form id="renewitems" [^>]+>)#<html><body>\1#s;
    $string =~ s#</form>.*#</form></html>#s;
    $string =~ s#<table border="0" bordercolor="red".*(<table border="0" bordercolor="blue" cellspacing="0" cellpadding="0">)#\1#s;
    $string =~ s#</table>.*</form>#</table></form>#s;
# Clean up for parsing
    $string =~ s#<!-- Print the date due -->##g;
    $string =~ s#<br> <!-- Displays Date -->##g;
    return $string;
}

sub log_in_to_library($$$) {
    my $agent = shift;
    my $username = shift;
    my $password = shift;
    $agent->get('http://beta.torontopubliclibrary.ca/youraccount');
    $agent->form_name('form_signin');
    $agent->current_form->value('userId', $username);
    $agent->current_form->value('password', $password);
    $agent->submit();
}
Ah, Emacs!

Update on Emacs Conf 2015 videos; Org Mode tables and time calculations

I spent the day cutting up the rest of the videos from the Emacs Conference 2015 Twitch.tv stream into individual talks. I'd already cut the set of talks before lunch, but there were quite a few more after. As it turned out, keeping the video data in .ts format instead of converting it to .mp4 is actually better for Youtube processing. Since Camtasia Studio and Movie Maker were both having problems with the large videos, I used VLC to play the video and find the timestamps at which I needed to cut the segments. I made an Org Mode table with the start and end times, and then I used the ;T flag in a table function to get the duration. A little bit of Emacs Lisp code later, and I had my ffmpeg commands. Here's the source from my Org file:
#+NAME: emacsconf-c.ts
| Notes                                            |      Start |        End | Duration |
|--------------------------------------------------+------------+------------+----------|
| Emacs configuration                              | 4:02:25.37 | 4:27:09.30 | 00:24:44 |
| Hearing from Emacs Beginners                     |    4:27:27 |    5:01:00 | 00:33:33 |
| Lightning talk: Emacs Club                       | 5:03:19.30 | 5:19:37.83 | 00:16:18 |
| Starting an Emacs Meetup - Harry Schwartz part 1 | 5:31:52.03 |    6:01:20 | 00:29:28 |
#+TBLFM: $4=$3-$2;T

#+NAME: emacsconf-a.ts
| Notes                                                    |   Start |     End | Duration |
|----------------------------------------------------------+---------+---------+----------|
| Starting an Emacs Meetup - Harry Schwartz part 2         |  0:0:00 | 0:20:04 | 00:20:04 |
| Literate Devops - Howard Abrams                          | 1:28:20 | 2:08:15 | 00:39:55 |
| Lightning talk: Wanderlust and other mail clients        | 2:15:04 | 2:26:55 | 00:11:51 |
| Making Emacs a Better Tool for Scholars - Erik Hetzner   | 2:27:00 | 2:57:38 | 00:30:38 |
| Wrapping up and going forward                            | 2:58:09 | 2:59:44 | 00:01:35 |
| Lightning talk: Collaborative coding with tmux and tmate | 3:00:20 | 3:05:53 | 00:05:33 |
| Lightning talk: Cask and Pellet                          | 3:05:56 | 3:09:04 | 00:03:08 |
| Lightning talk: File sharing with Git and save hooks     | 3:09:34 | 3:17:50 | 00:08:16 |
| Lightning talk: Calc                                     | 3:18:42 | 3:33:20 | 00:14:38 |
| Lightning talk: Magit                                    | 3:35:15 | 3:49:42 | 00:14:27 |
| Lightning talk: gist.el                                  | 3:53:50 | 4:01:58 | 00:08:08 |
| Lightning talk: Go                                       | 4:02:45 | 4:16:37 | 00:13:52 |
| Question: Emacs Lisp backtraces                          | 4:16:50 | 4:20:09 | 00:03:19 |
#+TBLFM: $4=$3-$2;T

#+begin_src emacs-lisp :var data=emacsconf-a.ts :var data2=emacsconf-c.ts :colnames t :results output
(let ((format-str "ffmpeg -i %s -ss %s -t %s -c:v copy -c:a copy \"EmacsConf 2015 - %s.ts\"\n"))
  (mapc (lambda (file)
    (mapc (lambda (row) 
      (princ (format format-str (car file) (elt row 1) (elt row 3) (my/convert-sketch-title-to-filename (elt row 0))))) 
     (cdr file)))
    `(("emacsconf-c.ts" . ,data2)
      ("emacsconf-a.ts" . ,data))))
#+end_src
and the output:
ffmpeg -i emacsconf-c.ts -ss 4:02:25.37 -t 00:24:44 -c:v copy -c:a copy "EmacsConf 2015 - Emacs configuration.ts"
ffmpeg -i emacsconf-c.ts -ss 4:27:27 -t 00:33:33 -c:v copy -c:a copy "EmacsConf 2015 - Hearing from Emacs Beginners.ts"
ffmpeg -i emacsconf-c.ts -ss 5:03:19.30 -t 00:16:18 -c:v copy -c:a copy "EmacsConf 2015 - Lightning talk - Emacs Club.ts"
ffmpeg -i emacsconf-c.ts -ss 5:31:52.03 -t 00:29:28 -c:v copy -c:a copy "EmacsConf 2015 - Starting an Emacs Meetup - Harry Schwartz part 1.ts"
ffmpeg -i emacsconf-a.ts -ss 0:0:00 -t 00:20:04 -c:v copy -c:a copy "EmacsConf 2015 - Starting an Emacs Meetup - Harry Schwartz part 2.ts"
ffmpeg -i emacsconf-a.ts -ss 1:28:20 -t 00:39:55 -c:v copy -c:a copy "EmacsConf 2015 - Literate Devops - Howard Abrams.ts"
ffmpeg -i emacsconf-a.ts -ss 2:15:04 -t 00:11:51 -c:v copy -c:a copy "EmacsConf 2015 - Lightning talk - Wanderlust and other mail clients.ts"
ffmpeg -i emacsconf-a.ts -ss 2:27:00 -t 00:30:38 -c:v copy -c:a copy "EmacsConf 2015 - Making Emacs a Better Tool for Scholars - Erik Hetzner.ts"
ffmpeg -i emacsconf-a.ts -ss 2:58:09 -t 00:01:35 -c:v copy -c:a copy "EmacsConf 2015 - Wrapping up and going forward.ts"
ffmpeg -i emacsconf-a.ts -ss 3:00:20 -t 00:05:33 -c:v copy -c:a copy "EmacsConf 2015 - Lightning talk - Collaborative coding with tmux and tmate.ts"
ffmpeg -i emacsconf-a.ts -ss 3:05:56 -t 00:03:08 -c:v copy -c:a copy "EmacsConf 2015 - Lightning talk - Cask and Pellet.ts"
ffmpeg -i emacsconf-a.ts -ss 3:09:34 -t 00:08:16 -c:v copy -c:a copy "EmacsConf 2015 - Lightning talk - File sharing with Git and save hooks.ts"
ffmpeg -i emacsconf-a.ts -ss 3:18:42 -t 00:14:38 -c:v copy -c:a copy "EmacsConf 2015 - Lightning talk - Calc.ts"
ffmpeg -i emacsconf-a.ts -ss 3:35:15 -t 00:14:27 -c:v copy -c:a copy "EmacsConf 2015 - Lightning talk - Magit.ts"
ffmpeg -i emacsconf-a.ts -ss 3:53:50 -t 00:08:08 -c:v copy -c:a copy "EmacsConf 2015 - Lightning talk - gist.el.ts"
ffmpeg -i emacsconf-a.ts -ss 4:02:45 -t 00:13:52 -c:v copy -c:a copy "EmacsConf 2015 - Lightning talk - Go.ts"
ffmpeg -i emacsconf-a.ts -ss 4:16:50 -t 00:03:19 -c:v copy -c:a copy "EmacsConf 2015 - Question - Emacs Lisp backtraces.ts"
You can watch the Emacs Conference 2015 playlist on YouTube. At some point, each talk will probably have individual wiki pages and IRC logs at http://emacsconf2015.org/ . =) Enjoy! Related tech notes: Emacs Conf video tech notes: jit.si, twitch.tv, livestreamer, ffmpeg