* update the guile-bindings and README a bit

This commit is contained in:
Dirk-Jan C. Binnema
2011-07-17 14:37:12 +03:00
parent 5695077514
commit 8b824da539
4 changed files with 132 additions and 32 deletions

View File

@ -76,26 +76,21 @@ SCM_DEFINE (msg_make_from_file, "mu:msg:make-from-file", 1, 0, 0,
#undef FUNC_NAME
static SCM
scm_from_string_or_null (const char *str)
{
return str ? scm_from_utf8_string (str) : SCM_UNSPECIFIED;
}
static SCM
msg_str_field (SCM msg_smob, MuMsgFieldId mfid)
{
const char *val, *endptr;
MuMsgWrapper *msgwrap;
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
val = mu_msg_get_field_string(msgwrap->_msg, mfid);
if (val && !g_utf8_validate (val, -1, &endptr)) {
//return scm_from_utf8_string("<invalid>");
gchar *part;
SCM scm;
part = g_strndup (val, (endptr-val));
scm = scm_from_utf8_string(part);
g_free (part);
return scm;
} else
return val ? scm_from_utf8_string(val) : SCM_UNSPECIFIED;
return scm_from_string_or_null (
mu_msg_get_field_string(msgwrap->_msg, mfid));
}
static gint64
@ -240,9 +235,8 @@ contacts_to_list (MuMsgContact *contact, EachContactData *ecdata)
item = scm_list_1
(scm_list_2 (
name ? scm_from_utf8_string(name) : SCM_UNSPECIFIED,
addr ? scm_from_utf8_string(addr) : SCM_UNSPECIFIED));
scm_from_string_or_null(name),
scm_from_string_or_null(addr)));
ecdata->lst = scm_append_x (scm_list_2(ecdata->lst, item));
}
}
@ -357,7 +351,7 @@ SCM_DEFINE (msg_body, "mu:msg:body", 1, 1, 0,
else
val = mu_msg_get_body_text(msgwrap->_msg);
return val ? scm_from_utf8_string (val) : SCM_UNSPECIFIED;
return scm_from_string_or_null (val);
}
#undef FUNC_NAME
@ -377,7 +371,7 @@ SCM_DEFINE (msg_header, "mu:msg:header", 1, 1, 0,
header = scm_to_utf8_string (HEADER);
val = mu_msg_get_header(msgwrap->_msg, header);
return val ? scm_from_utf8_string(val) : SCM_UNDEFINED;
return val ? scm_from_string_or_null(val) : SCM_UNDEFINED;
}
#undef FUNC_NAME
@ -394,7 +388,8 @@ msg_string_list_field (SCM msg_smob, MuMsgFieldId mfid)
for (scmlst = SCM_EOL; lst;
lst = g_slist_next(lst)) {
SCM item;
item = scm_list_1 (scm_from_utf8_string((const char*)lst->data));
item = scm_list_1
(scm_from_string_or_null((const char*)lst->data));
scmlst = scm_append_x (scm_list_2(scmlst, item));
}

View File

