* guile: some more improvements

This commit is contained in:
djcb 2012-07-15 12:44:52 +03:00
parent 18ce677299
commit 543f4a1926
5 changed files with 50 additions and 77 deletions

View File

@ -374,7 +374,7 @@ SCM_DEFINE (get_header, "mu:c:get-header", 2, 0, 0,
{ {
MuMsgWrapper *msgwrap; MuMsgWrapper *msgwrap;
char *header; char *header;
const char *val; SCM val;
MU_GUILE_INITIALIZED_OR_ERROR; MU_GUILE_INITIALIZED_OR_ERROR;
@ -384,13 +384,14 @@ SCM_DEFINE (get_header, "mu:c:get-header", 2, 0, 0,
msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); msgwrap = (MuMsgWrapper*) SCM_CDR(MSG);
header = scm_to_utf8_string (HEADER); header = scm_to_utf8_string (HEADER);
val = mu_msg_get_header (msgwrap->_msg, header); val = mu_guile_scm_from_str
(mu_msg_get_header(msgwrap->_msg, header));
free (header); free (header);
/* explicitly close the file backend, so we won't run of fds */ /* explicitly close the file backend, so we won't run of fds */
mu_msg_close_file_backend (msgwrap->_msg); mu_msg_close_file_backend (msgwrap->_msg);
return mu_guile_scm_from_str(val); return val;
} }
#undef FUNC_NAME #undef FUNC_NAME

View File

@ -204,23 +204,14 @@ Enter `,help' for help.
scheme@(guile-user)> scheme@(guile-user)>
@end verbatim @end verbatim
Now, the first thing we need to do is load the @t{mu-guile} modules; The first thing we need to do is loading the modules. All the basics are in
currently, there are six available: the @t{(mu)} module, with some statistical extras in @t{(mu stats)}, and some
graph plotting functionality in @t{(mu plot)}.
@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: Let's simply load all of them:
@verbatim @verbatim
scheme@(guile-user)> (use-modules (mu) (mu message) (mu contact) (mu part) scheme@(guile-user)> (use-modules (mu) (mu stats) (mu plot))
(mu stats) (mu plot))
@end verbatim @end verbatim
Assuming you have installed everything correctly, the first time you do this, Assuming you have installed everything correctly, the first time you do this,
@ -383,12 +374,12 @@ 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 this if you put the following in a separate file, make it executable, and run
it like any program. it like any program.
@verbatim @lisp
#!/bin/sh #!/bin/sh
exec guile -s $0 $@ exec guile -s $0 $@
!# !#
(use-modules (mu) (mu message)) (use-modules (mu))
(use-modules (srfi srfi-1)) (use-modules (srfi srfi-1))
(mu:initialize) (mu:initialize)
@ -405,14 +396,13 @@ exec guile -s $0 $@
subj1 subj2)) subj1 subj2))
"" list-of-subjects)) "" list-of-subjects))
(format #t "Longest subject: ~s" longest-subject) (format #t "Longest subject: ~s\n" longest-subject)
(newline) @end lisp
@end verbatim
There are many other ways to solve the same problem, for example by using an 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 iterative approach with @code{mu:for-each-message}, but it should show how one
can easily write little programs to answer specific questions about an e-mail can easily write little programs to answer specific questions about your
corpus. e-mail corpus.
@node Contacts @node Contacts
@chapter Contacts @chapter Contacts
@ -422,10 +412,6 @@ 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 functions return the list of recipients as a single string; however, often it
is more useful to deal with recipients as separate objects. is more useful to deal with recipients as separate objects.
@t{mu-guile} offers some functionality for this in the @code{(mu contact)}
module. Also, it adds some contact-related methods for @code{<mu:message>}
objects.
@menu @menu
* Contact functions and objects:: * Contact functions and objects::
* All contacts:: * All contacts::
@ -437,17 +423,12 @@ objects.
@node Contact functions and objects @node Contact functions and objects
@section Contact functions and objects @section Contact functions and objects
@verbatim Message objects (@pxref{Messages}) have a method @t{mu:contacts}:
(use-modules (mu contact))
@end verbatim
After loading the @code{(mu contact)}, message objects (@pxref{Messages}) gain
the the @t{contacts}-methods:
@code{(mu: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}, 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 @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 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 specify @t{#t} for it, you will get a list of @emph{all} contact objects for
the message. the message.
@ -462,7 +443,7 @@ Let's get a list of all names and e-mail addresses in the 'To:' field, of
messages matching 'book': messages matching 'book':
@lisp @lisp
(use-modules (mu) (mu message) (mu contact)) (use-modules (mu))
(mu:initialize) (mu:initialize)
(mu:for-each-message (mu:for-each-message
(lambda (msg) (lambda (msg)
@ -481,10 +462,9 @@ have each of the contacts only show up @emph{once} - for that, please refer to
@node All contacts @node All contacts
@section All contacts @section All contacts
Sometimes it may also be useful to look at @emph{all} the different contacts Sometimes you may want to inspect @emph{all} the different contacts in the
in the @t{mu} database -- that is, all the different contacts. This is useful, @t{mu} database. This is useful, for instance, when exporting contacts to some
for example, when exporting contacts to some external format that can then be external format that can then be important in an e-mail program.
important in an e-mail program.
To enable this, there is the function @code{mu:for-each-contact}, defined as To enable this, there is the function @code{mu:for-each-contact}, defined as
@ -492,7 +472,7 @@ To enable this, there is the function @code{mu:for-each-contact}, defined as
This will aggregate the unique contacts from @emph{all} messages matching 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 @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 them.
The @t{function} receives an object of the type @t{<mu:contact-with-stats>}, 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 which is a @emph{subclass} of the @t{<mu:contact>} class discussed in
@ -551,7 +531,7 @@ adjust them later by hand, obviously.
exec guile -s $0 $@ exec guile -s $0 $@
!# !#
(use-modules (mu) (mu message) (mu contact)) (use-modules (mu))
(mu:initialize) (mu:initialize)
;; Get a list of contacts that were seen at least 20 times since 2010 ;; Get a list of contacts that were seen at least 20 times since 2010
@ -627,7 +607,7 @@ in messages about Luxemburg:
exec guile -s $0 $@ exec guile -s $0 $@
!# !#
(use-modules (mu) (mu message) (mu part)) (use-modules (mu))
(mu:initialize) (mu:initialize)
(define (all-attachments expr) (define (all-attachments expr)
@ -676,7 +656,7 @@ how many messages we receive per weekday:
exec guile -s $0 $@ exec guile -s $0 $@
!# !#
(use-modules (mu) (mu message) (mu stats) (mu plot)) (use-modules (mu) (mu stats) (mu plot))
(mu:initialize) (mu:initialize)
;; create a list like (("Sun" . 13) ("Mon" . 23) ...) ;; create a list like (("Sun" . 13) ("Mon" . 23) ...)
@ -746,7 +726,7 @@ message per hour:
exec guile -s $0 $@ exec guile -s $0 $@
!# !#
(use-modules (mu) (mu message) (mu contact) (mu stats) (mu plot)) (use-modules (mu) (mu stats) (mu plot))
(mu:initialize) (mu:initialize)
(define (mail-per-hour-table) (define (mail-per-hour-table)
@ -784,8 +764,6 @@ exec guile -s $0 $@
Hour Hour
@end verbatim @end verbatim
@node GNU Free Documentation License @node GNU Free Documentation License
@appendix GNU Free Documentation License @appendix GNU Free Documentation License

View File

@ -35,7 +35,7 @@
mu:for-each-msg mu:for-each-msg
mu:message-list mu:message-list
;; message funcs ;; message funcs
header mu:header
;; message accessors ;; message accessors
mu:field:bcc mu:field:bcc
mu:field:body-html mu:field:body-html
@ -143,7 +143,7 @@
(define-getter mu:timestamp mu:field:timestamp) (define-getter mu:timestamp mu:field:timestamp)
(define-getter mu:to mu:field:to) (define-getter mu:to mu:field:to)
(define-method (header (msg <mu:message>) (hdr <string>)) (define-method (mu:header (msg <mu:message>) (hdr <string>))
"Get an arbitrary header HDR from message MSG; return #f if it does "Get an arbitrary header HDR from message MSG; return #f if it does
not exist." not exist."
(mu:c:get-header (slot-ref msg 'msg) hdr)) (mu:c:get-header (slot-ref msg 'msg) hdr))

View File

@ -24,8 +24,9 @@
:use-module (ice-9 r5rs) :use-module (ice-9 r5rs)
:export ( mu:tabulate :export ( mu:tabulate
mu:average mu:average
mu:standard-deviation mu:stddev
mu:pearsons-r mu:max
mu:min
mu:weekday-numbers->names mu:weekday-numbers->names
mu:month-numbers->names)) mu:month-numbers->names))
@ -68,11 +69,11 @@ undefined."
EXPR (or #t for all). Returns #f if undefined." EXPR (or #t for all). Returns #f if undefined."
(average (map func (mu:message-list expr)))) (average (map func (mu:message-list expr))))
(define* (mu:standard-deviation func #:optional (expr #t)) (define* (mu:stddev func #:optional (expr #t))
"Get the standard deviation for the the values of FUNC applied to "Get the standard deviation the the values of FUNC applied to all
all messages matching EXPR (or #t for all). Returns #f if undefined." messages matching EXPR (or #t for all). This is the 'population' stddev, not the 'sample' stddev. Returns #f if undefined."
(stddev (map func (mu:message-list expr)))) (stddev (map func (mu:message-list expr))))
(define* (mu:max func #:optional (expr #t)) (define* (mu:max func #:optional (expr #t))
"Get the maximum value of FUNC applied to all messages matching "Get the maximum value of FUNC applied to all messages matching
EXPR (or #t for all). Returns #f if undefined." EXPR (or #t for all). Returns #f if undefined."
@ -82,25 +83,7 @@ EXPR (or #t for all). Returns #f if undefined."
"Get the minimum value of FUNC applied to all messages matching "Get the minimum value of FUNC applied to all messages matching
EXPR (or #t for all). Returns #f if undefined." EXPR (or #t for all). Returns #f if undefined."
(apply min (map func (mu:message-list expr)))) (apply min (map func (mu:message-list expr))))
(define* (mu:pearsons-r func1 func2 #:optional (expr #t))
"Calculate Pearson's product-moment correlation coefficient between
func1 and func2. Inefficient implementation."
(let* ((msglist (mu:message-list expr))
(lst-x (map func1 msglist))
(lst-y (map func2 msglist))
(avg-x (average lst-x))
(avg-y (average lst-y))
(denominator (sqrt (* (stddev lst-x) (stddev lst-y))))
(n (length lst-x))
(cov-xy 0))
(while (not (null? lst-x))
(set! cov-xy (+ (* (- (car lst-x) avg-x) (- (car lst-y) avg-y))))
(set! lst-x (cdr lst-x))
(set! lst-y (cdr lst-y)))
(/ (/ cov-xy n) denominator)))
;; a list of abbreviated, localized day names ;; a list of abbreviated, localized day names
(define day-names (define day-names
(map locale-day-short (iota 7 1))) (map locale-day-short (iota 7 1)))

View File

@ -20,8 +20,9 @@ exec guile -e main -s $0 $@
;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
(setlocale LC_ALL "") (setlocale LC_ALL "")
(use-modules (srfi srfi-1))
(use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format)) (use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format))
(use-modules (mu)) (use-modules (mu) (mu stats))
(define (n-results-or-exit query n) (define (n-results-or-exit query n)
"Run QUERY, and exit 1 if the number of results != N." "Run QUERY, and exit 1 if the number of results != N."
@ -73,6 +74,7 @@ exec guile -e main -s $0 $@
(str-equal-or-exit (mu:subject msg) "Fwd: rfc822") (str-equal-or-exit (mu:subject msg) "Fwd: rfc822")
(str-equal-or-exit (mu:to msg) "martin") (str-equal-or-exit (mu:to msg) "martin")
(str-equal-or-exit (mu:from msg) "foobar <foo@example.com>") (str-equal-or-exit (mu:from msg) "foobar <foo@example.com>")
(str-equal-or-exit (mu:header msg "X-Mailer") "Ximian Evolution 1.4.5")
(if (not (equal? (mu:priority msg) mu:prio:normal)) (if (not (equal? (mu:priority msg) mu:prio:normal))
(error-exit "Expected ~A, got ~A" (mu:priority msg) mu:prio:normal))) (error-exit "Expected ~A, got ~A" (mu:priority msg) mu:prio:normal)))
@ -81,18 +83,27 @@ exec guile -e main -s $0 $@
(str-equal-or-exit (mu:subject msg) "atoms") (str-equal-or-exit (mu:subject msg) "atoms")
(str-equal-or-exit (mu:to msg) "Democritus <demo@example.com>") (str-equal-or-exit (mu:to msg) "Democritus <demo@example.com>")
(str-equal-or-exit (mu:from msg) "\"Richard P. Feynman\" <rpf@example.com>") (str-equal-or-exit (mu:from msg) "\"Richard P. Feynman\" <rpf@example.com>")
(str-equal-or-exit (mu:header msg "Content-transfer-encoding") "7BIT")
(if (not (equal? (mu:priority msg) mu:prio:high)) (if (not (equal? (mu:priority msg) mu:prio:high))
(error-exit "Expected ~a, got ~a" (mu:priority msg) mu:prio:high)))) (error-exit "Expected ~a, got ~a" (mu:priority msg) mu:prio:high))))
(define (num-equal-or-exit got exp)
"S1 == S2 or exit 1."
;; (format #t "'~A' <=> '~A'\n" s1 s2)
(if (not (= exp got))
(error-exit "Expected \"~S\", got \"~S\"\n" exp got)))
(define (test-stats) (define (test-stats)
"Test statistical functions." "Test statistical functions."
) ;; average
(num-equal-or-exit (mu:average mu:size) 20422/3)
(num-equal-or-exit (floor (mu:stddev mu:size))
(floor 13414.7101616927))
(num-equal-or-exit (mu:max mu:size) 46230)
(num-equal-or-exit (mu:min mu:size) 111))
(define (main args) (define (main args)
(let* ((optionspec '((muhome (value #t)) (let* ((optionspec '((muhome (value #t))
(test (value #t)))) (test (value #t))))
(options (getopt-long args optionspec)) (options (getopt-long args optionspec))