* 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
|
#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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user