@ -34,7 +34,7 @@ get_query (void)
err = NULL;
query = mu_query_new (mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), &err);
if (err) {
mu_guile_g_error ("<internal>", err);
mu_guile_g_error ("<internal error>", err);
g_error_free (err);
return NULL;
}
@ -53,7 +53,7 @@ get_query_iter (MuQuery *query, const char* expr)
iter = mu_query_run (query, expr,
FALSE, MU_MSG_FIELD_ID_NONE, TRUE, &err);
if (err) {
mu_guile_g_error ("<internal>", err);
mu_guile_g_error ("<internal error>", err);
g_error_free (err);
return NULL;
}
@ -62,7 +62,7 @@ get_query_iter (MuQuery *query, const char* expr)
}
SCM_DEFINE (store_foreach, "mu:store:foreach", 1, 1, 0,
SCM_DEFINE (store_foreach, "mu:store:for-each", 1, 1, 0,
(SCM FUNC, SCM EXPR),
"Call FUNC for each message in the store, or, if EXPR is specified, "
"for each message matching EXPR.\n")

View File

@ -62,11 +62,115 @@
in your ~/.guile configuration.
** What about searching messages in the database?
That's easy, too - it does require a little more scheme knowledge. For
searching messages there is the mu:store:for-each function, which takes two
arguments; the first argument is a function that will be called for each
message found. The optional second argument is the search expression (following
'mu find' syntax); if don't provide the argument, all messages match.
So how does this work in practice? Let's see I want to see the subject and
sender for messages about milk:
,----
| (mu:store:foreach (lambda(msg) (format #t "~s ~s\n" (mu:msg:from msg) (mu:msg:subject msg))) "milk")
`----
or slightly more readable:
,----
| (mu:store:foreach
| (lambda(msg)
| (format #t "~s ~s\n" (mu:msg:from msg) (mu:msg:subject msg)))
| "milk")
`----
As you can see, I provide an anonymous ('lambda') function which will be
called for each message matching 'milk'. Admittedly, this requires a bit of
Scheme-knowledge... but this time is good as any to learn this nice
language.
** Can I do some statistics on my messages?
Yes you can. It's pretty easy in guile. See the mu:stats functions.
Yes you can. In fact, it's pretty easy. If you load (in the muile/ directory)
the file 'mu-stats.scm':
,----
| (load "mu-stats.scm)
`----
you'll get a bunch of functions (with names starting with 'mu:stats') to make
this very easy. Let's see, suppose I want to see how many messages I get per
weekday:
,----
| scheme@(guile-user)> (mu:stats:per-weekday)
| $1 = ((0 . 2255) (1 . 2788) (2 . 2868) (3 . 2599) (4 . 2629) (5 . 2287) (6 . 1851))
`----
Note, Sunday=0, Monday=1 and so on. Apparently, I get/send most of e-mail on
Tuesdays, and least on Saturday.
And note that mu:stats:per-weekdays takes an optional search expression
argument, to limit the results to messages matching that, e.g., to only
consider messages related to emacs during this year:
,----
| scheme@(guile-user)> (mu:stats:per-weekday "emacs date:2011..now")
| $8 = ((0 . 54) (1 . 22) (2 . 46) (3 . 47) (4 . 39) (5 . 54) (6 . 50))
`----
There's also 'mu:stats:per-month', 'mu:stats:per-year', 'mu:stats:per-hour'.
I learnt that during 3-4am I sent/receive only about a third of what I sent
during 11-12pm.
** What about getting the top-10 people in the To:-field?
Easy.
,----
| scheme@(guile-user)> (mu:stats:top-n-to)
| $1 = ((("Abc" "myself@example.com") . 4465) (("Def" "somebodyelse@example.com") . 2114)
| (and so on)
`----
I've changed the names a bit to protect the innocent, but what the function
does is return a list of pairs of
(<name> <email>) . <frequency>
descending in order of frequency. Note, 'mu:stats:top-n-to' takes two
optional arguments - first the 'n' in top-n (default is 10), and seconds as
search expression to limit the messages considered.
There are also the functions 'mu:stats:top-n-subject' and
'mu:stats:top-n-from' which do the same, mutatis mutandis, and it's quite
easy to add your own (see the mu-stats.scm for examples)
** What about showing the results in a table?
Even easier. Try:
,----
| (mu:stats:table (mu:stats:top-n-to))
`----
or
,----
| (mu:stats:table (mu:stats:per-weekday))
`----
You can also export the table:
,----
| (mu:stats:export (mu:stats:per-weekday))
`----
which will create a temporary file with the results, for further processing
in e.g. 'R' or 'gnuplot'.
[1] http://www.gnu.org/s/guile/

View File

@ -24,14 +24,14 @@
(define* (mu:stats:count #:optional (EXPR ""))
"Count the total number of messages. If the optional EXPR is
provided, only count the messages that match it.\n"
(mu:store:foreach (lambda(msg) #f) EXPR))
(mu:store:for-each (lambda(msg) #f) EXPR))
(define* (mu:stats:average FUNC #:optional (EXPR ""))
"Count the average of the result of applying FUNC on all
messages. If the optional EXPR is provided, only consider the messages
that match it.\n"
(let* ((sum 0)
(n (mu:store:foreach
(n (mu:store:for-each
(lambda(msg) (set! sum (+ sum (FUNC msg)))) EXPR)))
(if (= n 0) 0 (exact->inexact (/ sum n)))))
@ -50,12 +50,12 @@ it.\n"
(length (mu:msg:bcc msg)))) EXPR))
(define* (mu:stats:frequency FUNC #:optional (EXPR ""))
"FUNC is a function that takes a Msg, and returns the frequency of
"FUNC is a function that takes a mMsg, and returns the frequency of
the different values this function returns. If FUNC returns a list,
update the frequency table for each element of this list. If the
optional EXPR is provided, only consider messages that match it.\n"
(let ((table '()))
(mu:store:foreach
(mu:store:for-each
(lambda(msg)
;; note, if val is not already a list, turn it into a list
;; then, take frequency for each element in the list
@ -148,7 +148,8 @@ that match it."
pairs)))
(define (mu:stats:export pairs)
"export pairs to a temporary file, return its name"
"Export PAIRS to a temporary file, return its name. The data can
then be used in, e.g., R and gnuplot."
(let* ((datafile (tmpnam))
(output (open datafile (logior O_CREAT O_WRONLY) #O0644)))
(mu:stats:table pairs output)