gnucash master: Multiple changes pushed
Geert Janssens
gjanssens at code.gnucash.org
Sun Mar 13 05:49:10 EDT 2016
Updated via https://github.com/Gnucash/gnucash/commit/de8e2db3 (commit)
via https://github.com/Gnucash/gnucash/commit/a4be710f (commit)
via https://github.com/Gnucash/gnucash/commit/99d0b03e (commit)
via https://github.com/Gnucash/gnucash/commit/b714df4c (commit)
via https://github.com/Gnucash/gnucash/commit/80e20256 (commit)
via https://github.com/Gnucash/gnucash/commit/213db2de (commit)
via https://github.com/Gnucash/gnucash/commit/427d62e8 (commit)
via https://github.com/Gnucash/gnucash/commit/f8090fa7 (commit)
via https://github.com/Gnucash/gnucash/commit/4e2e45e8 (commit)
via https://github.com/Gnucash/gnucash/commit/cc132ce9 (commit)
via https://github.com/Gnucash/gnucash/commit/c709d502 (commit)
via https://github.com/Gnucash/gnucash/commit/dae07fac (commit)
via https://github.com/Gnucash/gnucash/commit/57a0b46b (commit)
via https://github.com/Gnucash/gnucash/commit/82585bd8 (commit)
via https://github.com/Gnucash/gnucash/commit/fd233545 (commit)
from https://github.com/Gnucash/gnucash/commit/71f7f457 (commit)
commit de8e2db3b2b33b63fedde057b3b7d4edf11b4c8a
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Fri Mar 11 09:25:26 2016 +0000
Change look_for_old_mapping to return NULL
look_for_old_mapping should return NULL when we find an existing
map_account
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 944b75c..9a3638a 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5849,7 +5849,7 @@ look_for_old_mapping (GncImapInfo *imapInfo)
// do we have a map_account all ready, implying a guid string
if (imapInfo->map_account != NULL)
- return map_account;
+ return NULL;
root = gnc_account_get_root (imapInfo->source_account);
commit a4be710fdd185fd56f19cfebbab5b9bcff2eee21
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Thu Mar 10 17:27:57 2016 +0000
Rename some function to better reflect there use
Change some function names to better reflect there use and change
look_for_old_mapping to return map_account and use this to see if we
need to convert entry
diff --git a/src/engine/Account.c b/src/engine/Account.c
index e5fa8b4..944b75c 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5795,7 +5795,7 @@ look_for_old_separator_descendants (Account *root, gchar *full_name, const gchar
static void
-update_imap_entry (Account *map_account, GncImapInfo *imapInfo)
+convert_imap_entry (GncImapInfo *imapInfo, Account *map_account)
{
GncImportMatchMap *imap;
gchar *guid_string;
@@ -5838,10 +5838,10 @@ update_imap_entry (Account *map_account, GncImapInfo *imapInfo)
}
-static void
-convert_imap_entry (Account *root, GncImapInfo *imapInfo)
+static Account *
+look_for_old_mapping (GncImapInfo *imapInfo)
{
- Account *map_account = NULL;
+ Account *root, *map_account = NULL;
const gchar *sep = gnc_get_account_separator_string ();
gchar *full_name;
@@ -5849,7 +5849,9 @@ convert_imap_entry (Account *root, GncImapInfo *imapInfo)
// do we have a map_account all ready, implying a guid string
if (imapInfo->map_account != NULL)
- return;
+ return map_account;
+
+ root = gnc_account_get_root (imapInfo->source_account);
full_name = g_strdup (imapInfo->full_category + strlen (imapInfo->category_head) + 1);
@@ -5865,14 +5867,13 @@ convert_imap_entry (Account *root, GncImapInfo *imapInfo)
PINFO("Full account name is '%s'", full_name);
- if (map_account != NULL) // we have an account, try and update it
- update_imap_entry (map_account, imapInfo);
-
g_free (full_name);
+
+ return map_account;
}
static void
-get_account_imap_info (Account *root, Account *acc)
+convert_imap_account (Account *acc)
{
GList *imap_list, *node;
gchar *acc_name = NULL;
@@ -5888,10 +5889,14 @@ get_account_imap_info (Account *root, Account *acc)
for (node = imap_list; node; node = g_list_next (node))
{
+ Account *map_account = NULL;
GncImapInfo *imapInfo = node->data;
// Lets start doing stuff
- convert_imap_entry (root, imapInfo);
+ map_account = look_for_old_mapping (imapInfo);
+
+ if (map_account != NULL) // we have an account, try and update it
+ convert_imap_entry (imapInfo, map_account);
// Free the members and structure
g_free (imapInfo->category_head);
@@ -5932,7 +5937,7 @@ gnc_account_imap_convert_bayes (QofBook *book)
{
Account *acc = ptr->data;
- get_account_imap_info (root, acc);
+ convert_imap_account (acc);
}
g_list_free (accts);
commit 99d0b03e0f38a151ac02cc6c273ddf6bb7771d62
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Thu Mar 10 17:16:05 2016 +0000
Remove unnecessary use of g_strdup of full_name
Remove the unnecessary use of g_strdup on full_name and access it
directly
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 79ed0c4..e5fa8b4 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5755,7 +5755,6 @@ static gchar *
look_for_old_separator_descendants (Account *root, gchar *full_name, const gchar *separator)
{
GList *top_accounts, *ptr;
- gchar *converted_name = g_strdup (full_name);
gint found_len = 0;
gchar found_sep;
@@ -5769,30 +5768,29 @@ look_for_old_separator_descendants (Account *root, gchar *full_name, const gchar
const gchar *name = xaccAccountGetName (ptr->data);
// we are looking for the longest top level account that matches
- if (g_str_has_prefix (converted_name, name))
+ if (g_str_has_prefix (full_name, name))
{
gint name_len = strlen (name);
- const gchar old_sep = converted_name[name_len];
+ const gchar old_sep = full_name[name_len];
if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
{
if (name_len > found_len)
{
- found_sep = converted_name[name_len];
+ found_sep = full_name[name_len];
found_len = name_len;
}
}
}
}
g_list_free (top_accounts); // Free the List
- g_free (full_name);
if (found_len > 1)
- converted_name = g_strdelimit (converted_name, &found_sep, *separator);
+ full_name = g_strdelimit (full_name, &found_sep, *separator);
- PINFO("Return full_name is '%s'", converted_name);
+ PINFO("Return full_name is '%s'", full_name);
- return converted_name;
+ return full_name;
}
commit b714df4c8c1458b17b488bfaeab31b89e10477a2
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Sun Mar 13 10:04:03 2016 +0100
Change some functions to reduce code duplication
and move feature setting to where KVP is actually
set
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 0d391fa..79ed0c4 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5427,6 +5427,45 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
}
+static void
+change_imap_entry (GncImportMatchMap *imap, gchar *kvp_path, int64_t token_count)
+{
+ GValue value = G_VALUE_INIT;
+
+ PINFO("Source Account is '%s', kvp_path is '%s', Count is '%" G_GINT64_FORMAT "'",
+ xaccAccountGetName (imap->acc), kvp_path, token_count);
+
+ // check for existing guid entry
+ if (qof_instance_has_slot (QOF_INSTANCE(imap->acc), kvp_path))
+ {
+ int64_t existing_token_count = 0;
+
+ // get the existing_token_count value
+ qof_instance_get_kvp (QOF_INSTANCE (imap->acc), kvp_path, &value);
+
+ if (G_VALUE_HOLDS_INT64 (&value))
+ existing_token_count = g_value_get_int64 (&value);
+
+ PINFO("found existing value of '%" G_GINT64_FORMAT "'", existing_token_count);
+
+ token_count = token_count + existing_token_count;
+ }
+
+ if (!G_IS_VALUE (&value))
+ g_value_init (&value, G_TYPE_INT64);
+
+ g_value_set_int64 (&value, token_count);
+
+ // Add or Update the entry based on guid
+ qof_instance_set_kvp (QOF_INSTANCE (imap->acc), kvp_path, &value);
+
+ /* Set a feature flag in the book for the change to use guid.
+ * This will prevent older GnuCash versions that don't support this feature
+ * from opening this file. */
+ gnc_features_set_used (imap->book, GNC_FEATURE_GUID_BAYESIAN);
+}
+
+
/** Updates the imap for a given account using a list of tokens */
void
gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
@@ -5465,8 +5504,8 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
if (!current_token->data || (*((char*)current_token->data) == '\0'))
continue;
- /* start off with no tokens for this account */
- token_count = 0;
+ /* start off with one token for this account */
+ token_count = 1;
PINFO("adding token '%s'", (char*)current_token->data);
@@ -5474,23 +5513,9 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
(char*)current_token->data,
guid_string);
- qof_instance_get_kvp (QOF_INSTANCE (imap->acc), kvp_path, &value);
- /* if the token/account is already in the tree, read the current
- * value from the tree and use this for the basis of the value we
- * are putting back
- */
- if (G_VALUE_HOLDS_INT64 (&value))
- {
- int64_t count = g_value_get_int64 (&value);
- PINFO("found existing value of '%" G_GINT64_FORMAT "'", count);
+ /* change the imap entry for the account */
+ change_imap_entry (imap, kvp_path, token_count);
- token_count += count;
- }
- token_count++;
- if (!G_IS_VALUE (&value))
- g_value_init (&value, G_TYPE_INT64);
- g_value_set_int64 (&value, token_count);
- qof_instance_set_kvp (QOF_INSTANCE (imap->acc), kvp_path, &value);
g_free (kvp_path);
}
@@ -5770,8 +5795,53 @@ look_for_old_separator_descendants (Account *root, gchar *full_name, const gchar
return converted_name;
}
+
+static void
+update_imap_entry (Account *map_account, GncImapInfo *imapInfo)
+{
+ GncImportMatchMap *imap;
+ gchar *guid_string;
+ gchar *kvp_path;
+ int64_t token_count = 1;
+
+ GValue value = G_VALUE_INIT;
+
+ // Create an ImportMatchMap object
+ imap = gnc_account_imap_create_imap (imapInfo->source_account);
+
+ xaccAccountBeginEdit (imapInfo->source_account);
+
+ guid_string = guid_to_string (xaccAccountGetGUID (map_account));
+
+ PINFO("Map Account is '%s', GUID is '%s', Count is %s", xaccAccountGetName (map_account),
+ guid_string, imapInfo->count);
+
+ // save converting string, get the count value
+ qof_instance_get_kvp (QOF_INSTANCE (imapInfo->source_account), imapInfo->full_category, &value);
+
+ if (G_VALUE_HOLDS_INT64 (&value))
+ token_count = g_value_get_int64 (&value);
+
+ // Delete the old entry based on full account name
+ kvp_path = g_strdup (imapInfo->full_category);
+ gnc_account_delete_map_entry (imapInfo->source_account, kvp_path, FALSE);
+
+ // create path based on guid
+ kvp_path = g_strdup_printf ("/%s/%s", imapInfo->category_head, guid_string);
+
+ // change the imap entry of source_account
+ change_imap_entry (imap, kvp_path, token_count);
+
+ qof_instance_set_dirty (QOF_INSTANCE (imapInfo->source_account));
+ xaccAccountCommitEdit (imapInfo->source_account);
+
+ g_free (kvp_path);
+ g_free (guid_string);
+}
+
+
static void
-change_imap_entry (Account *root, GncImapInfo *imapInfo)
+convert_imap_entry (Account *root, GncImapInfo *imapInfo)
{
Account *map_account = NULL;
const gchar *sep = gnc_get_account_separator_string ();
@@ -5797,58 +5867,9 @@ change_imap_entry (Account *root, GncImapInfo *imapInfo)
PINFO("Full account name is '%s'", full_name);
- if (map_account != NULL) // we have an account, try and convert
- {
- gchar *guid_string;
- gchar *kvp_path;
- int64_t count = 1;
-
- GValue value = G_VALUE_INIT;
-
- xaccAccountBeginEdit (imapInfo->source_account);
-
- guid_string = guid_to_string (xaccAccountGetGUID (map_account));
-
- PINFO("Map Account is '%s',GUID is '%s', Count is %s", xaccAccountGetName (map_account),
- guid_string, imapInfo->count);
-
- // save converting string, get the count value
- qof_instance_get_kvp (QOF_INSTANCE (imapInfo->source_account), imapInfo->full_category, &value);
+ if (map_account != NULL) // we have an account, try and update it
+ update_imap_entry (map_account, imapInfo);
- if (G_VALUE_HOLDS_INT64 (&value))
- count = g_value_get_int64 (&value);
-
- // Delete the old entry based on full account name
- kvp_path = g_strdup (imapInfo->full_category);
- gnc_account_delete_map_entry (imapInfo->source_account, kvp_path, FALSE);
-
- // create path based on guid
- kvp_path = g_strdup_printf ("/%s/%s", imapInfo->category_head, guid_string);
-
- // check for existing guid entry
- if (qof_instance_has_slot (QOF_INSTANCE(imapInfo->source_account), kvp_path))
- {
- int64_t existing_count = 0;
-
- // get the count value
- qof_instance_get_kvp (QOF_INSTANCE (imapInfo->source_account), kvp_path, &value);
-
- if (G_VALUE_HOLDS_INT64 (&value))
- existing_count = g_value_get_int64 (&value);
-
- count = count + existing_count;
- }
- g_value_set_int64 (&value, count);
-
- // Add or Update the entry based on guid
- qof_instance_set_kvp (QOF_INSTANCE (imapInfo->source_account), kvp_path, &value);
-
- qof_instance_set_dirty (QOF_INSTANCE (imapInfo->source_account));
- xaccAccountCommitEdit (imapInfo->source_account);
-
- g_free (kvp_path);
- g_free (guid_string);
- }
g_free (full_name);
}
@@ -5863,7 +5884,7 @@ get_account_imap_info (Account *root, Account *acc)
imap_list = gnc_account_imap_get_info_bayes (acc);
- if (g_list_length (imap_list) > 0)
+ if (g_list_length (imap_list) > 0) // we have mappings
{
PINFO("List length is %d", g_list_length (imap_list));
@@ -5872,7 +5893,7 @@ get_account_imap_info (Account *root, Account *acc)
GncImapInfo *imapInfo = node->data;
// Lets start doing stuff
- change_imap_entry (root, imapInfo);
+ convert_imap_entry (root, imapInfo);
// Free the members and structure
g_free (imapInfo->category_head);
@@ -5923,11 +5944,6 @@ gnc_account_imap_convert_bayes (QofBook *book)
// set the run-once value
qof_instance_set_kvp (QOF_INSTANCE (book), "changed-bayesian-to-guid", &value_b);
-
- /* Set a feature flag in the book for the change to use guid.
- * This will prevent older GnuCash versions that don't support this feature
- * from opening this file. */
- gnc_features_set_used (book, GNC_FEATURE_GUID_BAYESIAN);
}
}
commit 80e20256ad811dd1e3528c32c2acc9d52c42433a
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Sun Mar 13 09:46:13 2016 +0100
Add feature flag to prevent versions older than 2.6.12 from opening the data file.
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 5598f6e..0d391fa 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -40,6 +40,7 @@
#include "gnc-lot.h"
#include "gnc-pricedb.h"
#include "qofinstance-p.h"
+#include "gnc-features.h"
static QofLogModule log_module = GNC_MOD_ACCOUNT;
@@ -5922,7 +5923,11 @@ gnc_account_imap_convert_bayes (QofBook *book)
// set the run-once value
qof_instance_set_kvp (QOF_INSTANCE (book), "changed-bayesian-to-guid", &value_b);
- qof_instance_set_dirty (QOF_INSTANCE (book));
+
+ /* Set a feature flag in the book for the change to use guid.
+ * This will prevent older GnuCash versions that don't support this feature
+ * from opening this file. */
+ gnc_features_set_used (book, GNC_FEATURE_GUID_BAYESIAN);
}
}
commit 213db2de52b4078255bb8921a2be3951be45c2f8
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Mar 7 17:09:35 2016 +0000
Use GncImapInfo instead of struct imap_info where
it makes sense to.
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 92e6ccd..5598f6e 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5639,7 +5639,7 @@ gnc_account_imap_get_info_bayes (Account *acc)
{
GList *list = NULL;
- struct imap_info imapInfo;
+ GncImapInfo imapInfo;
imapInfo.source_account = acc;
imapInfo.list = list;
@@ -5658,7 +5658,7 @@ gnc_account_imap_get_info (Account *acc, const char *category)
GList *list = NULL;
gchar *category_head = NULL;
- struct imap_info imapInfo;
+ GncImapInfo imapInfo;
imapInfo.source_account = acc;
imapInfo.list = list;
@@ -5770,14 +5770,12 @@ look_for_old_separator_descendants (Account *root, gchar *full_name, const gchar
}
static void
-change_imap_entry (Account *root, gpointer user_data)
+change_imap_entry (Account *root, GncImapInfo *imapInfo)
{
Account *map_account = NULL;
const gchar *sep = gnc_get_account_separator_string ();
gchar *full_name;
- struct imap_info *imapInfo = (struct imap_info*)user_data;
-
PINFO("Category Head is '%s', Full Category is '%s'", imapInfo->category_head, imapInfo->full_category);
// do we have a map_account all ready, implying a guid string
@@ -5870,7 +5868,7 @@ get_account_imap_info (Account *root, Account *acc)
for (node = imap_list; node; node = g_list_next (node))
{
- struct imap_info *imapInfo = node->data;
+ GncImapInfo *imapInfo = node->data;
// Lets start doing stuff
change_imap_entry (root, imapInfo);
diff --git a/src/engine/Account.h b/src/engine/Account.h
index 4c9a2e3..259b504 100644
--- a/src/engine/Account.h
+++ b/src/engine/Account.h
@@ -1406,7 +1406,7 @@ Account* gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList* to
void gnc_account_imap_add_account_bayes (GncImportMatchMap *imap, GList* tokens,
Account *acc);
-struct imap_info
+typedef struct imap_info
{
Account *source_account;
Account *map_account;
@@ -1415,7 +1415,7 @@ struct imap_info
char *full_category;
char *match_string;
char *count;
-};
+}GncImapInfo;
/** Returns a GList of structure imap_info of all Bayesian mappings for
* required Account
diff --git a/src/gnome/dialog-imap-editor.c b/src/gnome/dialog-imap-editor.c
index 962788f..80136b9 100644
--- a/src/gnome/dialog-imap-editor.c
+++ b/src/gnome/dialog-imap-editor.c
@@ -400,13 +400,11 @@ show_count_column (ImapDialog *imap_dialog, gboolean show)
}
static void
-add_to_store (GtkTreeModel *model, GtkTreeIter *iter, const gchar *text, gpointer user_data)
+add_to_store (GtkTreeModel *model, GtkTreeIter *iter, const gchar *text, GncImapInfo *imapInfo)
{
gchar *fullname = NULL;
gchar *map_fullname = NULL;
- struct imap_info *imapInfo = (struct imap_info*)user_data;
-
fullname = gnc_account_get_full_name (imapInfo->source_account);
// Do we have a valid map account
@@ -455,7 +453,7 @@ get_imap_info (Account *acc, const gchar *category, GtkTreeModel *model, const g
for (node = imap_list; node; node = g_list_next (node))
{
- struct imap_info *imapInfo = node->data;
+ GncImapInfo *imapInfo = node->data;
// First add a child entry and pass iter to add_to_store
gtk_tree_store_append (GTK_TREE_STORE(model), &child, &toplevel);
@@ -533,7 +531,7 @@ get_account_info_online (GList *accts, GtkTreeModel *model)
GList *ptr;
GtkTreeIter toplevel;
- struct imap_info imapInfo;
+ GncImapInfo imapInfo;
/* Go through list of accounts */
for (ptr = accts; ptr; ptr = g_list_next (ptr))
commit 427d62e805b25b9165731127490d5498dfb495c4
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Mar 7 13:34:07 2016 +0000
Change when an existing entry exists we add it to
the converted entry
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 91a33f6..92e6ccd 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5829,7 +5829,7 @@ change_imap_entry (Account *root, gpointer user_data)
// check for existing guid entry
if (qof_instance_has_slot (QOF_INSTANCE(imapInfo->source_account), kvp_path))
{
- int64_t existing_count = 1;
+ int64_t existing_count = 0;
// get the count value
qof_instance_get_kvp (QOF_INSTANCE (imapInfo->source_account), kvp_path, &value);
@@ -5837,9 +5837,7 @@ change_imap_entry (Account *root, gpointer user_data)
if (G_VALUE_HOLDS_INT64 (&value))
existing_count = g_value_get_int64 (&value);
- // if existing_count is greater, use that one
- if (existing_count > count)
- count = existing_count;
+ count = count + existing_count;
}
g_value_set_int64 (&value, count);
commit f8090fa78e68804d711aed4d353d210b0cef1531
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Mar 7 12:32:26 2016 +0000
Improve old separator recognition by looking for
the longest top level entry that matches the
prefix. Also test for valid map account so we are
not wasting time trying to convert a valid
full_name.
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 8506e6d..91a33f6 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5726,33 +5726,44 @@ gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean empty)
/*******************************************************************************/
static gchar *
-look_for_old_separator (Account *root, gchar *full_name, const gchar *separator)
+look_for_old_separator_descendants (Account *root, gchar *full_name, const gchar *separator)
{
GList *top_accounts, *ptr;
gchar *converted_name = g_strdup (full_name);
+ gint found_len = 0;
+ gchar found_sep;
top_accounts = gnc_account_get_descendants (root);
- PINFO("Incoming full_name is '%s'", full_name);
+ PINFO("Incoming full_name is '%s', current separator is '%s'", full_name, separator);
/* Go through list of top level accounts */
for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
{
const gchar *name = xaccAccountGetName (ptr->data);
+ // we are looking for the longest top level account that matches
if (g_str_has_prefix (converted_name, name))
{
- const gchar old_sep = converted_name[strlen (name)];
+ gint name_len = strlen (name);
+ const gchar old_sep = converted_name[name_len];
if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
- converted_name = g_strdelimit (converted_name, &old_sep, *separator);
-
- break;
+ {
+ if (name_len > found_len)
+ {
+ found_sep = converted_name[name_len];
+ found_len = name_len;
+ }
+ }
}
}
g_list_free (top_accounts); // Free the List
g_free (full_name);
+ if (found_len > 1)
+ converted_name = g_strdelimit (converted_name, &found_sep, *separator);
+
PINFO("Return full_name is '%s'", converted_name);
return converted_name;
@@ -5769,16 +5780,25 @@ change_imap_entry (Account *root, gpointer user_data)
PINFO("Category Head is '%s', Full Category is '%s'", imapInfo->category_head, imapInfo->full_category);
+ // do we have a map_account all ready, implying a guid string
+ if (imapInfo->map_account != NULL)
+ return;
+
full_name = g_strdup (imapInfo->full_category + strlen (imapInfo->category_head) + 1);
- if (g_strstr_len (full_name, -1, sep) == NULL) // does full_name contain a different account separator
- full_name = look_for_old_separator (root, full_name, sep);
+ // may be top level or match with existing separator
+ map_account = gnc_account_lookup_by_full_name (root, full_name);
- PINFO("full name is '%s'", full_name);
+ // do we have a valid account, if not, look for old separator
+ if (map_account == NULL)
+ {
+ full_name = look_for_old_separator_descendants (root, full_name, sep);
+ map_account = gnc_account_lookup_by_full_name (root, full_name); // lets try again
+ }
- map_account = gnc_account_lookup_by_full_name (root, full_name);
+ PINFO("Full account name is '%s'", full_name);
- if (map_account != NULL)
+ if (map_account != NULL) // we have an account, try and convert
{
gchar *guid_string;
gchar *kvp_path;
commit 4e2e45e87133445df44091f458766cc9349d210a
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Feb 29 12:51:40 2016 +0000
Modify gnc_account_imap_convert_bayes to run only
once by setting a KVP Book entry and then testing
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 639713b..8506e6d 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5874,19 +5874,40 @@ gnc_account_imap_convert_bayes (QofBook *book)
{
Account *root;
GList *accts, *ptr;
+ gboolean run_once = FALSE;
+ GValue value_s = G_VALUE_INIT;
- /* Get list of Accounts */
- root = gnc_book_get_root_account (book);
- accts = gnc_account_get_descendants_sorted (root);
+ // get the run-once value
+ qof_instance_get_kvp (QOF_INSTANCE (book), "changed-bayesian-to-guid", &value_s);
- /* Go through list of accounts */
- for (ptr = accts; ptr; ptr = g_list_next (ptr))
+ if (G_VALUE_HOLDS_STRING (&value_s) && (strcmp(g_value_get_string (&value_s), "true") == 0))
+ run_once = TRUE;
+
+ if (run_once == FALSE)
{
- Account *acc = ptr->data;
+ GValue value_b = G_VALUE_INIT;
+
+ /* Get list of Accounts */
+ root = gnc_book_get_root_account (book);
+ accts = gnc_account_get_descendants_sorted (root);
+
+ /* Go through list of accounts */
+ for (ptr = accts; ptr; ptr = g_list_next (ptr))
+ {
+ Account *acc = ptr->data;
+
+ get_account_imap_info (root, acc);
+ }
+ g_list_free (accts);
+
+ g_value_init (&value_b, G_TYPE_BOOLEAN);
+
+ g_value_set_boolean (&value_b, TRUE);
- get_account_imap_info (root, acc);
+ // set the run-once value
+ qof_instance_set_kvp (QOF_INSTANCE (book), "changed-bayesian-to-guid", &value_b);
+ qof_instance_set_dirty (QOF_INSTANCE (book));
}
- g_list_free (accts);
}
commit cc132ce947cd02a6e3aabe0568229c4db79f5985
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Feb 29 12:49:58 2016 +0000
Add Bayesian conversion to end of file open
diff --git a/src/gnome-utils/gnc-file.c b/src/gnome-utils/gnc-file.c
index 62b253b..95521fa 100644
--- a/src/gnome-utils/gnc-file.c
+++ b/src/gnome-utils/gnc-file.c
@@ -1002,6 +1002,11 @@ RESTART:
g_free ( message );
}
+ // Convert imap mappings from account full name to guid strings
+ qof_event_suspend();
+ gnc_account_imap_convert_bayes (gnc_get_current_book());
+ qof_event_resume();
+
return TRUE;
}
commit c709d5024680c6499b4d5248939237c72aa5fd28
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Feb 29 09:46:37 2016 +0000
Check for existing guid entries and update
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 202eb8d..639713b 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5803,10 +5803,27 @@ change_imap_entry (Account *root, gpointer user_data)
kvp_path = g_strdup (imapInfo->full_category);
gnc_account_delete_map_entry (imapInfo->source_account, kvp_path, FALSE);
+ // create path based on guid
+ kvp_path = g_strdup_printf ("/%s/%s", imapInfo->category_head, guid_string);
+
+ // check for existing guid entry
+ if (qof_instance_has_slot (QOF_INSTANCE(imapInfo->source_account), kvp_path))
+ {
+ int64_t existing_count = 1;
+
+ // get the count value
+ qof_instance_get_kvp (QOF_INSTANCE (imapInfo->source_account), kvp_path, &value);
+
+ if (G_VALUE_HOLDS_INT64 (&value))
+ existing_count = g_value_get_int64 (&value);
+
+ // if existing_count is greater, use that one
+ if (existing_count > count)
+ count = existing_count;
+ }
g_value_set_int64 (&value, count);
- // Add new entry based on guid
- kvp_path = g_strdup_printf ("/%s/%s", imapInfo->category_head, guid_string);
+ // Add or Update the entry based on guid
qof_instance_set_kvp (QOF_INSTANCE (imapInfo->source_account), kvp_path, &value);
qof_instance_set_dirty (QOF_INSTANCE (imapInfo->source_account));
commit dae07facf069ca601c6fe4be88b283eb86212736
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Feb 29 09:45:29 2016 +0000
Create function to change Bayesian entries based
on full account name to that of the account guid
diff --git a/src/engine/Account.c b/src/engine/Account.c
index c64b58b..202eb8d 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5723,6 +5723,155 @@ gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean empty)
g_free (full_category);
}
+/*******************************************************************************/
+
+static gchar *
+look_for_old_separator (Account *root, gchar *full_name, const gchar *separator)
+{
+ GList *top_accounts, *ptr;
+ gchar *converted_name = g_strdup (full_name);
+
+ top_accounts = gnc_account_get_descendants (root);
+
+ PINFO("Incoming full_name is '%s'", full_name);
+
+ /* Go through list of top level accounts */
+ for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
+ {
+ const gchar *name = xaccAccountGetName (ptr->data);
+
+ if (g_str_has_prefix (converted_name, name))
+ {
+ const gchar old_sep = converted_name[strlen (name)];
+
+ if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
+ converted_name = g_strdelimit (converted_name, &old_sep, *separator);
+
+ break;
+ }
+ }
+ g_list_free (top_accounts); // Free the List
+ g_free (full_name);
+
+ PINFO("Return full_name is '%s'", converted_name);
+
+ return converted_name;
+}
+
+static void
+change_imap_entry (Account *root, gpointer user_data)
+{
+ Account *map_account = NULL;
+ const gchar *sep = gnc_get_account_separator_string ();
+ gchar *full_name;
+
+ struct imap_info *imapInfo = (struct imap_info*)user_data;
+
+ PINFO("Category Head is '%s', Full Category is '%s'", imapInfo->category_head, imapInfo->full_category);
+
+ full_name = g_strdup (imapInfo->full_category + strlen (imapInfo->category_head) + 1);
+
+ if (g_strstr_len (full_name, -1, sep) == NULL) // does full_name contain a different account separator
+ full_name = look_for_old_separator (root, full_name, sep);
+
+ PINFO("full name is '%s'", full_name);
+
+ map_account = gnc_account_lookup_by_full_name (root, full_name);
+
+ if (map_account != NULL)
+ {
+ gchar *guid_string;
+ gchar *kvp_path;
+ int64_t count = 1;
+
+ GValue value = G_VALUE_INIT;
+
+ xaccAccountBeginEdit (imapInfo->source_account);
+
+ guid_string = guid_to_string (xaccAccountGetGUID (map_account));
+
+ PINFO("Map Account is '%s',GUID is '%s', Count is %s", xaccAccountGetName (map_account),
+ guid_string, imapInfo->count);
+
+ // save converting string, get the count value
+ qof_instance_get_kvp (QOF_INSTANCE (imapInfo->source_account), imapInfo->full_category, &value);
+
+ if (G_VALUE_HOLDS_INT64 (&value))
+ count = g_value_get_int64 (&value);
+
+ // Delete the old entry based on full account name
+ kvp_path = g_strdup (imapInfo->full_category);
+ gnc_account_delete_map_entry (imapInfo->source_account, kvp_path, FALSE);
+
+ g_value_set_int64 (&value, count);
+
+ // Add new entry based on guid
+ kvp_path = g_strdup_printf ("/%s/%s", imapInfo->category_head, guid_string);
+ qof_instance_set_kvp (QOF_INSTANCE (imapInfo->source_account), kvp_path, &value);
+
+ qof_instance_set_dirty (QOF_INSTANCE (imapInfo->source_account));
+ xaccAccountCommitEdit (imapInfo->source_account);
+
+ g_free (kvp_path);
+ g_free (guid_string);
+ }
+ g_free (full_name);
+}
+
+static void
+get_account_imap_info (Account *root, Account *acc)
+{
+ GList *imap_list, *node;
+ gchar *acc_name = NULL;
+
+ acc_name = gnc_account_get_full_name (acc);
+ PINFO("Source Acc '%s'", acc_name);
+
+ imap_list = gnc_account_imap_get_info_bayes (acc);
+
+ if (g_list_length (imap_list) > 0)
+ {
+ PINFO("List length is %d", g_list_length (imap_list));
+
+ for (node = imap_list; node; node = g_list_next (node))
+ {
+ struct imap_info *imapInfo = node->data;
+
+ // Lets start doing stuff
+ change_imap_entry (root, imapInfo);
+
+ // Free the members and structure
+ g_free (imapInfo->category_head);
+ g_free (imapInfo->full_category);
+ g_free (imapInfo->match_string);
+ g_free (imapInfo->count);
+ g_free (imapInfo);
+ }
+ }
+ g_free (acc_name);
+ g_list_free (imap_list); // Free the List
+}
+
+void
+gnc_account_imap_convert_bayes (QofBook *book)
+{
+ Account *root;
+ GList *accts, *ptr;
+
+ /* Get list of Accounts */
+ root = gnc_book_get_root_account (book);
+ accts = gnc_account_get_descendants_sorted (root);
+
+ /* Go through list of accounts */
+ for (ptr = accts; ptr; ptr = g_list_next (ptr))
+ {
+ Account *acc = ptr->data;
+
+ get_account_imap_info (root, acc);
+ }
+ g_list_free (accts);
+}
+
/* ================================================================ */
/* QofObject function implementation and registration */
diff --git a/src/engine/Account.h b/src/engine/Account.h
index 067d015..4c9a2e3 100644
--- a/src/engine/Account.h
+++ b/src/engine/Account.h
@@ -1437,6 +1437,11 @@ gchar *gnc_account_get_map_entry (Account *acc, const char *full_category);
*/
void gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean empty);
+/** Search for Bayesian entries with mappings based on full account name and change
+ * them to be based on the account guid
+ */
+void gnc_account_imap_convert_bayes (QofBook *book);
+
/** @} */
commit 57a0b46b41b5023702ba9d9e23217e505f7b29cc
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Feb 29 09:44:10 2016 +0000
Modify Imap helper function to find map account by
guid
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 4bee130..c64b58b 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5508,7 +5508,8 @@ static void
build_bayes_layer_two (const char *key, const GValue *value, gpointer user_data)
{
QofBook *book;
- Account *root;
+ Account *map_account = NULL;
+ GncGUID *guid;
gchar *kvp_path;
gchar *count;
@@ -5518,21 +5519,32 @@ build_bayes_layer_two (const char *key, const GValue *value, gpointer user_data)
// Get the book
book = qof_instance_get_book (imapInfo->source_account);
- root = gnc_book_get_root_account (book);
- PINFO("build_bayes_layer_two: account '%s', token_count: '%" G_GINT64_FORMAT "'",
+ if (G_VALUE_HOLDS_INT64 (value))
+ {
+ PINFO("build_bayes_layer_two: account '%s', token_count: '%" G_GINT64_FORMAT "'",
(char*)key, g_value_get_int64(value));
- count = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value));
+ count = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value));
+ }
+ else
+ count = g_strdup ("0");
kvp_path = g_strconcat (imapInfo->category_head, "/", key, NULL);
PINFO("build_bayes_layer_two: kvp_path is '%s'", kvp_path);
+ guid = g_new (GncGUID, 1);
+
+ if (string_to_guid (key, guid))
+ map_account = xaccAccountLookup (guid, book);
+
+ g_free (guid);
+
imapInfo_node = g_malloc(sizeof(*imapInfo_node));
imapInfo_node->source_account = imapInfo->source_account;
- imapInfo_node->map_account = gnc_account_lookup_by_full_name (root, key);
+ imapInfo_node->map_account = map_account;
imapInfo_node->full_category = g_strdup (kvp_path);
imapInfo_node->match_string = g_strdup (imapInfo->match_string);
imapInfo_node->category_head = g_strdup (imapInfo->category_head);
commit 82585bd8c4be2176f963e86a3b5161428e1e7547
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Feb 29 09:43:16 2016 +0000
Rename account_name to account_guid to reflect
change of use and remove newlines from PINFO lines
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 1a05bb2..4bee130 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5137,8 +5137,8 @@ gnc_account_imap_delete_account (GncImportMatchMap *imap,
struct account_token_count
{
- char* account_name;
- gint64 token_count; /**< occurances of a given token for this account_name */
+ char* account_guid;
+ gint64 token_count; /**< occurances of a given token for this account_guid */
};
/** total_count and the token_count for a given account let us calculate the
@@ -5159,7 +5159,7 @@ buildTokenInfo(const char *key, const GValue *value, gpointer data)
struct token_accounts_info *tokenInfo = (struct token_accounts_info*)data;
struct account_token_count* this_account;
- // PINFO("buildTokenInfo: account '%s', token_count: '%" G_GINT64_FORMAT "'\n", (char*)key,
+ // PINFO("buildTokenInfo: account '%s', token_count: '%" G_GINT64_FORMAT "'", (char*)key,
// g_value_get_int64(value));
/* add the count to the total_count */
@@ -5169,8 +5169,8 @@ buildTokenInfo(const char *key, const GValue *value, gpointer data)
this_account = (struct account_token_count*)
g_new0(struct account_token_count, 1);
- /* fill in the account name and number of tokens found for this account name */
- this_account->account_name = (char*)key;
+ /* fill in the account guid and number of tokens found for this account guid */
+ this_account->account_guid = (char*)key;
this_account->token_count = g_value_get_int64(value);
/* append onto the glist a pointer to the new account_token_count structure */
@@ -5207,7 +5207,7 @@ buildProbabilities(gpointer key, gpointer value, gpointer data)
(account_p->product + account_p->product_difference))
* PROBABILITY_FACTOR;
- PINFO("P('%s') = '%d'\n", (char*)key, probability);
+ PINFO("P('%s') = '%d'", (char*)key, probability);
g_hash_table_insert(final_probabilities, key, GINT_TO_POINTER(probability));
}
@@ -5222,19 +5222,19 @@ freeProbabilities(gpointer key, gpointer value, gpointer data)
g_free(value);
}
-/** holds an account name and its corresponding integer probability
+/** holds an account guid and its corresponding integer probability
the integer probability is some factor of 10
*/
struct account_info
{
- char* account_name;
+ char* account_guid;
gint32 probability;
};
-/** Find the highest probability and the corresponding account name
+/** Find the highest probability and the corresponding account guid
store in data, a (struct account_info*)
NOTE: this is a g_hash_table_foreach() function for a hash table of entries
- key is a pointer to the account name, value is a gint32, 100000x
+ key is a pointer to the account guid, value is a gint32, 100000x
the probability for this account
*/
static void
@@ -5245,9 +5245,9 @@ highestProbability(gpointer key, gpointer value, gpointer data)
/* if the current probability is greater than the stored, store the current */
if (GPOINTER_TO_INT(value) > account_i->probability)
{
- /* Save the new highest probability and the assoaciated account name */
+ /* Save the new highest probability and the assoaciated account guid */
account_i->probability = GPOINTER_TO_INT(value);
- account_i->account_name = key;
+ account_i->account_guid = key;
}
}
@@ -5295,7 +5295,7 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
{
char* path = g_strdup_printf (IMAP_FRAME_BAYES "/%s",
(char*)current_token->data);
- /* zero out the token_accounts_info structure */
+ /* zero out the token_accounts_info structure */
memset(&tokenInfo, 0, sizeof(struct token_accounts_info));
PINFO("token: '%s'", (char*)current_token->data);
@@ -5317,14 +5317,14 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
/* get the account name and corresponding token count */
account_c = (struct account_token_count*)current_account_token->data;
- PINFO("account_c->account_name('%s'), "
+ PINFO("account_c->account_guid('%s'), "
"account_c->token_count('%" G_GINT64_FORMAT
"')/total_count('%" G_GINT64_FORMAT "')",
- account_c->account_name, account_c->token_count,
+ account_c->account_guid, account_c->token_count,
tokenInfo.total_count);
account_p = g_hash_table_lookup(running_probabilities,
- account_c->account_name);
+ account_c->account_guid);
/* if the account exists in the list then continue
* the running probablities
@@ -5358,10 +5358,10 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
PINFO("product == %f, product_difference == %f",
account_p->product, account_p->product_difference);
- /* add the account name and (struct account_probability*)
+ /* add the account guid and (struct account_probability*)
* to the hash table */
g_hash_table_insert(running_probabilities,
- account_c->account_name, account_p);
+ account_c->account_guid, account_p);
}
} /* for all accounts in tokenInfo */
@@ -5394,7 +5394,7 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
g_hash_table_destroy(final_probabilities);
PINFO("highest P('%s') = '%d'",
- account_i.account_name ? account_i.account_name : "(null)",
+ account_i.account_guid ? account_i.account_guid : "(null)",
account_i.probability);
/* has this probability met our threshold? */
@@ -5407,7 +5407,7 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
guid = g_new (GncGUID, 1);
- if (string_to_guid (account_i.account_name, guid))
+ if (string_to_guid (account_i.account_guid, guid))
account = xaccAccountLookup (guid, imap->book);
g_free (guid);
@@ -5415,7 +5415,7 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
if (account != NULL)
LEAVE("Return account is '%s'", xaccAccountGetName (account));
else
- LEAVE("Return NULL, account for Guid '%s' can not be found", account_i.account_name);
+ LEAVE("Return NULL, account for Guid '%s' can not be found", account_i.account_guid);
return account;
}
@@ -5448,7 +5448,7 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
account_fullname = gnc_account_get_full_name(acc);
xaccAccountBeginEdit (imap->acc);
- PINFO("account name: '%s'\n", account_fullname);
+ PINFO("account name: '%s'", account_fullname);
guid_string = guid_to_string (xaccAccountGetGUID (acc));
@@ -5467,7 +5467,7 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
/* start off with no tokens for this account */
token_count = 0;
- PINFO("adding token '%s'\n", (char*)current_token->data);
+ PINFO("adding token '%s'", (char*)current_token->data);
kvp_path = g_strdup_printf (IMAP_FRAME_BAYES "/%s/%s",
(char*)current_token->data,
@@ -5481,7 +5481,7 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
if (G_VALUE_HOLDS_INT64 (&value))
{
int64_t count = g_value_get_int64 (&value);
- PINFO("found existing value of '%" G_GINT64_FORMAT "'\n", count);
+ PINFO("found existing value of '%" G_GINT64_FORMAT "'", count);
token_count += count;
}
commit fd2335452832230ba436c6a62d99e21418579ca0
Author: Robert Fewell <14uBobIT at gmail.com>
Date: Mon Feb 29 09:41:45 2016 +0000
Change Bayes functions to use guid account string
instead of full account name in kvp path
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 5871cd4..1a05bb2 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -5400,14 +5400,27 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
/* has this probability met our threshold? */
if (account_i.probability >= threshold)
{
- PINFO("found match");
- LEAVE(" ");
- return gnc_account_lookup_by_full_name(gnc_book_get_root_account(imap->book),
- account_i.account_name);
- }
+ GncGUID *guid;
+ Account *account = NULL;
- PINFO("no match");
- LEAVE(" ");
+ PINFO("Probability has met threshold");
+
+ guid = g_new (GncGUID, 1);
+
+ if (string_to_guid (account_i.account_name, guid))
+ account = xaccAccountLookup (guid, imap->book);
+
+ g_free (guid);
+
+ if (account != NULL)
+ LEAVE("Return account is '%s'", xaccAccountGetName (account));
+ else
+ LEAVE("Return NULL, account for Guid '%s' can not be found", account_i.account_name);
+
+ return account;
+ }
+ PINFO("Probability has not met threshold");
+ LEAVE("Return NULL");
return NULL; /* we didn't meet our threshold, return NULL for an account */
}
@@ -5422,6 +5435,7 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
GList *current_token;
gint64 token_count;
char *account_fullname, *kvp_path;
+ char *guid_string;
ENTER(" ");
if (!imap)
@@ -5436,6 +5450,8 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
PINFO("account name: '%s'\n", account_fullname);
+ guid_string = guid_to_string (xaccAccountGetGUID (acc));
+
/* process each token in the list */
for (current_token = g_list_first(tokens); current_token;
current_token = current_token->next)
@@ -5455,7 +5471,7 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
kvp_path = g_strdup_printf (IMAP_FRAME_BAYES "/%s/%s",
(char*)current_token->data,
- account_fullname);
+ guid_string);
qof_instance_get_kvp (QOF_INSTANCE (imap->acc), kvp_path, &value);
/* if the token/account is already in the tree, read the current
@@ -5477,10 +5493,11 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
g_free (kvp_path);
}
- /* free up the account fullname string */
+ /* free up the account fullname and guid string */
qof_instance_set_dirty (QOF_INSTANCE (imap->acc));
xaccAccountCommitEdit (imap->acc);
- g_free(account_fullname);
+ g_free (account_fullname);
+ g_free (guid_string);
LEAVE(" ");
}
Summary of changes:
src/engine/Account.c | 368 ++++++++++++++++++++++++++++++++++-------
src/engine/Account.h | 9 +-
src/gnome-utils/gnc-file.c | 5 +
src/gnome/dialog-imap-editor.c | 8 +-
4 files changed, 327 insertions(+), 63 deletions(-)
More information about the gnucash-changes
mailing list