* mu-guile.texi: extend / improve mu-guile documentation
This commit is contained in:
@ -28,8 +28,9 @@ Documentation License.''
|
||||
|
||||
Welcome to @t{mu-guile}!
|
||||
|
||||
@t{mu-guile} is a binding of the @t{mu} email search engine and the @t{guile}
|
||||
programming language.
|
||||
@t{mu-guile} is a binding of the @t{mu} email search engine for the @t{guile}
|
||||
programming language. That means that you can write simple (and not so simple)
|
||||
programs to data-mine your e-mail database.
|
||||
|
||||
@menu
|
||||
* Introduction::
|
||||
@ -49,17 +50,14 @@ Appendices
|
||||
@node Introduction
|
||||
@chapter Introduction
|
||||
|
||||
@t{mu4e} is an e-mail program for @emph{GNU/Emacs}; it uses the @t{mu} maildir
|
||||
search engine as its backend, making @t{mu} fully search-based.
|
||||
@t{mu} is a program for indexing / searching e-mails, as well as an
|
||||
@t{emacs}-based email-client (@t{mu4e}.
|
||||
@t{mu} is a program for indexing / searching e-mails stored in Maildirs.
|
||||
|
||||
@t{guile} is the @emph{GNU Ubiquitous Intelligent Language for Extensions} - a
|
||||
version of the @emph{Scheme} programming language and the official GNU
|
||||
extension language.
|
||||
|
||||
@t{mu-guile} connects @t{mu} and @t{guile}, and allows you to easily write
|
||||
programs to do things with your e-mails.
|
||||
programs for your specific needs.
|
||||
|
||||
@node Getting started
|
||||
@chapter Getting started
|
||||
@ -76,12 +74,10 @@ things work correctly.
|
||||
@section Installation
|
||||
|
||||
@t{mu-guile} is part of @t{mu} - by installing the latter, the former will be
|
||||
installed as well. Note, however, that @t{mu-guile} requires you to have
|
||||
@t{guile} version 2.0 installed, otherwise @t{mu-guile} will not be
|
||||
built/installed.
|
||||
installed as well, provided that you have @t{guile} version 2.0 installed.
|
||||
|
||||
At the time of writing, there are no distribution packages for @t{mu-guile}
|
||||
yet, so we are assuming installation from source packages.
|
||||
At the time of writing, there are no distribution packages for @t{mu-guile},
|
||||
so we are assuming installation from source packages.
|
||||
|
||||
Installation follows the normal sequence of:
|
||||
@example
|
||||
@ -148,7 +144,7 @@ Enter `,help' for help.
|
||||
scheme@(guile-user)>
|
||||
@end verbatim
|
||||
|
||||
Now, we need to load the @t{mu-guile} module:
|
||||
Now, we need to load some of the @t{mu-guile} modules:
|
||||
|
||||
@verbatim
|
||||
scheme@(guile-user)> (use-modules (mu) (mu message))
|
||||
@ -161,17 +157,22 @@ loaded the modules, we need to initialize the @t{mu-guile} system:
|
||||
scheme@(guile-user)> (mu:initialize)
|
||||
@end verbatim
|
||||
|
||||
When this is done, we can start querying the database. We'll go into various
|
||||
functions later in this manual, but just to give an example, let's get a list
|
||||
of the subjects of all messages that mention @emph{hello}:
|
||||
When this is done, we can start querying the database. We discuss various
|
||||
methods and functions later in this manual, but just to give an example, let's
|
||||
get a list of the subjects of all messages that mention @emph{hello}:
|
||||
|
||||
@verbatim
|
||||
scheme@(guile-user)> (for-each
|
||||
(lambda(msg)
|
||||
(format #t "Subject: ~a\n" (subject msg)))
|
||||
(format #t "Subject: ~a\n" (mu:subject msg)))
|
||||
(mu:message-list "hello"))
|
||||
@end verbatim
|
||||
|
||||
Note, the multi-lines in the example are only for readability; since it can be
|
||||
a bit uncomfortable to type long sequences at the 'REPL' (the Guile
|
||||
command-line), we recommend using a tool like
|
||||
Geiser@footnote{@url{http://www.nongnu.org/geiser/}}.
|
||||
|
||||
@node Initializing mu-guile
|
||||
@chapter Initializing mu-guile
|
||||
|
||||
@ -195,29 +196,30 @@ scheme@(guile-user)>
|
||||
@end verbatim
|
||||
|
||||
Now, the first thing we need to do is load the @t{mu-guile} modules;
|
||||
currently, there are three available:
|
||||
currently, there are six available:
|
||||
|
||||
@itemize
|
||||
@item @code{mu} - initialization, functions to get messages, contacts
|
||||
@item @code{mu message} - functions to deal with messages
|
||||
@item @code{mu contact} - functions to deal with contacts
|
||||
@item @code{mu part} - functions to deal with message-parts / attachments
|
||||
@item @code{mu stats} - some functions for doing statistics on your messages
|
||||
@item @code{mu plot} - functions to draw graphs from the statistics (requires @t{gnuplot}
|
||||
@end itemize
|
||||
|
||||
Let's simply load all of them:
|
||||
|
||||
@verbatim
|
||||
scheme@(guile-user)> (use-modules (mu) (mu message) (mu contact))
|
||||
scheme@(guile-user)> (use-modules (mu) (mu message) (mu contact) (mu part)
|
||||
(mu stats) (mu plot))
|
||||
@end verbatim
|
||||
|
||||
Assuming you have installed everything correctly, the first time you do this,
|
||||
@t{guile} will probably respond by showing some message about compiling the
|
||||
modules, and then end with another prompt.
|
||||
modules, and then return to you with another prompt.
|
||||
|
||||
Before we can do anything with @t{mu guile}, we need to initialize the
|
||||
system. The reason as to not do this automatically is to enable people to use
|
||||
non-default places to keep there @t{mu} data files.
|
||||
|
||||
We can initialize the system with:
|
||||
system. This goes like this:
|
||||
|
||||
@verbatim
|
||||
scheme@(guile-user)> (mu:initialize)
|
||||
@ -230,24 +232,25 @@ your @t{mu} data in a non-standard place:
|
||||
scheme@(guile-user)> (mu:initialize "/path/to/my/mu/")
|
||||
@end verbatim
|
||||
|
||||
If all worked up until here, we're ready to go with @t{mu-guile}.
|
||||
If all worked up until here, we're ready to go with @t{mu-guile} - hurray! In
|
||||
the next chapters we'll walk through all the modules.
|
||||
|
||||
@node Messages
|
||||
@chapter Messages
|
||||
|
||||
In this chapter, we discuss how to find messages, and then how to do various
|
||||
things with them.
|
||||
In this chapter, we discuss how to find messages and how to do various things
|
||||
with them.
|
||||
|
||||
@menu
|
||||
* Finding messages::
|
||||
* Message functions::
|
||||
* Message methods::
|
||||
* Example - 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
|
||||
principle functions to do this:
|
||||
Now we are ready to retrieve some messages from the system. There are two main
|
||||
functions to do this:
|
||||
|
||||
@itemize
|
||||
@item @code{(mu:message-list [<search-expression>])}
|
||||
@ -263,26 +266,27 @@ could do:
|
||||
|
||||
@verbatim
|
||||
scheme@(guile-user)> (mu:message-list "subject:coffee")
|
||||
$1 = (#<<mu-message> 9040640> #<<mu-message> 9040630>
|
||||
#<<mu-message> 9040570>)
|
||||
$1 = (#<<mu:message> 9040640> #<<mu:message> 9040630>
|
||||
#<<mu:message> 9040570>)
|
||||
@end verbatim
|
||||
|
||||
So, we get a list with three @t{<mu-message>} objects. We'll discuss them in a
|
||||
bit more detail in the next section, but let's just use the @code{subject}
|
||||
function ('method') provided by @t{<mu-message>} objects to retrieve the
|
||||
subject-field.
|
||||
So, since apparently we have three messages matching @t{subject:coffee}, we
|
||||
get a list of three @t{<mu:message>} objects. Let's just use the
|
||||
@code{mu:subject} function ('method') provided by @t{<mu:message>} objects to
|
||||
retrieve the subject-field (more about methods in the next section).
|
||||
|
||||
For your convenience, @t{guile} has saved the result in @t{$1}, so to get the
|
||||
subject of the first message in the list, we can do:
|
||||
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)> (subject (car $1))
|
||||
scheme@(guile-user)> (mu:subject (car $1))
|
||||
$2 = "Re: best coffee ever!"
|
||||
@end verbatim
|
||||
|
||||
The second function we mentioned, @code{mu:for-each-message}, executes some
|
||||
function for each message matched by the search expression (or @emph{all}
|
||||
message if the search expression is omitted).
|
||||
messages if the search expression is omitted).
|
||||
|
||||
@verbatim
|
||||
scheme@(guile-user)> (mu:for-each-message
|
||||
@ -300,78 +304,72 @@ 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 for
|
||||
@code{for-each} and @code{mu:message-list}} and a couple of @t{<mu-message>}
|
||||
methods, together with that Guile/Scheme provides should allow for many
|
||||
@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 functions
|
||||
@section Message functions
|
||||
@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>}), let's see what we can do with such an object.
|
||||
|
||||
@code{<mu-message>} defines the following methods
|
||||
@footnote{A note on naming: functions we have seen before --
|
||||
@code{mu:initialize}, @code{mu:message-list} and @code{mu:for-each-message}
|
||||
are prefixed with @t{mu:}. This is not the case for the @code{<mu-message>}
|
||||
methods to be discussed next, such as the methods @code{subject} and
|
||||
@code{from}. Reason for this is that it is not @emph{needed}, since these
|
||||
methods only recognized for @code{<mu-message>} objects, and do not affect
|
||||
anything else, while the @code{mu:}-prefixed are 'globally visible' and thus
|
||||
we need to be careful about naming conflicts}
|
||||
that all only take a single
|
||||
@code{<mu-message>} object as a parameter. We won't go into the exact meanings
|
||||
@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 functions here - for the details about various flags /
|
||||
properties, please refer to the @t{mu-find} man-page.
|
||||
|
||||
@itemize
|
||||
@item @code{bcc}: the @t{Bcc} field of the message, or @t{#f} if there is none
|
||||
@item @code{body-html}: : the html body of the message, or @t{#f} if there is none
|
||||
@item @code{body-txt}: the plain-text body of the message, or @t{#f} if there is none
|
||||
@item @code{cc}: the @t{Bcc} field of the message, or @t{#f} if there is none
|
||||
@item @code{date}: the @t{Date} field of the message, or 0 if there is none
|
||||
@item @code{flags}: list of message-flags for this message
|
||||
@item @code{from}: the @t{From} field of the message, or @t{#f} if there is none
|
||||
@item @code{maildir}: the maildir this message lives in, or @t{#f} if there is none
|
||||
@item @code{message-id}: the @t{Message-Id} field of the message, or @t{#f} if there is none
|
||||
@item @code{path}: the file system path for this message
|
||||
@item @code{priority}: the priority of this message (either @t{mu:low}, @t{mu:normal}
|
||||
or @t{mu:high}
|
||||
@item @code{references}: the list of messages (message-ids) this message
|
||||
refers to in the @t{References:} header
|
||||
@item @code{size}: size of the message in bytes
|
||||
@item @code{subject}: the @t{Subject} field of the message, or @t{#f} if there is none.
|
||||
@item @code{tags}: list of tags for this message
|
||||
@item @code{to}: the sender of the message, or @t{#f} if there is none.
|
||||
@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:to msg)}: the sender of the message, or @t{#f} if there is none.
|
||||
@end itemize
|
||||
|
||||
With these functions, we can query messages for their properties; for example:
|
||||
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)> (subject msg)
|
||||
scheme@(guile-user)> (mu:subject msg)
|
||||
$1 = "Re: Running in the snow is beautiful"
|
||||
scheme@(guile-user)> (flags msg)
|
||||
$2 = (mu:replied mu:seen)
|
||||
scheme@(guile-user)> (strftime "%F" (localtime (date msg)))
|
||||
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 functions:
|
||||
There are a couple more methods:
|
||||
@itemize
|
||||
@item @code{(header <mu-message> "<header-name>")} returns an arbitrary message
|
||||
@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 @code{(contacts <mu-message> contact-type)} which returns a list
|
||||
of contacts (names/e-mail addresses in the To/From/Cc/Bcc-fields). @xref{Contacts}.
|
||||
@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. If you put the
|
||||
following in a separate file, make it executable, and run it like any program.
|
||||
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.
|
||||
|
||||
@verbatim
|
||||
#!/bin/sh
|
||||
@ -386,7 +384,7 @@ exec guile -s $0 $@
|
||||
;; note: (subject msg) => #f if there is no subject
|
||||
(define list-of-subjects
|
||||
(map (lambda (msg)
|
||||
(or (subject msg) "")) (mu:message-list "date:2011..2011")))
|
||||
(or (mu:subject msg) "")) (mu:message-list "date:2011..2011")))
|
||||
;; see the mu-find manpage for the date syntax
|
||||
|
||||
(define longest-subject
|
||||
@ -408,16 +406,18 @@ corpus.
|
||||
@chapter Contacts
|
||||
|
||||
We can retrieve the sender and recipients of an e-mail message using methods
|
||||
like @code{from}, @code{to}, @code{cc} and @code{bcc}; @xref{Message
|
||||
functions}. These functions return the list of recipients as a single string;
|
||||
however, often it is more useful to deal with recipients as separate objects.
|
||||
like @code{mu:from}, @code{mu:to} etc.; @xref{Message methods}. These
|
||||
functions return the list of recipients as a single string; however, often it
|
||||
is more useful to deal with recipients as separate objects.
|
||||
|
||||
@t{mu-guile} offers some functionality for this in the @code{(mu contact)}
|
||||
module.
|
||||
module. Also, it adds some contact-related methods for @code{<mu:message>}
|
||||
objects.
|
||||
|
||||
@menu
|
||||
* Contact functions and objects::
|
||||
* All contacts::
|
||||
* Utility functions::
|
||||
* Example - mutt export::
|
||||
@end menu
|
||||
|
||||
@ -432,7 +432,7 @@ module.
|
||||
After loading the @code{(mu contact)}, message objects (@pxref{Messages}) gain
|
||||
the the @t{contacts}-methods:
|
||||
|
||||
@code{(contacts <message-object> [<contact-type>])}
|
||||
@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
|
||||
@ -440,10 +440,10 @@ 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:
|
||||
A contact object (@code{<mu:contact>}) has two methods:
|
||||
@itemize
|
||||
@item @code{name} returns the name of the contact, or #f if there is none
|
||||
@item @code{email} returns the e-mail address of the contact, or #f if there is none
|
||||
@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
|
||||
@ -457,8 +457,8 @@ messages matching 'book':
|
||||
(for-each
|
||||
(lambda (contact)
|
||||
(format #t "~a => ~a\n"
|
||||
(or (email contact) "") (or (name contact) "no-name")))
|
||||
(contacts msg mu:to)))
|
||||
(or (mu:email contact) "") (or (mu:name contact) "no-name")))
|
||||
(mu:contacts msg mu:field:to)))
|
||||
"book")
|
||||
@end lisp
|
||||
|
||||
@ -476,26 +476,39 @@ important in an e-mail program.
|
||||
|
||||
To enable this, there is the function @code{mu:for-each-contact}, defined as
|
||||
|
||||
@code{(mu:for-each-contact <function> [<search-expression>])}.
|
||||
@code{(mu:for-each-contact function [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{<function>} for each of these contacts.
|
||||
the database), and execute @t{function} for each of these contacts.
|
||||
|
||||
The @t{<function>} receives an object of the type @t{<contact-with-stats>},
|
||||
which is a @emph{subclass} of the @t{<contact>} class discussed in
|
||||
@xref{Contact functions and objects}. @t{<contact-with-stats>} objects expose
|
||||
the following methods:
|
||||
The @t{function} 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 functions and objects}. @t{<mu:contact-with-stats>} objects
|
||||
expose the following additional methods:
|
||||
|
||||
@itemize
|
||||
@item @code{frequency}: returns the @emph{number of times} this contact occured in
|
||||
@item @code{(mu:frequency <contact>)}: returns the @emph{number of times} this contact occured in
|
||||
one of the address fields
|
||||
@item @code{last-seen}: returns the @emph{most recent time} the contact was
|
||||
@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 function aggregates per e-mail address; if a certain e-mail address occurs
|
||||
with different names, it uses the most recent non-empty name.
|
||||
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 functions
|
||||
@section Utility functions
|
||||
|
||||
To make dealing with contacts even easier, there are a number of utility
|
||||
functions 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
|
||||
@ -510,46 +523,41 @@ something like:
|
||||
alias <nick> [<name>] "<" <email> ">"
|
||||
@end verbatim
|
||||
|
||||
Many of the names in our database could be random people writing things in
|
||||
mailing lists, so we may want to limit it to people we have seen at least 10
|
||||
times in the last year.
|
||||
Anyway, there is the function @code{(mu:contact->string <mu:contact> format)}
|
||||
that we can use to do the conversion.
|
||||
|
||||
It is a bit hard to @emph{guess} the nick name for e-mail contacts, so we are
|
||||
going to assume it is the lowercase version of the first word in
|
||||
@t{<name>}. You can always adjust them later by hand, obviously.
|
||||
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 message) (mu contact))
|
||||
(mu:initialize)
|
||||
|
||||
;; Get a list of contacts that were seen at least 20 times since 2010
|
||||
(define (selected-contacts)
|
||||
"Get a list of contacts that were seen at least 20 times since
|
||||
2010."
|
||||
(let ((addrs '())
|
||||
(start (car (mktime (car (strptime "%F" "2010-01-01")))))
|
||||
(minfreq 20))
|
||||
(mu:for-each-contact
|
||||
(lambda (contact)
|
||||
(if (and (email contact)
|
||||
(>= (frequency contact) minfreq)
|
||||
(>= (last-seen contact) start))
|
||||
(if (and (mu:email contact)
|
||||
(>= (mu:frequency contact) minfreq)
|
||||
(>= (mu:last-seen contact) start))
|
||||
(set! addrs (cons contact addrs)))))
|
||||
addrs))
|
||||
|
||||
(define (guess-nick contact)
|
||||
"Guess a nick name for CONTACT."
|
||||
(string-map
|
||||
(lambda(kar)
|
||||
(if (char-alphabetic? kar) kar #\_))
|
||||
(string-downcase (or (name contact) (email contact)))))
|
||||
|
||||
(for-each
|
||||
(lambda (contact)
|
||||
(format #t "alias ~a ~a <~a>\n"
|
||||
(guess-nick contact) (name contact) (email contact)))
|
||||
(format #t "~a\n" (mu:contact->string contact "mutt-alias")))
|
||||
(selected-contacts))
|
||||
@end lisp
|
||||
|
||||
@ -570,39 +578,37 @@ is the @t{mu part} module.
|
||||
@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:
|
||||
@code{<mu:message>} objects:
|
||||
@itemize
|
||||
@item @code{(parts msg)} - returns a list @code{<mu-part>} objects, one for
|
||||
@item @code{(mu:parts msg)} - returns a list @code{<mu-part>} objects, one for
|
||||
each MIME-parts in the message.
|
||||
@item @code{(attachments)} - like @code{parts}, but only list those MIME-parts
|
||||
@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
|
||||
A @code{<mu:part>} object exposes a few methods to get information about the
|
||||
part:
|
||||
@itemize
|
||||
@item @code{name} - returns the file name of the mime-part, or @code{#f} if
|
||||
@item @code{(mu:name <part>)} - returns the file name of the mime-part, or @code{#f} if
|
||||
there is none.
|
||||
@item @code{mime-type} - returns the mime-type of the mime-part, or @code{#f}
|
||||
@item @code{(mu:mime-type <part>)} - returns the mime-type of the mime-part, or @code{#f}
|
||||
if there is none.
|
||||
@item @code{size} - returns the size in bytes of the mime-part
|
||||
@item @code{(mu:size <part>)} - returns the size in bytes of the mime-part
|
||||
@end itemize
|
||||
|
||||
Then, we may want to save the part to a file; this can be done using either:
|
||||
@itemize
|
||||
@item @code{(save part)} - save a part to a temporary file, return the file
|
||||
@item @code{(mu:save part <part>)} - save a part to a temporary file, return the file
|
||||
name@footnote{the temporary filename is a predictable function of (user-id,
|
||||
msg-path, part-index)}
|
||||
@item @code{(save-as part path)} - save part to file at path
|
||||
@item @code{(mu:save-as <part> <path>)} - save part to file at path
|
||||
@end itemize
|
||||
|
||||
@node Attachment example
|
||||
@section Attachment example
|
||||
|
||||
Let's look at some small examples.
|
||||
|
||||
First, let's get a list of the biggest attachments in messages about
|
||||
Luxemburg:
|
||||
Let's look at some small example. Let's get a list of the biggest attachments
|
||||
in messages about Luxemburg:
|
||||
|
||||
@lisp
|
||||
#!/bin/sh
|
||||
@ -620,8 +626,8 @@ matching EXPR."
|
||||
(lambda (msg)
|
||||
(for-each
|
||||
(lambda (att) ;; add (filename . size) to the list
|
||||
(set! pairs (cons (cons (name att) (or (size att) 0)) pairs)))
|
||||
(attachments msg)))
|
||||
(set! pairs (cons (cons (mu:name att) (or (mu:size att) 0)) pairs)))
|
||||
(mu:attachments msg)))
|
||||
expr)
|
||||
pairs))
|
||||
|
||||
@ -645,9 +651,9 @@ probably be a bit more elegant.
|
||||
@t{mu-guile} offers some convenience functions to determine various statistics
|
||||
about the messages in the database.
|
||||
|
||||
First, there is @code{(mu:tabulate-messages <function> [<search-expr>])}. This
|
||||
function applies @t{<function>} to each message matching @t{<search-expr>}
|
||||
(leave empty to match @emph{all} messages), and returns a associative list
|
||||
@code{(mu:tabulate-messages <function> [<search-expr>])} applies
|
||||
@t{<function>} 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{<function>} and their frequencies.
|
||||
|
||||
This can best be demonstrated with a little example. Suppose we want to know
|
||||
@ -658,34 +664,36 @@ how many messages we receive per weekday:
|
||||
exec guile -s $0 $@
|
||||
!#
|
||||
|
||||
(use-modules (mu) (mu message) (mu stats))
|
||||
(use-modules (mu) (mu message) (mu stats) (mu plot))
|
||||
(mu:initialize)
|
||||
|
||||
(define (weekday-table)
|
||||
"Returns something like
|
||||
'((0 . 12) (5 . 20) (2 . 16) ... )
|
||||
that is, an unsorted list of (<weekday> . <frequency>)."
|
||||
;; create a list like (("Sun" . 13) ("Mon" . 23) ...)
|
||||
(define weekday-table
|
||||
(mu:weekday-numbers->names
|
||||
(sort
|
||||
(mu:tabulate-messages
|
||||
(lambda (msg)
|
||||
(tm:wday (localtime (date msg))))))
|
||||
(tm:wday (localtime (mu:date msg)))))
|
||||
(lambda (a b) (< (car a) (car b))))))
|
||||
|
||||
;; sort & display
|
||||
(let ((table (weekday-table)))
|
||||
(for-each
|
||||
(lambda (pair)
|
||||
(let ((days '("Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat")))
|
||||
(format #t "~a: ~a\n"
|
||||
(list-ref days (car pair)) (cdr pair))))
|
||||
(sort (weekday-table)(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 function @code{weekday-table} use @code{mu:tabulate-message} to get the
|
||||
The function @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
|
||||
|
||||
The script then output these numbers in following form:
|
||||
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
|
||||
@ -702,6 +710,25 @@ Clearly, Saturday is a slow day for e-mail...
|
||||
@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} function takes the following arguments:
|
||||
|
||||
@code{(mu:plot <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 $@
|
||||
@ -714,10 +741,10 @@ exec guile -s $0 $@
|
||||
(sort
|
||||
(mu:tabulate-messages
|
||||
(lambda (msg)
|
||||
(tm:hour (localtime (date msg)))))
|
||||
(tm:hour (localtime (mu:date msg)))))
|
||||
(lambda (x y) (< (car x) (car y)))))
|
||||
|
||||
(mu:plot-ascii (mail-per-hour-table) "Mail per hour" "Hour" "Frequency")
|
||||
(mu:plot (mail-per-hour-table) "Mail per hour" "Hour" "Frequency" #t)
|
||||
@end lisp
|
||||
|
||||
@verbatim
|
||||
|
||||
Reference in New Issue
Block a user