\input texinfo.tex    @c -*-texinfo-*-
@c %**start of header
@setfilename mu-guile.info
@settitle mu-guile user manual

@c Use proper quote and backtick for code sections in PDF output
@c Cf. Texinfo manual 14.2
@set txicodequoteundirected
@set txicodequotebacktick

@documentencoding UTF-8
@c %**end of header

@include version.texi

@copying
Copyright @copyright{} 2012-@value{UPDATED-YEAR} Dirk-Jan C. Binnema

@quotation
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A
copy of the license is included in the section entitled ``GNU Free
Documentation License.''
@end quotation
@end copying

@titlepage
@title @t{mu-guile} - extending @t{mu} with Guile Scheme
@subtitle version  @value{VERSION}
@author Dirk-Jan C. Binnema

@c The following two commands start the copyright page.
@page
@vskip 0pt plus 1filll
@insertcopying
@end titlepage

@dircategory The Algorithmic Language Scheme
@direntry
* Mu-guile: (mu-guile).        Guile-bindings for the mu e-mail indexer/searcher
@end direntry

@contents

@ifnottex
@node Top
@top mu-guile manual
@end ifnottex

@iftex
@node Welcome to mu-guile
@unnumbered Welcome to mu-guile
@end iftex

Welcome to @t{mu-guile}!

@t{mu} is a program for indexing and searching your e-mails. It can search
your messages in many different ways, but sometimes that may not be
enough. If you have very specific queries, or want do generate some
statistics, you need some more power.

@t{mu-guile} is made for those cases. @t{mu-guile} exposes the internals of
@t{mu} and its database to the @t{guile} programming language. Guile is the
@emph{GNU Ubiquitous Intelligent Language for Extensions} - a version of the
@emph{Scheme} programming language and the official GNU extension language.

Guile/Scheme is a member of the @emph{Lisp} family of programming languages --
like emacs-lisp, @emph{Racket}, Common Lisp. If you're not familiar with
Scheme, @t{mu-guile} is an excellent opportunity to learn a bit about!

Trust me, it's not very hard -- and it's @emph{fun}!

@menu
* Getting started::
* Initializing mu-guile::
* Messages::
* Contacts::
* Attachments and other parts::
* Statistics::
* Plotting data::
* Writing scripts::

Appendices

* Recipes:: Snippets do specific things
* GNU Free Documentation License::  The license of this manual.
@end menu

@node Getting started
@chapter Getting started

@menu
* Installation::
* Making sure it works::
@end menu

This chapter walks you through the installation and the basic setup.

@node Installation
@section Installation

@t{mu-guile} is part of @t{mu} - by installing the latter, the former is
necessarily installed as well. At the time of writing, there are no
distribution-provided packaged versions of @t{mu-guile}; so for now, you need
to follow the steps below.

@subsection Guile 2.x

@t{mu-guile} is built automatically when @t{mu} is built, if you have
@t{guile} version 2 or higher. (@t{mu} checks for this during
@t{configure}). Thus, the first step is to ensure you have @t{guile}
installed.

On Debian/Ubuntu you can install @t{guile} 2.x using the @t{guile-2.0-dev}
package (and its dependencies):
@example
$ sudo apt-get install guile-2.0-dev
@end example

