2012-07-20 11:20:03 +02:00
|
|
|
\input texinfo.tex @c -*-texinfo-*-
|
2012-01-01 21:45:47 +01:00
|
|
|
@c %**start of header
|
|
|
|
@setfilename mu-guile.info
|
|
|
|
@settitle mu-guile user manual
|
|
|
|
|
2012-10-19 15:00:41 +02:00
|
|
|
@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
|
2012-10-20 11:51:19 +02:00
|
|
|
|
|
|
|
@include texi.texi
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
@copying
|
|
|
|
Copyright @copyright{} 2012 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
|
|
|
|
|
2012-10-19 15:00:41 +02:00
|
|
|
@titlepage
|
|
|
|
@title @t{mu-guile} - extending @t{mu} with Guile Scheme
|
|
|
|
@subtitle{version @value{mu-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
|
2012-01-01 21:45:47 +01:00
|
|
|
@node Top
|
2012-10-19 15:00:41 +02:00
|
|
|
@top mu-guile manual
|
|
|
|
@end ifnottex
|
|
|
|
|
|
|
|
@iftex
|
|
|
|
@node Welcome to mu-guile
|
|
|
|
@unnumbered Welcome to mu-guile
|
|
|
|
@end iftex
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
Welcome to @t{mu-guile}!
|
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@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
|
2013-06-09 11:11:01 +02:00
|
|
|
statistics, you need some more power.
|
2012-10-20 11:17:19 +02:00
|
|
|
|
|
|
|
@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 it's @emph{fun}!
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
@menu
|
2012-01-04 23:08:50 +01:00
|
|
|
* Getting started::
|
2012-01-06 15:18:18 +01:00
|
|
|
* Initializing mu-guile::
|
2012-01-04 23:08:50 +01:00
|
|
|
* Messages::
|
|
|
|
* Contacts::
|
2012-01-12 23:53:05 +01:00
|
|
|
* Attachments and other parts::
|
|
|
|
* Statistics::
|
|
|
|
* Plotting data::
|
2013-06-09 11:11:01 +02:00
|
|
|
* Writing scripts::
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
Appendices
|
|
|
|
|
|
|
|
* GNU Free Documentation License:: The license of this manual.
|
|
|
|
@end menu
|
|
|
|
|
2012-01-04 23:08:50 +01:00
|
|
|
@node Getting started
|
|
|
|
@chapter Getting started
|
2012-01-01 21:45:47 +01:00
|
|
|
|
2012-01-04 23:08:50 +01:00
|
|
|
@menu
|
|
|
|
* Installation::
|
2012-10-20 11:17:19 +02:00
|
|
|
* Making sure it works::
|
2012-01-04 23:08:50 +01:00
|
|
|
@end menu
|
2012-01-01 21:45:47 +01:00
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
This chapter walks you through the installation and the basic setup.
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
@node Installation
|
2012-01-04 23:08:50 +01:00
|
|
|
@section Installation
|
2012-01-01 21:45:47 +01:00
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@t{mu-guile} is part of @t{mu} - by installing the latter, the former is
|
2013-06-09 11:11:01 +02:00
|
|
|
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.
|
2012-01-01 21:45:47 +01:00
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@subsection Guile 2.x
|
|
|
|
|
|
|
|
@t{mu-guile} is built automatically when @t{mu} is built, if you have
|
2013-06-09 11:11:01 +02:00
|
|
|
@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.
|
2012-10-20 11:17:19 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
At the time of writing, there are no official packages for
|
2012-10-20 11:17:19 +02:00
|
|
|
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:
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
@example
|
2012-10-20 11:17:19 +02:00
|
|
|
$ sudo yum install gnuplot
|
2012-01-01 21:45:47 +01:00
|
|
|
@end example
|
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@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.
|
|
|
|
|
2012-01-01 21:45:47 +01:00
|
|
|
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
|
2012-10-20 11:17:19 +02:00
|
|
|
@t{configure} again. After a succesfull @t{./configure}, we can make and
|
|
|
|
install the package:
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
@example
|
|
|
|
$ make && sudo make install
|
|
|
|
@end example
|
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@subsection mu-guile
|
2012-01-01 21:45:47 +01:00
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
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:
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
@example
|
|
|
|
guile -c '(display %load-path)(newline)'
|
|
|
|
@end example
|
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
If necessary, you can add the @t{%load-path} by adding to your
|
|
|
|
@file{~/.guile}:
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
@lisp
|
|
|
|
(set! %load-path (cons "/usr/local/share/guile/site/2.0" %load-path))
|
|
|
|
@end lisp
|
|
|
|
|
2012-01-19 20:41:11 +01:00
|
|
|
Or, alternatively, you can set @t{GUILE_LOAD_PATH}:
|
|
|
|
@example
|
|
|
|
export GUILE_LOAD_PATH="/usr/local/share/guile/site/2.0"
|
|
|
|
@end example
|
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
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!
|
2012-01-19 20:41:11 +01:00
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@node Making sure it works
|
|
|
|
@section Making sure it works
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
Assuming @t{mu-guile} has been installed correctly (@ref{Installation}), and
|
2012-01-01 21:46:11 +01:00
|
|
|
also assuming that you have already indexed your e-mail messages (if
|
2012-01-01 21:45:47 +01:00
|
|
|
necessary, see the @t{mu-index} man-page), we are ready to start @t{mu-guile};
|
|
|
|
a session may look something like this:
|
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@cartouche
|
2012-01-01 21:45:47 +01:00
|
|
|
@verbatim
|
2012-10-20 11:17:19 +02:00
|
|
|
GNU Guile 2.0.5.123-4bd53
|
|
|
|
Copyright (C) 1995-2012 Free Software Foundation, Inc.
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
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
|
2012-10-20 11:17:19 +02:00
|
|
|
@end cartouche
|
2012-01-01 21:45:47 +01:00
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@noindent
|
|
|
|
Now, copy-paste the following after the prompt:
|
2012-01-01 21:45:47 +01:00
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@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
|
2012-01-01 21:45:47 +01:00
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@noindent
|
|
|
|
After pressing @key{Enter}, you should get a list of all subjects of messages
|
2013-06-09 11:11:01 +02:00
|
|
|
that match @t{hello}:
|
2012-01-01 21:45:47 +01:00
|
|
|
|
|
|
|
@verbatim
|
2012-10-20 11:17:19 +02:00
|
|
|
...
|
|
|
|
Subject: RE: The Bird Serpent War Cataclysm
|
|
|
|
Subject: Hello!
|
|
|
|
Subject: Re: post-run tomorrow
|
|
|
|
Subject: When all is lost
|
|
|
|
...
|
2012-01-01 21:45:47 +01:00
|
|
|
@end verbatim
|
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
@noindent
|
|
|
|
If all this works, congratulations! @t{mu-guile} is installed now, ready to
|
2013-06-09 11:11:01 +02:00
|
|
|
serve your every searching need!
|
2012-01-15 13:38:08 +01:00
|
|
|
|
2012-01-06 15:18:18 +01:00
|
|
|
@node Initializing mu-guile
|
|
|
|
@chapter Initializing mu-guile
|
2012-01-04 23:08:50 +01:00
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
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}.
|
2012-10-20 11:17:19 +02:00
|
|
|
|
2012-10-19 15:00:41 +02:00
|
|
|
It is possible to write separate programs with @t{mu-guile}, but for now we'll
|
2012-10-27 13:58:21 +02:00
|
|
|
do things @emph{interactively}, that is, from the Guile-prompt
|
|
|
|
(``@abbr{REPL}'').
|
2012-01-04 23:08:50 +01:00
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
As we have seen, we start our @t{mu-guile} session by starting @t{guile}:
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
@verbatim
|
|
|
|
$ guile
|
2012-10-20 11:17:19 +02:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
@cartouche
|
|
|
|
@verbatim
|
2012-10-19 15:00:41 +02:00
|
|
|
GNU Guile 2.0.5.123-4bd53
|
|
|
|
Copyright (C) 1995-2012 Free Software Foundation, Inc.
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
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.
|
2013-06-09 11:11:01 +02:00
|
|
|
scheme@(guile-user)>
|
2012-01-04 23:08:50 +01:00
|
|
|
@end verbatim
|
2012-10-19 15:00:41 +02:00
|
|
|
@end cartouche
|
2012-01-04 23:08:50 +01:00
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
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
|
2012-10-20 11:17:19 +02:00
|
|
|
graph plotting functionality in @t{(mu plot)}@footnote{@code{(mu plot)}
|
2012-10-27 13:58:21 +02:00
|
|
|
requires the @t{gnuplot} program}. Let's load all of them:
|
2012-01-04 23:08:50 +01:00
|
|
|
@verbatim
|
2012-07-15 11:44:52 +02:00
|
|
|
scheme@(guile-user)> (use-modules (mu) (mu stats) (mu plot))
|
2012-01-04 23:08:50 +01:00
|
|
|
@end verbatim
|
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
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
|
2012-10-27 13:58:21 +02:00
|
|
|
system:
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
@verbatim
|
2012-01-06 13:33:06 +01:00
|
|
|
scheme@(guile-user)> (mu:initialize)
|
2012-01-04 23:08:50 +01:00
|
|
|
@end verbatim
|
|
|
|
|
2012-10-20 11:17:19 +02:00
|
|
|
This opens the database for reading, using the default location of
|
2012-10-27 13:58:21 +02:00
|
|
|
@file{~/.mu}@footnote{If you keep your @t{mu} database in a non-standard
|
|
|
|
place, use @code{(mu:initialize "/path/to/my/mu/")}}
|
2013-06-09 11:11:01 +02:00
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
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.
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
@node Messages
|
|
|
|
@chapter Messages
|
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
In this chapter, we discuss searching messages and doing things with them.
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
@menu
|
2012-10-27 13:58:21 +02:00
|
|
|
* Finding messages:: query for messages in the databse
|
|
|
|
* Message methods:: what methods are available for messages?
|
|
|
|
* Example - the longest subject:: find the messages with the longest subject
|
2012-01-04 23:08:50 +01:00
|
|
|
@end menu
|
|
|
|
|
|
|
|
@node Finding messages
|
|
|
|
@section Finding messages
|
2012-01-15 13:38:08 +01:00
|
|
|
Now we are ready to retrieve some messages from the system. There are two main
|
2013-06-09 11:11:01 +02:00
|
|
|
procedures to do this:
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
@itemize
|
2012-01-06 13:33:06 +01:00
|
|
|
@item @code{(mu:message-list [<search-expression>])}
|
2013-06-09 11:11:01 +02:00
|
|
|
@item @code{(mu:for-each-message <procedure> [<search-expression>])}
|
2012-01-04 23:08:50 +01:00
|
|
|
@end itemize
|
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
@noindent
|
2013-06-09 11:11:01 +02:00
|
|
|
The first procedure, @code{mu:message-list} returns a list of all messages
|
2012-01-04 23:08:50 +01:00
|
|
|
matching @t{<search-expression>}; if you leave @t{<search-expression>} out, it
|
2012-10-27 13:58:21 +02:00
|
|
|
returns @emph{all} messages. For example, to get all messages with @t{coffee}
|
|
|
|
in the subject line:
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
@verbatim
|
|
|
|
scheme@(guile-user)> (mu:message-list "subject:coffee")
|
2012-01-15 13:38:08 +01:00
|
|
|
$1 = (#<<mu:message> 9040640> #<<mu:message> 9040630>
|
|
|
|
#<<mu:message> 9040570>)
|
2012-01-04 23:08:50 +01:00
|
|
|
@end verbatim
|
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
@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
|
2013-06-09 11:11:01 +02:00
|
|
|
@code{mu:subject} procedure ('method') provided by @code{<mu:message>} objects
|
2012-10-27 13:58:21 +02:00
|
|
|
to retrieve the subject-field (more about methods in the next section).
|
2012-01-04 23:08:50 +01:00
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
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:
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
@verbatim
|
2012-01-15 13:38:08 +01:00
|
|
|
scheme@(guile-user)> (mu:subject (car $1))
|
2012-01-04 23:08:50 +01:00
|
|
|
$2 = "Re: best coffee ever!"
|
|
|
|
@end verbatim
|
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
@noindent
|
2013-06-09 11:11:01 +02:00
|
|
|
The second procedure we mentioned, @code{mu:for-each-message}, executes some
|
|
|
|
procedure for each message matched by the search expression (or @emph{all}
|
2012-10-27 13:58:21 +02:00
|
|
|
messages if the search expression is omitted):
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
@verbatim
|
|
|
|
scheme@(guile-user)> (mu:for-each-message
|
|
|
|
(lambda(msg)
|
2012-07-10 22:36:21 +02:00
|
|
|
(display (mu:subject msg))
|
2012-01-04 23:08:50 +01:00
|
|
|
(newline))
|
|
|
|
"subject:coffee")
|
|
|
|
Re: best coffee ever!
|
|
|
|
best coffee ever!
|
2012-01-06 15:18:18 +01:00
|
|
|
Coffee beans
|
2012-01-04 23:08:50 +01:00
|
|
|
scheme@(guile-user)>
|
|
|
|
@end verbatim
|
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
@noindent
|
2012-01-04 23:08:50 +01:00
|
|
|
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,
|
2012-01-15 13:38:08 +01:00
|
|
|
@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
|
2012-01-04 23:08:50 +01:00
|
|
|
interesting programs.
|
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
@node Message methods
|
|
|
|
@section Message methods
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
Now that we've seen how to retrieve lists of message objects
|
2012-01-15 13:38:08 +01:00
|
|
|
(@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
|
2013-06-09 11:11:01 +02:00
|
|
|
for all of these procedures here - for the details about various flags /
|
2012-01-04 23:08:50 +01:00
|
|
|
properties, please refer to the @t{mu-find} man-page.
|
|
|
|
|
|
|
|
@itemize
|
2012-01-15 13:38:08 +01:00
|
|
|
@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
|
2012-07-10 22:36:21 +02:00
|
|
|
@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
|
2012-01-04 23:08:50 +01:00
|
|
|
@end itemize
|
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
With these methods, we can query messages for their properties; for example:
|
2012-01-04 23:08:50 +01:00
|
|
|
|
|
|
|
@verbatim
|
|
|
|
scheme@(guile-user)> (define msg (car (mu:message-list "snow")))
|
2012-01-15 13:38:08 +01:00
|
|
|
scheme@(guile-user)> (mu:subject msg)
|
2012-01-04 23:08:50 +01:00
|
|
|
$1 = "Re: Running in the snow is beautiful"
|
2012-01-15 13:38:08 +01:00
|
|
|
scheme@(guile-user)> (mu:flags msg)
|
|
|
|
$2 = (mu:flag:replied mu:flag:seen)
|
|
|
|
scheme@(guile-user)> (strftime "%F" (localtime (mu:date msg)))
|
2012-01-04 23:08:50 +01:00
|
|
|
$3 = "2011-01-15"
|
|
|
|
@end verbatim
|
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
There are a couple more methods:
|
2012-01-04 23:08:50 +01:00
|
|
|
@itemize
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:header msg "<header-name>")} returns an arbitrary message
|
2012-01-04 23:08:50 +01:00
|
|
|
header (or @t{#f} if not found) -- e.g. @code{(header msg "User-Agent")}
|
2012-01-15 13:38:08 +01:00
|
|
|
@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}.
|
2012-01-04 23:08:50 +01:00
|
|
|
@end itemize
|
|
|
|
|
2012-01-09 07:23:11 +01:00
|
|
|
@node Example - the longest subject
|
|
|
|
@section Example - the longest subject
|
|
|
|
|
2012-01-06 13:33:06 +01:00
|
|
|
Now, let's write a little example -- let's find out what is the @emph{longest
|
2012-01-15 13:38:08 +01:00
|
|
|
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.
|
2012-01-06 13:33:06 +01:00
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
@lisp
|
2012-01-06 13:33:06 +01:00
|
|
|
#!/bin/sh
|
2012-01-09 07:23:11 +01:00
|
|
|
exec guile -s $0 $@
|
2012-01-06 13:33:06 +01:00
|
|
|
!#
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
(use-modules (mu))
|
2012-01-09 07:23:11 +01:00
|
|
|
(use-modules (srfi srfi-1))
|
|
|
|
|
|
|
|
(mu:initialize)
|
2012-01-06 13:33:06 +01:00
|
|
|
|
2012-01-09 07:23:11 +01:00
|
|
|
;; note: (subject msg) => #f if there is no subject
|
|
|
|
(define list-of-subjects
|
|
|
|
(map (lambda (msg)
|
2012-01-15 13:38:08 +01:00
|
|
|
(or (mu:subject msg) "")) (mu:message-list "date:2011..2011")))
|
2012-01-09 07:23:11 +01:00
|
|
|
;; 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))
|
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
(format #t "Longest subject: ~s\n" longest-subject)
|
|
|
|
@end lisp
|
2012-01-04 23:08:50 +01:00
|
|
|
|
2012-01-09 07:23:11 +01:00
|
|
|
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
|
2012-07-15 11:44:52 +02:00
|
|
|
can easily write little programs to answer specific questions about your
|
|
|
|
e-mail corpus.
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-01-04 23:08:50 +01:00
|
|
|
@node Contacts
|
|
|
|
@chapter Contacts
|
|
|
|
|
2012-01-09 07:23:11 +01:00
|
|
|
We can retrieve the sender and recipients of an e-mail message using methods
|
2012-01-15 13:38:08 +01:00
|
|
|
like @code{mu:from}, @code{mu:to} etc.; @xref{Message methods}. These
|
2013-06-09 11:11:01 +02:00
|
|
|
procedures return the list of recipients as a single string; however, often it
|
2012-01-15 13:38:08 +01:00
|
|
|
is more useful to deal with recipients as separate objects.
|
2012-01-09 07:23:11 +01:00
|
|
|
|
|
|
|
@menu
|
2013-06-09 11:11:01 +02:00
|
|
|
* Contact procedures and objects::
|
2012-01-09 07:23:11 +01:00
|
|
|
* All contacts::
|
2013-06-09 11:11:01 +02:00
|
|
|
* Utility procedures::
|
2012-01-09 07:23:11 +01:00
|
|
|
* Example - mutt export::
|
|
|
|
@end menu
|
|
|
|
|
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
@node Contact procedures and objects
|
|
|
|
@section Contact procedures and objects
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
Message objects (@pxref{Messages}) have a method @t{mu:contacts}:
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
@code{(mu:contacts <message-object> [<contact-type>])}
|
2012-01-09 07:23:11 +01:00
|
|
|
|
|
|
|
The @t{<contact-type>} is a symbol, one of @code{mu:to}, @code{mu:from},
|
2012-07-15 11:44:52 +02:00
|
|
|
@code{mu:cc} or @code{mu:bcc}. This will then get the contact objects for the
|
2012-01-09 07:23:11 +01:00
|
|
|
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.
|
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
A contact object (@code{<mu:contact>}) has two methods:
|
2012-01-09 07:23:11 +01:00
|
|
|
@itemize
|
2012-01-15 13:38:08 +01:00
|
|
|
@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
|
2012-01-09 07:23:11 +01:00
|
|
|
@end itemize
|
|
|
|
|
|
|
|
Let's get a list of all names and e-mail addresses in the 'To:' field, of
|
|
|
|
messages matching 'book':
|
|
|
|
|
2012-01-12 23:53:05 +01:00
|
|
|
@lisp
|
2012-07-15 11:44:52 +02:00
|
|
|
(use-modules (mu))
|
2012-01-09 07:23:11 +01:00
|
|
|
(mu:initialize)
|
|
|
|
(mu:for-each-message
|
|
|
|
(lambda (msg)
|
|
|
|
(for-each
|
|
|
|
(lambda (contact)
|
|
|
|
(format #t "~a => ~a\n"
|
2012-01-15 13:38:08 +01:00
|
|
|
(or (mu:email contact) "") (or (mu:name contact) "no-name")))
|
|
|
|
(mu:contacts msg mu:field:to)))
|
2012-01-09 07:23:11 +01:00
|
|
|
"book")
|
2012-01-12 23:53:05 +01:00
|
|
|
@end lisp
|
2012-01-09 07:23:11 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
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.
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
To enable this, there is the procedure @code{mu:for-each-contact}, defined as
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
@code{(mu:for-each-contact procedure [search-expression])}.
|
2012-01-09 07:23:11 +01:00
|
|
|
|
|
|
|
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
|
2013-06-09 11:11:01 +02:00
|
|
|
the database), and execute @t{procedure} for each of them.
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
The @t{procedure} receives an object of the type @t{<mu:contact-with-stats>},
|
2012-01-15 13:38:08 +01:00
|
|
|
which is a @emph{subclass} of the @t{<mu:contact>} class discussed in
|
2013-06-09 11:11:01 +02:00
|
|
|
@xref{Contact procedures and objects}. @t{<mu:contact-with-stats>} objects
|
2012-01-15 13:38:08 +01:00
|
|
|
expose the following additional methods:
|
2012-01-09 07:23:11 +01:00
|
|
|
|
|
|
|
@itemize
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:frequency <contact>)}: returns the @emph{number of times} this contact occured in
|
2012-01-09 07:23:11 +01:00
|
|
|
one of the address fields
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:last-seen <contact>)}: returns the @emph{most recent time} the contact was
|
2012-01-09 07:23:11 +01:00
|
|
|
seen in one of the address fields, as a @t{time_t} value
|
|
|
|
@end itemize
|
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
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.
|
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
@node Utility procedures
|
|
|
|
@section Utility procedures
|
2012-01-15 13:38:08 +01:00
|
|
|
|
|
|
|
To make dealing with contacts even easier, there are a number of utility
|
2013-06-09 11:11:01 +02:00
|
|
|
procedures that can save you a bit of typing.
|
2012-01-15 13:38:08 +01:00
|
|
|
|
|
|
|
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"}.
|
|
|
|
|
2012-01-09 07:23:11 +01:00
|
|
|
|
|
|
|
@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
|
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
@t{mu guile} provides the procedure @code{(mu:contact->string <mu:contact>
|
|
|
|
format)} that we can use to do the conversion.
|
2012-01-15 13:38:08 +01:00
|
|
|
|
|
|
|
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.
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
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.
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-01-12 23:53:05 +01:00
|
|
|
@lisp
|
|
|
|
#!/bin/sh
|
|
|
|
exec guile -s $0 $@
|
|
|
|
!#
|
2012-01-15 13:38:08 +01:00
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
(use-modules (mu))
|
2012-01-09 07:23:11 +01:00
|
|
|
(mu:initialize)
|
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
;; Get a list of contacts that were seen at least 20 times since 2010
|
2012-01-12 23:53:05 +01:00
|
|
|
(define (selected-contacts)
|
|
|
|
(let ((addrs '())
|
|
|
|
(start (car (mktime (car (strptime "%F" "2010-01-01")))))
|
|
|
|
(minfreq 20))
|
|
|
|
(mu:for-each-contact
|
|
|
|
(lambda (contact)
|
2012-01-15 13:38:08 +01:00
|
|
|
(if (and (mu:email contact)
|
|
|
|
(>= (mu:frequency contact) minfreq)
|
|
|
|
(>= (mu:last-seen contact) start))
|
2012-01-12 23:53:05 +01:00
|
|
|
(set! addrs (cons contact addrs)))))
|
|
|
|
addrs))
|
|
|
|
|
|
|
|
(for-each
|
|
|
|
(lambda (contact)
|
2012-01-15 13:38:08 +01:00
|
|
|
(format #t "~a\n" (mu:contact->string contact "mutt-alias")))
|
2012-01-12 23:53:05 +01:00
|
|
|
(selected-contacts))
|
|
|
|
@end lisp
|
|
|
|
|
|
|
|
This simple program could be improved in many ways; this is left as an
|
|
|
|
excercise 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
|
2012-01-15 13:38:08 +01:00
|
|
|
@code{<mu:message>} objects:
|
2012-01-12 23:53:05 +01:00
|
|
|
@itemize
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:parts msg)} - returns a list @code{<mu-part>} objects, one for
|
2012-01-12 23:53:05 +01:00
|
|
|
each MIME-parts in the message.
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:attachments msg)} - like @code{parts}, but only list those MIME-parts
|
2012-01-12 23:53:05 +01:00
|
|
|
that look like proper attachments.
|
|
|
|
@end itemize
|
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
A @code{<mu:part>} object exposes a few methods to get information about the
|
2012-01-12 23:53:05 +01:00
|
|
|
part:
|
|
|
|
@itemize
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:name <part>)} - returns the file name of the mime-part, or @code{#f} if
|
2012-01-12 23:53:05 +01:00
|
|
|
there is none.
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:mime-type <part>)} - returns the mime-type of the mime-part, or @code{#f}
|
2012-01-12 23:53:05 +01:00
|
|
|
if there is none.
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:size <part>)} - returns the size in bytes of the mime-part
|
2012-01-12 23:53:05 +01:00
|
|
|
@end itemize
|
|
|
|
|
|
|
|
Then, we may want to save the part to a file; this can be done using either:
|
|
|
|
@itemize
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:save part <part>)} - save a part to a temporary file, return the file
|
2013-06-09 11:11:01 +02:00
|
|
|
name@footnote{the temporary filename is a predictable procedure of (user-id,
|
2012-01-12 23:53:05 +01:00
|
|
|
msg-path, part-index)}
|
2012-01-15 13:38:08 +01:00
|
|
|
@item @code{(mu:save-as <part> <path>)} - save part to file at path
|
2012-01-12 23:53:05 +01:00
|
|
|
@end itemize
|
|
|
|
|
|
|
|
@node Attachment example
|
|
|
|
@section Attachment example
|
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
Let's look at some small example. Let's get a list of the biggest attachments
|
|
|
|
in messages about Luxemburg:
|
2012-01-12 23:53:05 +01:00
|
|
|
|
|
|
|
@lisp
|
|
|
|
#!/bin/sh
|
|
|
|
exec guile -s $0 $@
|
|
|
|
!#
|
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
(use-modules (mu))
|
2012-01-12 23:53:05 +01:00
|
|
|
(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
|
2012-01-15 13:38:08 +01:00
|
|
|
(set! pairs (cons (cons (mu:name att) (or (mu:size att) 0)) pairs)))
|
|
|
|
(mu:attachments msg)))
|
2012-01-12 23:53:05 +01:00
|
|
|
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
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
@t{mu-guile} offers some convenience procedures to determine various statistics
|
2012-01-12 23:53:05 +01:00
|
|
|
about the messages in the database.
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
@menu
|
2013-01-27 20:31:26 +01:00
|
|
|
* Basics:: @code{mu:count}, @code{mu:average}, ...
|
2012-10-27 13:58:21 +02:00
|
|
|
* Tabulating values:: @code{mu:tabulate}
|
|
|
|
* Most frequent values:: @code{mu:top-n-most-frequent}
|
|
|
|
@end menu
|
|
|
|
|
2013-01-27 20:31:26 +01:00
|
|
|
@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
|
2013-06-09 11:11:01 +02:00
|
|
|
scheme@@(guile-user)>
|
2013-01-27 20:31:26 +01:00
|
|
|
@end example
|
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
@node Tabulating values
|
|
|
|
@section Tabulating values
|
2012-01-12 23:53:05 +01:00
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
@code{(mu:tabulate <procedure> [<search-expr>])} applies @t{<procedure>} to each
|
2012-10-27 13:58:21 +02:00
|
|
|
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
|
2013-06-09 11:11:01 +02:00
|
|
|
results of @t{<procedure>} and their frequencies. For fields that contain lists
|
2012-10-27 13:58:21 +02:00
|
|
|
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:
|
2012-01-12 23:53:05 +01:00
|
|
|
|
|
|
|
@lisp
|
|
|
|
#!/bin/sh
|
|
|
|
exec guile -s $0 $@
|
|
|
|
!#
|
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
(use-modules (mu) (mu stats) (mu plot))
|
2012-01-12 23:53:05 +01:00
|
|
|
(mu:initialize)
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
;; create a list like (("Sun" . 13) ("Mon" . 23) ...)
|
|
|
|
(define weekday-table
|
|
|
|
(mu:weekday-numbers->names
|
|
|
|
(sort
|
2012-07-12 14:46:19 +02:00
|
|
|
(mu:tabulate
|
2012-01-15 13:38:08 +01:00
|
|
|
(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)
|
2012-01-12 23:53:05 +01:00
|
|
|
@end lisp
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
The procedure @code{weekday-table} uses @code{mu:tabulate-message} to get the
|
2012-01-12 23:53:05 +01:00
|
|
|
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
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
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:
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-01-12 23:53:05 +01:00
|
|
|
@verbatim
|
|
|
|
Sun: 2278
|
|
|
|
Mon: 2993
|
|
|
|
Tue: 3184
|
|
|
|
Wed: 2833
|
|
|
|
Thu: 2800
|
|
|
|
Fri: 2339
|
|
|
|
Sat: 1856
|
2012-01-09 07:23:11 +01:00
|
|
|
@end verbatim
|
|
|
|
|
2012-01-12 23:53:05 +01:00
|
|
|
Clearly, Saturday is a slow day for e-mail...
|
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
@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))))
|
2013-06-09 11:11:01 +02:00
|
|
|
10)
|
2012-10-27 13:58:21 +02:00
|
|
|
@end lisp
|
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
If this is not short enough, @t{mu-guile} offers a convenience procedure to do
|
2012-10-27 13:58:21 +02:00
|
|
|
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!
|
|
|
|
|
|
|
|
|
2012-01-12 23:53:05 +01:00
|
|
|
@node Plotting data
|
|
|
|
@chapter Plotting data
|
|
|
|
|
2012-01-15 13:38:08 +01:00
|
|
|
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.
|
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
The @code{mu:plot-histogram} procedure takes the following arguments:
|
2012-01-15 13:38:08 +01:00
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
@code{(mu:plot-histogram <data> <title> <x-label> <y-label> [<want-ascii>])}
|
2012-01-15 13:38:08 +01:00
|
|
|
|
|
|
|
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:
|
|
|
|
|
2012-01-12 23:53:05 +01:00
|
|
|
@lisp
|
|
|
|
#!/bin/sh
|
|
|
|
exec guile -s $0 $@
|
|
|
|
!#
|
|
|
|
|
2012-07-15 11:44:52 +02:00
|
|
|
(use-modules (mu) (mu stats) (mu plot))
|
2012-01-12 23:53:05 +01:00
|
|
|
(mu:initialize)
|
|
|
|
|
|
|
|
(define (mail-per-hour-table)
|
|
|
|
(sort
|
2012-07-12 14:46:19 +02:00
|
|
|
(mu:tabulate
|
2012-01-12 23:53:05 +01:00
|
|
|
(lambda (msg)
|
2012-01-15 13:38:08 +01:00
|
|
|
(tm:hour (localtime (mu:date msg)))))
|
2012-01-12 23:53:05 +01:00
|
|
|
(lambda (x y) (< (car x) (car y)))))
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-10-27 13:58:21 +02:00
|
|
|
(mu:plot-histogram (mail-per-hour-table) "Mail per hour" "Hour" "Frequency"
|
|
|
|
#t)
|
2012-01-12 23:53:05 +01:00
|
|
|
@end lisp
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2012-10-19 15:00:41 +02:00
|
|
|
@cartouche
|
2012-01-12 23:53:05 +01:00
|
|
|
@verbatim
|
2012-10-19 15:00:41 +02:00
|
|
|
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
|
2012-01-12 23:53:05 +01:00
|
|
|
Hour
|
|
|
|
@end verbatim
|
2012-10-19 15:00:41 +02:00
|
|
|
@end cartouche
|
2012-01-09 07:23:11 +01:00
|
|
|
|
2013-06-09 11:11:01 +02:00
|
|
|
@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.
|
|
|
|
|
2012-01-01 21:45:47 +01:00
|
|
|
@node GNU Free Documentation License
|
|
|
|
@appendix GNU Free Documentation License
|
|
|
|
|
2012-10-20 14:49:53 +02:00
|
|
|
@include fdl.texi
|
2012-01-01 21:45:47 +01:00
|
|
|
@bye
|