* update the guile-bindings and README a bit
This commit is contained in:
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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/
|
||||
|
||||
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user