At the time of writing, there are no official packages for
Fedora@footnote{@url{https://bugzilla.redhat.com/show_bug.cgi?id=678238}}.  If
you are using Fedora or any other system that does not have packages, you need
to compile @t{guile} from
source@footnote{@url{http://www.gnu.org/software/guile/manual/html_node/Obtaining-and-Installing-Guile.html#Obtaining-and-Installing-Guile}}.

@subsection gnuplot

For creating graphs with @t{mu-guile}, you need the @t{gnuplot} program --
most likely, there is a package available for your system; for example:

@example
$ sudo apt-get install gnuplot
@end example

and in Fedora:

@example
$ sudo yum install gnuplot
@end example

@subsection mu

Assuming @t{guile} 2.x is installed correctly, @t{mu} finds it during its
@t{configure}-stage, and creates @t{mu-guile}. Building @t{mu} follows the
normal steps -- please see the @t{mu} documentation for the details.

The output of @t{./configure} should end with a little text describing the
detected versions of various libraries @t{mu} depends on. In particular, it
should mention the @t{guile} version, e.g.

@example
Guile version                        : 2.0.3.82-a2c66
@end example

If you don't see any line referring to @t{guile}, please install it, and run
@t{configure} again. After a successful @t{./configure}, we can make and
install the package:

@example
$ make && sudo make install
@end example

@subsection mu-guile

After this, @t{mu} and @t{mu-guile} are installed -- usually somewhere under
@t{/usr/local}.You may need to update @t{guile}'s @code{%load-path} to find it
there. You can check the current @code{%load-path} with the following:

@example
guile -c '(display %load-path)(newline)'
@end example

If necessary, you can add the @t{%load-path} by adding to your
@file{~/.guile}:

@lisp
(set! %load-path (cons "/usr/local/share/guile/site/2.0" %load-path))
@end lisp

Or, alternatively, you can set @t{GUILE_LOAD_PATH}:
@example
export GUILE_LOAD_PATH=/usr/local/share/guile/site/2.0
@end example

In both cases the directory should be the directory that contains the
installed @t{mu.scm}; if you installed @t{mu} under a different prefix, you
must change the @code{%load-path} accordingly. After this, you should be ready
to go!

Furthermore, you need to ensure that @t{guile} can find the mu-guile
library; for this we can use @code{LTDL_LIBRARY_PATH}, e.g.
@example
export LTDL_LIBRARY_PATH=/usr/local/lib
@end example

@node Making sure it works
@section Making sure it works

Assuming @t{mu-guile} has been installed correctly (@ref{Installation}), and
also assuming that you have already indexed your e-mail messages (if
necessary, see the @t{mu-index} man-page), we are ready to start @t{mu-guile};
a session may look something like this:

@cartouche
@verbatim
GNU Guile 2.0.5.123-4bd53
Copyright (C) 1995-2012 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(guile-user)>
@end verbatim
@end cartouche

@noindent
Now, copy-paste the following after the prompt:

@cartouche
@lisp
(use-modules (mu))
(mu:initialize)
(for-each
    (lambda(msg)
       (format #t "Subject: ~a\n" (mu:subject msg)))
        (mu:message-list "hello"))
@end lisp
@end cartouche

@noindent
After pressing @key{Enter}, you should get a list of all subjects of messages
that match @t{hello}:

@verbatim
...
Subject: RE: The Bird Serpent War Cataclysm
Subject: Hello!
Subject: Re: post-run tomorrow
Subject: When all is lost
...
@end verbatim

@noindent
If all this works, congratulations! @t{mu-guile} is installed now, ready to
serve your every searching need!

@node Initializing mu-guile
@chapter Initializing mu-guile

We now have installed @t{mu-guile}, and in @ref{Making sure it works}
confirmed that things work by trying some simple script. In this and the
following chapters, we take a closer look at programming with @t{mu-guile}.

It is possible to write separate programs with @t{mu-guile}, but for now we'll
do things @emph{interactively}, that is, from the Guile-prompt
(``@abbr{REPL}'').

As we have seen, we start our @t{mu-guile} session by starting @t{guile}:

@verbatim
$ guile
@end verbatim

@cartouche
@verbatim
GNU Guile 2.0.5.123-4bd53
Copyright (C) 1995-2012 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(guile-user)>
@end verbatim
@end cartouche

The first thing we need to do is loading the modules. All the basics are in
the @t{(mu)} module, with some statistical extras in @t{(mu stats)}, and some
graph plotting functionality in @t{(mu plot)}@footnote{@code{(mu plot)}
requires the @t{gnuplot} program}. Let's load all of them:
@verbatim
scheme@(guile-user)> (use-modules (mu) (mu stats) (mu plot))
@end verbatim

The first time you do this, @t{guile} will probably respond by showing some
messages about compiling the modules, and then return to you with another
prompt. Before we can do anything with @t{mu guile}, we need to initialize the
system:

@verbatim
scheme@(guile-user)> (mu:initialize)
@end verbatim

This opens the database for reading, using the default location of
@file{~/.cache/mu}@footnote{If you keep your @t{mu} database in a non-standard
place, use @code{(mu:initialize "/path/to/my/mu/")}}

Now, @t{mu-guile} is ready to go. In the next chapter, we go through the
modules and show what you can do with them.

@node Messages
@chapter Messages

In this chapter, we discuss searching messages and doing things with them.

@menu
* Finding messages:: query for messages in the database
* Message methods::  what methods are available for messages?
* Example - the longest subject:: find the messages with the longest subject
@end menu

@node Finding messages
@section Finding messages
Now we are ready to retrieve some messages from the system. There are two main
procedures to do this:

@itemize
@item @code{(mu:message-list [<search-expression>])}
@item @code{(mu:for-each-message <procedure> [<search-expression>])}
@end itemize

@noindent
The first procedure, @code{mu:message-list} returns a list of all messages
matching @t{<search-expression>}; if you leave @t{<search-expression>} out, it
returns @emph{all} messages. For example, to get all messages with @t{coffee}
in the subject line:

@verbatim
scheme@(guile-user)> (mu:message-list "subject:coffee")
$1 = (#<<mu:message> 9040640> #<<mu:message> 9040630>
      #<<mu:message> 9040570>)
@end verbatim

@noindent
Apparently, we have three messages matching @t{subject:coffee}, so we get a
list of three @code{<mu:message>} objects. Let's just use the
@code{mu:subject} procedure ('method') provided by @code{<mu:message>} objects
to retrieve the subject-field (more about methods in the next section).

For your convenience, @t{guile} has saved the result of our last query in a
variable called @t{$1}, so to get the subject of the first message in the
list, we can do:

@verbatim
scheme@(guile-user)> (mu:subject (car $1))
$2 = "Re: best coffee ever!"
@end verbatim

@noindent
The second procedure we mentioned, @code{mu:for-each-message}, executes some
procedure for each message matched by the search expression (or @emph{all}
messages if the search expression is omitted):

@verbatim
scheme@(guile-user)> (mu:for-each-message
                       (lambda(msg)
                         (display (mu:subject msg))
                         (newline))
                      "subject:coffee")
Re: best coffee ever!
best coffee ever!
Coffee beans
scheme@(guile-user)>
@end verbatim

@noindent
Using @code{mu:message-list} and/or
@code{mu:for-each-message}@footnote{Implementation node:
@code{mu:message-list} is implemented in terms of @code{mu:for-each-message},
not the other way around. Due to the way @t{mu} works,
@code{mu:for-each-message} is rather more efficient than a combination of
@code{for-each} and @code{mu:message-list}} and a couple of @t{<mu:message>}
methods, together with what Guile/Scheme provides, should allow for many
interesting programs.

@node Message methods
@section Message methods

Now that we've seen how to retrieve lists of message objects
(@code{<mu:message>}), let's see what we can do with such an object.

@code{<mu:message>} defines the following methods that all take a single
@code{<mu:message>} object as a parameter. We won't go into the exact meanings
for all of these procedures here - for the details about various flags /
properties, please refer to the @t{mu-find} man-page.

@itemize
@item @code{(mu:bcc msg)}: the @t{Bcc} field of the message, or @t{#f} if there is none
@item @code{(mu:body-html msg)}: : the html body of the message, or @t{#f} if there is none
@item @code{(mu:body-txt msg)}: the plain-text body of the message, or @t{#f} if there is none
@item @code{(mu:cc msg)}: the @t{Bcc} field of the message, or @t{#f} if there is none
@item @code{(mu:date msg)}: the @t{Date} field of the message, or 0 if there is none
@item @code{(mu:flags msg)}: list of message-flags for this message
@item @code{(mu:from msg)}: the @t{From} field of the message, or @t{#f} if there is none
@item @code{(mu:maildir msg)}: the maildir this message lives in, or @t{#f} if there is none
@item @code{(mu:message-id msg)}: the @t{Message-Id} field of the message, or @t{#f} if there is none
@item @code{(mu:path msg)}: the file system path for this message
@item @code{(mu:priority msg)}: the priority of this message (either @t{mu:prio:low}, @t{mu:prio:normal} or @t{mu:prio:high}
@item @code{(mu:references msg)}: the list of messages (message-ids) this message
refers to in(mu: the @t{References:} header
@item @code{(mu:size msg)}: size of the message in bytes
@item @code{(mu:subject msg)}: the @t{Subject} field of the message, or @t{#f} if there is none.
@item @code{(mu:tags msg)}: list of tags for this message
@item @code{(mu:timestamp msg)}: the timestamp (mtime) of the message file, or
#f if there is none.
message file
@item @code{(mu:to msg)}: the sender of the message, or @t{#f} if there is none
@end itemize

With these methods, we can query messages for their properties; for example:

@verbatim
scheme@(guile-user)> (define msg (car (mu:message-list "snow")))
scheme@(guile-user)> (mu:subject msg)
$1 = "Re: Running in the snow is beautiful"
scheme@(guile-user)> (mu:flags msg)
$2 = (mu:flag:replied mu:flag:seen)
scheme@(guile-user)> (strftime "%F" (localtime (mu:date msg)))
$3 = "2011-01-15"
@end verbatim

There are a couple more methods:
@itemize
@item @code{(mu:header msg "<header-name>")} returns an arbitrary message
header (or @t{#f} if not found) -- e.g. @code{(header msg "User-Agent")}
@item If you include the @t{mu contact} module, the @code{(mu:contacts
msg [contact-type])} method (to get a list of contacts) is
added. @xref{Contacts}.
@item If you include the @t{mu part} module, the @code{((mu:parts msg)} and
@code{(mu:attachments msg)} methods are added. @xref{Attachments and other parts}.
@end itemize

@node Example - the longest subject
@section Example - the longest subject

Now, let's write a little example -- let's find out what is the @emph{longest
subject} of any e-mail messages we received in the year 2011.  You can try
this if you put the following in a separate file, make it executable, and run
it like any program.

@lisp
#!/bin/sh
exec guile -s $0 $@
!#

(use-modules (mu))
(use-modules (srfi srfi-1))

(mu:initialize)

;; note: (subject msg) => #f if there is no subject
(define list-of-subjects
  (map (lambda (msg)
      (or (mu:subject msg) "")) (mu:message-list "date:2011..2011")))
;; see the mu-find manpage for the date syntax

(define longest-subject
  (fold (lambda (subj1 subj2)
      (if (> (string-length subj1) (string-length subj2))
	subj1 subj2))
    "" list-of-subjects))

(format #t "Longest subject: ~s\n" longest-subject)
@end lisp

There are many other ways to solve the same problem, for example by using an
iterative approach with @code{mu:for-each-message}, but it should show how one
can easily write little programs to answer specific questions about your
e-mail corpus.

@node Contacts
@chapter Contacts

We can retrieve the sender and recipients of an e-mail message using methods
like @code{mu:from}, @code{mu:to} etc.; @xref{Message methods}. These
procedures return the list of recipients as a single string; however, often it
is more useful to deal with recipients as separate objects.

@menu
* Contact procedures and objects::
* All contacts::
* Utility procedures::
* Example - mutt export::
@end menu


@node Contact procedures and objects
@section Contact procedures and objects

Message objects (@pxref{Messages}) have a method @t{mu:contacts}:

    @code{(mu:contacts <message-object> [<contact-type>])}

The @t{<contact-type>} is a symbol, one of @code{mu:to}, @code{mu:from},
@code{mu:cc} or @code{mu:bcc}. This will then get the contact objects for the
contacts of the corresponding type. If you leave out the contact-type (or
specify @t{#t} for it, you will get a list of @emph{all} contact objects for
the message.

A contact object (@code{<mu:contact>}) has two methods:
@itemize
@item @code{mu:name} returns the name of the contact, or #f if there is none
@item @code{mu:email} returns the e-mail address of the contact, or #f if there is none
@end itemize

Let's get a list of all names and e-mail addresses in the 'To:' field, of
messages matching 'book':

@lisp
(use-modules (mu))
(mu:initialize)
(mu:for-each-message
   (lambda (msg)
      (for-each
        (lambda (contact)
           (format #t "~a => ~a\n"
             (or (mu:email contact) "") (or (mu:name contact) "no-name")))
        (mu:contacts msg mu:contact:to)))
    "book")
@end lisp

This shows what the methods do, but for many uses, it would be more useful to
have each of the contacts only show up @emph{once} - for that, please refer to
@xref{All contacts}.

@node All contacts
@section All contacts

Sometimes you may want to inspect @emph{all} the different contacts in the
@t{mu} database. This is useful, for instance, when exporting contacts to some
external format that can then be important in an e-mail program.

To enable this, there is the procedure @code{mu:for-each-contact}, defined as

   @code{(mu:for-each-contact procedure [search-expression])}.

This will aggregate the unique contacts from @emph{all} messages matching
@t{<search-expression>} (when it is left empty, it will match all messages in
the database), and execute @t{procedure} for each of them.

The @t{procedure} receives an object of the type @t{<mu:contact-with-stats>},
which is a @emph{subclass} of the @t{<mu:contact>} class discussed in
@xref{Contact procedures and objects}. @t{<mu:contact-with-stats>} objects
expose the following additional methods:

@itemize
@item @code{(mu:frequency <contact>)}: returns the @emph{number of times} this contact occurred in
one of the address fields
@item @code{(mu:last-seen <contact>)}: returns the @emph{most recent time} the contact was
seen in one of the address fields, as a @t{time_t} value
@end itemize

The method assumes an e-mail address is unique for a certain contact; if a
certain e-mail address occurs with different names, it uses the most recent
non-empty name.

@node Utility procedures
@section Utility procedures

To make dealing with contacts even easier, there are a number of utility
procedures that can save you a bit of typing.

For converting contacts to some textual form, there is @code{(mu:contact->string
<mu:contact> format)}, which takes a contact and returns a text string with
the given format. Currently supported formats are @t{"org-contact}, @t{"mutt-alias"},
@t{"mutt-ab"}, @t{"wanderlust"} and @t{"plain"}.


@node Example - mutt export
@section Example - mutt export

Let's see how we could export the addresses in the @t{mu} database to the
addressbook format of the venerable
@t{mutt}@footnote{@url{http://www.mutt.org/}} e-mail client.

The addressbook format that @t{mutt} uses is a sequence of lines that look
something like:
@verbatim
alias <nick> [<name>] "<" <email> ">"
@end verbatim

@t{mu guile} provides the procedure @code{(mu:contact->string <mu:contact>
format)} that we can use to do the conversion.

We may want to focus on people with whom we have frequent correspondence; so
we may want to limit ourselves to people we have seen at least 10 times in the
last year.

It is a bit hard to @emph{guess} the nick name for e-mail contacts, but
@code{mu:contact->string} tries something based on the name. You can always
adjust them later by hand, obviously.

@lisp
#!/bin/sh
exec guile -s $0 $@
!#

(use-modules (mu))
(mu:initialize)

;; Get a list of contacts that were seen at least 20 times since 2010
(define (selected-contacts)
  (let  ((addrs '())
	  (start (car (mktime (car (strptime "%F" "2010-01-01")))))
	  (minfreq 20))
    (mu:for-each-contact
      (lambda (contact)
	(if (and (mu:email contact)
	      (>= (mu:frequency contact) minfreq)
	      (>= (mu:last-seen contact) start))
	  (set! addrs (cons contact addrs)))))
      addrs))

(for-each
  (lambda (contact)
    (format #t "~a\n" (mu:contact->string contact "mutt-alias")))
  (selected-contacts))
@end lisp

This simple program could be improved in many ways; this is left as an
exercise to the reader.

@node Attachments and other parts
@chapter Attachments and other parts

To deal with @emph{attachments}, or, more in general @emph{MIME-parts}, there
is the @t{mu part} module.

@menu
* Parts and their methods::
* Attachment example::
@end menu

@node Parts and their methods
@section Parts and their methods
The module defines the @code{<mu-part>} class, and adds two methods to
@code{<mu:message>} objects:
@itemize
@item @code{(mu:parts msg)} - returns a list @code{<mu-part>} objects, one for
each MIME-parts in the message.
@item @code{(mu:attachments msg)} - like @code{parts}, but only list those MIME-parts
that look like proper attachments.
@end itemize

A @code{<mu:part>} object exposes a few methods to get information about the
part:
@itemize
@item @code{(mu:name <part>)} - returns the file name of the mime-part, or @code{#f} if
there is none.
@item @code{(mu:mime-type <part>)} - returns the mime-type of the mime-part, or @code{#f}
if there is none.
@item @code{(mu:size <part>)} - returns the size in bytes of the mime-part
@end itemize

@c Then, we may want to save the part to a file; this can be done using either:
@c @itemize
@c @item @code{(mu:save part <part>)} - save a part to a temporary file, return the file
@c name@footnote{the temporary filename is a predictable procedure of (user-id,
@c msg-path, part-index)}
@c @item @code{(mu:save-as <part> <path>)} - save part to file at path
@c @end itemize

@node Attachment example
@section Attachment example

Let's look at some small example. Let's get a list of the biggest attachments
in messages about Luxemburg:

@lisp
#!/bin/sh
exec guile -s $0 $@
!#

(use-modules (mu))
(mu:initialize)

(define (all-attachments expr)
  "Return a list of (name . size) for all attachments in messages
matching EXPR."
  (let ((pairs '()))
    (mu:for-each-message
      (lambda (msg)
	(for-each
	  (lambda (att) ;; add (filename . size) to the list
	    (set! pairs (cons (cons (mu:name att) (or (mu:size att) 0)) pairs)))
	  (mu:attachments msg)))
      expr)
    pairs))

(for-each
  (lambda (att)
    (format #t "~a: ~,1fKb\n"
      (car att) (exact->inexact (/ (cdr att) 1024))))
  (sort (all-attachments "Luxemburg")
    (lambda (att1 att2)
      (< (cdr att1) (cdr att2)))))
@end lisp

As an exercise for the reader, you might want to re-rewrite the
@code{all-attachments} in terms of @code{mu:message-list}, which would
probably be a bit more elegant.


@node Statistics
@chapter Statistics

@t{mu-guile} offers some convenience procedures to determine various statistics
about the messages in the database.

@menu
* Basics:: @code{mu:count}, @code{mu:average}, ...
* Tabulating values:: @code{mu:tabulate}
* Most frequent values:: @code{mu:top-n-most-frequent}
@end menu

@node Basics
@section Basics

Let's look at some of the basic statistical operations available, in an
interactive session:
@example
GNU Guile 2.0.5.123-4bd53
Copyright (C) 1995-2012 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@@(guile-user)> ;; load modules, initialize mu
scheme@@(guile-user)> (use-modules (mu) (mu stats))
scheme@@(guile-user)> (mu:initialize)
scheme@@(guile-user)>
scheme@@(guile-user)> ;; count the number of messages with 'hello' in their subject
scheme@@(guile-user)> (mu:count "subject:hello")
$1 = 162
scheme@@(guile-user)> ;; average the size of messages with hello in their subject
scheme@@(guile-user)> (mu:average mu:size "subject:hello")
$2 = 34597733/81
scheme@@(guile-user)> (exact->inexact $2)
$3 = 427132.506172839
scheme@@(guile-user)> ;; calculate the correlation between message size and
scheme@@(guile-user)> ;; subject length
scheme@@(guile-user)> (mu:correl mu:size (lambda (msg)
                        (string-length (mu:subject msg))) "subject:hello")
$5 = -0.10804368622292
scheme@@(guile-user)>
@end example

@node Tabulating values
@section Tabulating values

@code{(mu:tabulate <procedure> [<search-expr>])} applies @t{<procedure>} to each
message matching @t{<search-expr>} (leave empty to match @emph{all} messages),
and returns a associative list (a list of pairs) with each of the different
results of @t{<procedure>} and their frequencies. For fields that contain lists
of values (such as address-fields), each of the values in the list is added
separately.

@subsection Example: messages per weekday

We demonstrate @code{mu:tabulate} with an example. Suppose we want to know how
many messages we receive per weekday:

@lisp
#!/bin/sh
exec guile -s $0 $@
!#

(use-modules (mu) (mu stats) (mu plot))
(mu:initialize)

;; create a list like (("Sun" . 13) ("Mon" . 23) ...)
(define weekday-table
  (mu:weekday-numbers->names
    (sort
      (mu:tabulate
	(lambda (msg)
	  (tm:wday (localtime (mu:date msg)))))
      (lambda (a b) (< (car a) (car b))))))

(for-each
  (lambda (elm)
    (format #t "~a: ~a\n" (car elm) (cdr elm)))
  weekday-table)
@end lisp


The procedure @code{weekday-table} uses @code{mu:tabulate-message} to get the
frequencies per hour -- this returns a list of pairs:
@verbatim
((5 . 2339) (0 . 2278) (4 . 2800) (2 . 3184) (6 . 1856) (3 . 2833) (1 . 2993))
@end verbatim

We sort these pairs by the day number, and then apply
@code{mu:weekday-numbers->names}, which takes the list, and returns a list
where the day numbers are replace by there abbreviated name (in the current
locale). Note, there is also @code{mu:month-numbers->names}.

The script then outputs these numbers in the following form:

@verbatim
Sun: 2278
Mon: 2993
Tue: 3184
Wed: 2833
Thu: 2800
Fri: 2339
Sat: 1856
@end verbatim

Clearly, Saturday is a slow day for e-mail...

@node Most frequent values
@section Most frequent values

In the above example, the number of values is small (the seven weekdays);
however, in many cases there can be many different values (for example, all
different message subjects), many of which may not be very interesting -- all
we need to know is the top-10 of most frequently seen values.

This is fairly easy to achieve using @code{mu:tabulate} -- to get the top-10
subjects@footnote{this requires the @code{(srfi srfi-1)}-module}, we can use
something like this:
@lisp
(take
  (sort
    (mu:tabulate mu:subject)
    (lambda (a b) (> (cdr a) (cdr b))))
  10)
@end lisp

If this is not short enough, @t{mu-guile} offers a convenience procedure to do
this: @code{mu:top-n-most-frequent}. For example, to get the top-10 people we
sent mail to most often:

@lisp
(mu:top-n-most-frequent mu:to 10 "maildir:/sent")
@end lisp

Can't make it much easier than that!


@node Plotting data
@chapter Plotting data

You can plot the results in the format produced by @code{mu:tabulate} with the
@t{(mu plot)} module, an experimental module that requires the
@t{gnuplot}@footnote{@url{http://www.gnuplot.info/}} program to be installed
on your system.

The @code{mu:plot-histogram} procedure takes the following arguments:

@code{(mu:plot-histogram <data> <title> <x-label> <y-label> [<want-ascii>])}

Here, @code{<data>} is a table of data in the format that @code{mu:tabulate}
produces. @code{<title>}, @code{<x-label>} and @code{<y-lablel>} are,
respectively, the title of the graph, and the labels for X- and
Y-axis. Finally, if you pass @t{#t} for the final @code{<want-ascii>}
parameter, a plain-text rendering of the graph will be produced; otherwise, a
graphical window will be shown.

An example should clarify how this works in practice; let's plot the number of
message per hour:

@lisp
#!/bin/sh
exec guile -s $0 $@
!#

(use-modules (mu) (mu stats) (mu plot))
(mu:initialize)

(define (mail-per-hour-table)
  (sort
    (mu:tabulate
      (lambda (msg)
	(tm:hour (localtime (mu:date msg)))))
    (lambda (x y) (< (car x) (car y)))))

(mu:plot-histogram (mail-per-hour-table) "Mail per hour" "Hour" "Frequency")
@end lisp

@cartouche
@verbatim
				 Mail per hour
Frequency
  1200 ++--+--+--+--+-+--+--+--+--+-+--+--+--+-+--+--+--+--+-+--+--+--+--++
       |+  +  +	 +  + +	 + "/tmp/fileHz7D2u" using 2:xticlabels(1) ********
  1100 ++							     *** +*
       ****							     * *  *
  1000 *+ *							  **** * +*
       *  *			      ******  ****		  * ** *  *
   900 *+ *			      * ** ****	 *	       **** ** * +*
       *  *			      * ** *  *	 *********     * ** ** *  *
   800 *+ *			   **** ** *  *	 *  * ** *     * ** ** * +*
   700 *+ ***			****  * ** *  *	 *  * ** ****  * ** ** * +*
       *  * *		     ****  *  * ** *  *	 *  * ** *  **** ** ** *  *
   600 *+ * ****	     *	*  *  * ** *  *	 *  * ** *  *  * ** ** * +*
       *  * ** *	     *	*  *  * ** *  *	 *  * ** *  *  * ** ** *  *
   500 *+ * ** *	     *	*  *  * ** *  *	 *  * ** *  *  * ** ** * +*
       *  * ** ****	   ***	*  *  * ** *  *	 *  * ** *  *  * ** ** *  *
   400 *+ * ** ** ****	   * *	*  *  * ** *  *	 *  * ** *  *  * ** ** * +*
       *+ *+**+**+* +*******+* +* +*+ *+**+* +*+ *+ *+**+* +*+ *+**+**+* +*
   300 ********************************************************************
	0  1  2	 3  4 5	 6  7  8  910 11 12 1314 15 16 17 1819 20 21 22 23
                                       Hour
@end verbatim
@end cartouche

@node Writing scripts
@chapter Writing scripts

The @t{mu} program has built-in support for running guile-scripts, and comes
with a number of examples.

You can get a list of all scripts with the @t{mu script} command:
@verbatim
$ mu script
Available scripts (use --verbose for details):
  * find-dups: find duplicate messages
  * msgs-count: count the number of messages matching some query
  * msgs-per-day: graph the number of messages per day
  * msgs-per-hour: graph the number of messages per hour
  * msgs-per-month: graph the number of messages per month
  * msgs-per-year: graph the number of messages per year
  * msgs-per-year-month: graph the number of messages per year-month
@end verbatim

You can then execute such a script by its name:
@verbatim
$ mu msgs-per-month --textonly --query=hello


                        Messages per month matching hello

  240 ++-+-----+----+-----+-----+-----+----+-----+-----+-----+----+-----+-++
      |  +     +    +     + "/tmp/filewi9H0N" using 2:xticlabels(1) ****** |
  220 ++                                       *    *                 ******
      |                                        *    *                 *    *
  200 ++                                       *    *                 *   +*
      |                                        *    *                 *    *
  180 ++                     ******            *    *                 *   +*
      |                      *    *            *    *                 *    *
  160 ******                 *    *            *    *                 *   +*
      *    *                 *    *            *    *                 *    *
      *    *******           *    *            *    *     ******      *    *
  140 *+   **    *           *    *            *    *     *    ********   +*
      *    **    *******     *    *            *    *     *    **    **    *
  120 *+   **    **    *******    *            *    *     *    **    **   +*
      *    **    **    **    *    *            *    *******    **    **    *
  100 *+   **    **    **    *    *            *    *    **    **    **   +*
      *  + **  + ** +  ** +  *  + *   +    +   * +  *  + **  + ** +  ** +  *
   80 **********************************************************************
        Jan   Feb  Mar   Apr   May   Jun  Jul   Aug   Sep   Oct  Nov   Dec
                                      Month
@end verbatim

Please refer to the @t{mu-script} man-page for some details on writing your
own scripts.


@node Recipes
@appendix Recipes

@itemize
@item Calculating the average length of subject-lines
@lisp
;; the average length of all our
(let ((len 0) (n 0))
  (mu:for-each-message
    (lambda (msg)
      (set! len (+ len (string-length (or (mu:subject msg) ""))))
      (set! n (+ n 1))))
  (if (= n 0) 0 (/ len n)))
  ;; this gives a rational, exact result;
  ;; use exact->inexact to get decimals

;; we we can make this short with the mu:average (with (mu stats))
(mu:average (lambda (msg) (string-length (or (mu:subject msg) ""))))


@end lisp
@end itemize

@node GNU Free Documentation License
@appendix GNU Free Documentation License

@include fdl.texi
@bye
