From 8b824da53965aaa403915383c1e46a071dec811f Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sun, 17 Jul 2011 14:37:12 +0300 Subject: [PATCH] * update the guile-bindings and README a bit --- libmuguile/mu-guile-msg.c | 35 +++++------ libmuguile/mu-guile-store.c | 6 +- toys/muile/README | 112 ++++++++++++++++++++++++++++++++++-- toys/muile/mu-stats.scm | 11 ++-- 4 files changed, 132 insertions(+), 32 deletions(-) diff --git a/libmuguile/mu-guile-msg.c b/libmuguile/mu-guile-msg.c index e45e0c8f..ca6f00c0 100644 --- a/libmuguile/mu-guile-msg.c +++ b/libmuguile/mu-guile-msg.c @@ -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(""); - 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)); } diff --git a/libmuguile/mu-guile-store.c b/libmuguile/mu-guile-store.c index 77e0f66e..f9aac54f 100644 --- a/libmuguile/mu-guile-store.c +++ b/libmuguile/mu-guile-store.c @@ -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 ("", err); + mu_guile_g_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 ("", err); + mu_guile_g_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") diff --git a/toys/muile/README b/toys/muile/README index 177947a9..d82297a4 100644 --- a/toys/muile/README +++ b/toys/muile/README @@ -61,15 +61,119 @@ (activate-readline) 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 + + ( ) . + + 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) # Local Variables: diff --git a/toys/muile/mu-stats.scm b/toys/muile/mu-stats.scm index 316d430e..20d98478 100644 --- a/toys/muile/mu-stats.scm +++ b/toys/muile/mu-stats.scm @@ -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)