TransLog.c

00001 /********************************************************************\
00002  * TransLog.c -- the transaction logger                             *
00003  * Copyright (C) 1998 Linas Vepstas                                 *
00004  *                                                                  *
00005  * This program is free software; you can redistribute it and/or    *
00006  * modify it under the terms of the GNU General Public License as   *
00007  * published by the Free Software Foundation; either version 2 of   *
00008  * the License, or (at your option) any later version.              *
00009  *                                                                  *
00010  * This program is distributed in the hope that it will be useful,  *
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00013  * GNU General Public License for more details.                     *
00014  *                                                                  *
00015  * You should have received a copy of the GNU General Public License*
00016  * along with this program; if not, contact:                        *
00017  *                                                                  *
00018  * Free Software Foundation           Voice:  +1-617-542-5942       *
00019  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
00020  * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
00021  *                                                                  *
00022 \********************************************************************/
00023 
00024 #define _GNU_SOURCE
00025 #include "config.h"
00026 
00027 #include <errno.h>
00028 #include <glib.h>
00029 #include <stdio.h>
00030 #include <string.h>
00031 
00032 #include "Account.h"
00033 #include "AccountP.h"
00034 #include "Transaction.h"
00035 #include "TransactionP.h"
00036 #include "TransLog.h"
00037 #include "gnc-date.h"
00038 #include "gnc-engine-util.h"
00039 
00040 /* 
00041  * Some design philosphy that I think would be good to keep in mind:
00042  * (0) Simplicity and foolproofness are the over-riding design points.
00043  *     This is supposed to be a fail-safe safety net.   We don't want
00044  *     our safety net to fail because of some whiz-bang shenanigans.
00045  *
00046  * (1) Try to keep the code simple.  Want to make it simple and obvious
00047  *     that we are recording everything that we need to record.
00048  *
00049  * (2) Keep the printed format human readable, for the same reasons.
00050  * (2.a) Keep the format, simple, flat, more or less unstructured,
00051  *       record oriented.  This will help parsing by perl scripts.
00052  *       No, using a perl script to analyze a file that's supposed to
00053  *       be human readable is not a contradication in terms -- that's 
00054  *       exactly the point.
00055  * (2.b) Use tabs as a human freindly field separator; its also a 
00056  *       character that does not (should not) appear naturally anywhere 
00057  *       in the data, as it serves no formatting purpose in the current 
00058  *       GUI design.  (hack alert -- this is not currently tested for 
00059  *       or enforced, so this is a very unsafe assumption. Maybe 
00060  *       urlencoding should be used.)
00061  * (2.c) Don't print redundant information in a single record. This 
00062  *       would just confuse any potential user of this file.
00063  * (2.d) Saving space, being compact is not a priority, I don't think.
00064  *       
00065  * (3) There are no compatibility requirements from release to release.
00066  *     Sounds OK to me to change the format of the output when needed.
00067  *
00068  * (-) print transaction start and end delimiters
00069  * (-) print a unique transaction id as a handy label for anyone 
00070  *     who actually examines these logs.  
00071  *     The C address pointer to the transaction struct should be fine, 
00072  *     as it is simple and unique until the transaction is deleted ...
00073  *     and we log deletions, so that's OK.  Just note that the id
00074  *     for a deleted transaction might be recycled.
00075  * (-) print the current timestamp, so that if it is known that a bug
00076  *     occurred at a certain time, it can be located.
00077  * (-) hack alert -- something better than just the account name 
00078  *     is needed for identifying the account.
00079  */
00080 /* ------------------------------------------------------------------ */
00081 
00082 
00083 static int gen_logs = 1;
00084 static FILE * trans_log = NULL;
00085 static char * log_base_name = NULL;
00086 
00087 /********************************************************************\
00088 \********************************************************************/
00089 
00090 void xaccLogDisable (void) { gen_logs = 0; }
00091 void xaccLogEnable  (void) { gen_logs = 1; }
00092 
00093 /********************************************************************\
00094 \********************************************************************/
00095 
00096 void 
00097 xaccLogSetBaseName (const char *basepath)
00098 {
00099    if (!basepath) return;
00100 
00101    g_free (log_base_name);
00102    log_base_name = g_strdup (basepath);
00103 
00104    if (trans_log) {
00105       xaccCloseLog();
00106       xaccOpenLog();
00107    }
00108 }
00109 
00110 /********************************************************************\
00111 \********************************************************************/
00112 
00113 void
00114 xaccOpenLog (void)
00115 {
00116    char * filename;
00117    char * timestamp;
00118 
00119    if (!gen_logs) return;
00120    if (trans_log) return;
00121 
00122    if (!log_base_name) log_base_name = g_strdup ("translog");
00123 
00124    /* tag each filename with a timestamp */
00125    timestamp = xaccDateUtilGetStampNow ();
00126 
00127    filename = g_strconcat (log_base_name, ".", timestamp, ".log", NULL);
00128 
00129    trans_log = fopen (filename, "a");
00130    if (!trans_log) {
00131       int norr = errno;
00132       printf ("Error: xaccOpenLog(): cannot open journal \n"
00133               "\t %d %s\n", norr, strerror (norr));
00134 
00135       g_free (filename);
00136       g_free (timestamp);
00137       return;
00138    }
00139 
00140    g_free (filename);
00141    g_free (timestamp);
00142 
00143    /* use tab-separated fields */
00144    fprintf (trans_log, "mod        trans_guid        split_guid        time_now        " \
00145                        "date_entered        date_posted        " \
00146                        "acc_guid        acc_name        num        description        " \
00147                        "notes        memo        action        reconciled        " \
00148                        "amount        value        date_reconciled\n");
00149    fprintf (trans_log, "-----------------\n");
00150 }
00151 
00152 /********************************************************************\
00153 \********************************************************************/
00154 
00155 void
00156 xaccCloseLog (void)
00157 {
00158    if (!trans_log) return;
00159    fflush (trans_log);
00160    fclose (trans_log);
00161    trans_log = NULL;
00162 }
00163 
00164 /********************************************************************\
00165 \********************************************************************/
00166 
00167 void
00168 xaccTransWriteLog (Transaction *trans, char flag)
00169 {
00170    GList *node;
00171    char trans_guid_str[GUID_ENCODING_LENGTH+1];
00172    char split_guid_str[GUID_ENCODING_LENGTH+1];
00173    const char *trans_notes; 
00174    char dnow[100], dent[100], dpost[100], drecn[100]; 
00175    Timespec ts;
00176 
00177    if (!gen_logs) return;
00178    if (!trans_log) return;
00179 
00180    timespecFromTime_t(&ts,time(NULL));
00181    gnc_timespec_to_iso8601_buff (ts, dnow);
00182 
00183    timespecFromTime_t(&ts,trans->date_entered.tv_sec);
00184    gnc_timespec_to_iso8601_buff (ts, dent);
00185 
00186    timespecFromTime_t(&ts,trans->date_posted.tv_sec);
00187    gnc_timespec_to_iso8601_buff (ts, dpost);
00188 
00189    guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
00190    trans_notes = xaccTransGetNotes(trans);
00191    fprintf (trans_log, "===== START\n");
00192 
00193    for (node = trans->splits; node; node = node->next) 
00194    {
00195       Split *split = node->data;
00196       const char * accname = "";
00197       char acc_guid_str[GUID_ENCODING_LENGTH+1];
00198       gnc_numeric amt,val;
00199 
00200       if (xaccSplitGetAccount(split))
00201       {
00202         accname = xaccAccountGetName (xaccSplitGetAccount(split));
00203         guid_to_string_buff(xaccAccountGetGUID(xaccSplitGetAccount(split)),
00204                             acc_guid_str);
00205       } 
00206       else 
00207       {
00208         acc_guid_str[0] = '\0';
00209       }
00210       
00211       timespecFromTime_t(&ts,split->date_reconciled.tv_sec);
00212       gnc_timespec_to_iso8601_buff (ts, drecn);
00213 
00214       guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
00215       amt = xaccSplitGetAmount (split);
00216       val = xaccSplitGetValue (split);
00217 
00218       /* use tab-separated fields */
00219       fprintf (trans_log,
00220                "%c\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
00221                "%s\t%s\t%s\t%s\t%c\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%s\n",
00222                flag,
00223                trans_guid_str, split_guid_str,  /* trans+split make up unique id */
00224                dnow ? dnow : "",
00225                dent ? dent : "", 
00226                dpost ? dpost : "", 
00227                acc_guid_str,
00228                accname ? accname : "",
00229                trans->num ? trans->num : "", 
00230                trans->description ? trans->description : "",
00231                trans_notes ? trans_notes : "",
00232                split->memo ? split->memo : "",
00233                split->action ? split->action : "",
00234                split->reconciled,
00235                gnc_numeric_num(amt), 
00236                gnc_numeric_denom(amt),
00237                gnc_numeric_num(val), 
00238                gnc_numeric_denom(val),
00239                drecn ? drecn : "");
00240    }
00241 
00242    fprintf (trans_log, "===== END\n");
00243 
00244    /* get data out to the disk */
00245    fflush (trans_log);
00246 }
00247 
00248 /********************************************************************\
00249 \********************************************************************/
00250 
00251 #if 0
00252 /* open_memstream seems to give various distros fits
00253  * this has resulted in warfare on the mailing list.
00254  * I think the truce called required changing this to asprintf
00255  * this code is not currently used ...  so its ifdef out
00256  */
00257 
00258 char *
00259 xaccSplitAsString(Split *split, const char prefix[]) 
00260 {
00261   char *result = NULL;
00262   size_t result_size;
00263   FILE *stream = open_memstream(&result, &result_size); 
00264   const char *split_memo = xaccSplitGetMemo(split);
00265   const double split_value = gnc_numeric_to_double(xaccSplitGetValue(split));
00266   Account *split_dest = xaccSplitGetAccount(split);
00267   const char *dest_name =
00268     split_dest ? xaccAccountGetName(split_dest) : NULL;
00269 
00270   g_return_val_if_fail (stream, NULL);
00271 
00272   fputc('\n', stream);
00273   fputs(prefix, stream);
00274   fprintf(stream, "  %10.2f | %15s | %s",
00275           split_value,
00276           dest_name ? dest_name : "<no-account-name>",
00277           split_memo ? split_memo : "<no-split-memo>");
00278   fclose(stream); 
00279   return(result);
00280 }
00281 
00282 static char *
00283 xaccTransGetDateStr (Transaction *trans)
00284 {
00285    char buf [MAX_DATE_LENGTH];
00286    struct tm *date;
00287    time_t secs;
00288 
00289    secs = xaccTransGetDate (trans);
00290 
00291    date = localtime (&secs);
00292 
00293    qof_print_date_buff(buf, date->tm_mday, date->tm_mon+1, date->tm_year +1900);
00294 
00295    return g_strdup (buf);
00296 }
00297 
00298 char *
00299 xaccTransAsString(Transaction *txn, const char prefix[]) 
00300 {
00301   char *result = NULL;
00302   size_t result_size;
00303   FILE *stream = open_memstream(&result, &result_size); 
00304   time_t date = xaccTransGetDate(txn);
00305   const char *num = xaccTransGetNum(txn);
00306   const char *desc = xaccTransGetDescription(txn);
00307   const char *memo = xaccSplitGetMemo(xaccTransGetSplit(txn, 0));
00308   const double total = gnc_numeric_to_double(xaccSplitGetValue(xaccTransGetSplit(txn, 0)));
00309   
00310   g_return_val_if_fail (stream, NULL);
00311 
00312   fputs(prefix, stream);
00313   if(date) {
00314     char *datestr = xaccTransGetDateStr(txn);
00315     fprintf(stream, "%s", datestr);
00316     free(datestr);
00317   } else {
00318     fprintf(stream, "<no-date>");
00319   }
00320   fputc(' ', stream); 
00321   if(num) {
00322     fputs(num, stream);
00323   } else {
00324     fprintf(stream, "<no-num>");
00325   }
00326 
00327   fputc('\n', stream);
00328   fputs(prefix, stream);
00329   if(desc) {
00330     fputs("  ", stream);
00331     fputs(desc, stream);
00332   } else {
00333     fprintf(stream, "<no-description>");
00334   }
00335   
00336   fputc('\n', stream);
00337   fputs(prefix, stream);
00338   if(memo) {
00339     fputs("  ", stream);
00340     fputs(memo, stream);
00341   } else {
00342     fprintf(stream, "<no-transaction-memo>");
00343   }
00344   
00345   {
00346     int split_count = xaccTransCountSplits(txn);
00347     int i;
00348     for(i = 1; i < split_count; i++) {
00349       Split *split = xaccTransGetSplit(txn, i);
00350       char *split_text = xaccSplitAsString(split, prefix);
00351       fputs(split_text, stream);
00352       free(split_text);
00353     }
00354   }
00355   fputc('\n', stream);
00356 
00357   fputs(prefix, stream);
00358   fprintf(stream, "  %10.2f -- Transaction total\n", total);
00359   fclose(stream); 
00360 
00361   return(result);
00362 }
00363 
00364 #endif
00365 
00366 /************************ END OF ************************************\
00367 \************************* FILE *************************************/

Generated on Sun Sep 4 18:08:58 2005 for GnuCash by  doxygen 1.4.3-20050530