* 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 #undef FUNC_NAME
static SCM
scm_from_string_or_null (const char *str)
{
return str ? scm_from_utf8_string (str) : SCM_UNSPECIFIED;
}
static SCM static SCM
msg_str_field (SCM msg_smob, MuMsgFieldId mfid) msg_str_field (SCM msg_smob, MuMsgFieldId mfid)
{ {
const char *val, *endptr;
MuMsgWrapper *msgwrap; MuMsgWrapper *msgwrap;
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob); msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
val = mu_msg_get_field_string(msgwrap->_msg, mfid); return scm_from_string_or_null (
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;
} }
static gint64 static gint64
@ -240,9 +235,8 @@ contacts_to_list (MuMsgContact *contact, EachContactData *ecdata)
item = scm_list_1 item = scm_list_1
(scm_list_2 ( (scm_list_2 (
name ? scm_from_utf8_string(name) : SCM_UNSPECIFIED, scm_from_string_or_null(name),
addr ? scm_from_utf8_string(addr) : SCM_UNSPECIFIED)); scm_from_string_or_null(addr)));
ecdata->lst = scm_append_x (scm_list_2(ecdata->lst, item)); 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 else
val = mu_msg_get_body_text(msgwrap->_msg); 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 #undef FUNC_NAME
@ -377,7 +371,7 @@ SCM_DEFINE (msg_header, "mu:msg:header", 1, 1, 0,
header = scm_to_utf8_string (HEADER); header = scm_to_utf8_string (HEADER);
val = mu_msg_get_header(msgwrap->_msg, 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 #undef FUNC_NAME
@ -394,7 +388,8 @@ msg_string_list_field (SCM msg_smob, MuMsgFieldId mfid)
for (scmlst = SCM_EOL; lst; for (scmlst = SCM_EOL; lst;
lst = g_slist_next(lst)) { lst = g_slist_next(lst)) {
SCM item; 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)); scmlst = scm_append_x (scm_list_2(scmlst, item));
} }

View File

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

View File

@ -61,15 +61,119 @@
(activate-readline) (activate-readline)
in your ~/.guile configuration. 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? ** 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/
[1] http://www.gnu.org/s/guile/
[2] http://en.wikipedia.org/wiki/Scheme_(programming_language) [2] http://en.wikipedia.org/wiki/Scheme_(programming_language)
# Local Variables: # Local Variables:

View File

@ -24,14 +24,14 @@
(define* (mu:stats:count #:optional (EXPR "")) (define* (mu:stats:count #:optional (EXPR ""))
"Count the total number of messages. If the optional EXPR is "Count the total number of messages. If the optional EXPR is
provided, only count the messages that match it.\n" 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 "")) (define* (mu:stats:average FUNC #:optional (EXPR ""))
"Count the average of the result of applying FUNC on all "Count the average of the result of applying FUNC on all
messages. If the optional EXPR is provided, only consider the messages messages. If the optional EXPR is provided, only consider the messages
that match it.\n" that match it.\n"
(let* ((sum 0) (let* ((sum 0)
(n (mu:store:foreach (n (mu:store:for-each
(lambda(msg) (set! sum (+ sum (FUNC msg)))) EXPR))) (lambda(msg) (set! sum (+ sum (FUNC msg)))) EXPR)))
(if (= n 0) 0 (exact->inexact (/ sum n))))) (if (= n 0) 0 (exact->inexact (/ sum n)))))
@ -50,12 +50,12 @@ it.\n"
(length (mu:msg:bcc msg)))) EXPR)) (length (mu:msg:bcc msg)))) EXPR))
(define* (mu:stats:frequency FUNC #:optional (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, the different values this function returns. If FUNC returns a list,
update the frequency table for each element of this list. If the update the frequency table for each element of this list. If the
optional EXPR is provided, only consider messages that match it.\n" optional EXPR is provided, only consider messages that match it.\n"
(let ((table '())) (let ((table '()))
(mu:store:foreach (mu:store:for-each
(lambda(msg) (lambda(msg)
;; note, if val is not already a list, turn it into a list ;; note, if val is not already a list, turn it into a list
;; then, take frequency for each element in the list ;; then, take frequency for each element in the list
@ -148,7 +148,8 @@ that match it."
pairs))) pairs)))
(define (mu:stats:export 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)) (let* ((datafile (tmpnam))
(output (open datafile (logior O_CREAT O_WRONLY) #O0644))) (output (open datafile (logior O_CREAT O_WRONLY) #O0644)))
(mu:stats:table pairs output) (mu:stats:table pairs output)