kpilot Library API Documentation

expense.cc

00001 /*expense.cc                    KPilot
00002 **
00003 ** Copyright (C) 2000-2001 by Adriaan de Groot, Christopher Molnar
00004 **
00005 ** This file is part of the Expense conduit, a conduit for KPilot that
00006 ** synchronises the Pilot's expense application with .. something?
00007 ** Actually it just writes a CSV file.
00008 */
00009 
00010 /*
00011 ** This program is free software; you can redistribute it and/or modify
00012 ** it under the terms of the GNU General Public License as published by
00013 ** the Free Software Foundation; either version 2 of the License, or
00014 ** (at your option) any later version.
00015 **
00016 ** This program is distributed in the hope that it will be useful,
00017 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00019 ** GNU General Public License for more details.
00020 **
00021 ** You should have received a copy of the GNU General Public License
00022 ** along with this program in a file called COPYING; if not, write to
00023 ** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00024 ** MA 02111-1307, USA.
00025 */
00026 
00027 /*
00028 ** Bug reports and questions can be sent to kde-pim@kde.org
00029 */
00030 
00031 
00032 #include "options.h"
00033 
00034 // Only include what we really need:
00035 // First UNIX system stuff, then std C++,
00036 // then Qt, then KDE, then local includes.
00037 //
00038 //
00039 #include <time.h>
00040 #include <stdlib.h>
00041 #include <stdio.h>
00042 #include <string.h>
00043 
00044 
00045 #include <qdir.h>
00046 #include <qmap.h>
00047 #include <qcstring.h>
00048 #include <qobject.h>
00049 #include <qdatetime.h>
00050 #include <qtextstream.h>
00051 #include <qtextcodec.h>
00052 
00053 #include <kconfig.h>
00054 #include <kdebug.h>
00055 #include <kprocess.h>
00056 
00057 #include <pi-expense.h>
00058 
00059 
00060 
00061 
00062 
00063 #include "pilotSerialDatabase.h"
00064 #include "pilotRecord.h"
00065 #include "pilotAppCategory.h"
00066 
00067 #include "setupDialog.h"
00068 #include "expense.moc"
00069 #include <qtimer.h>
00070 #define DATESIZE 10
00071 /*  This was copied out of the pilot-link package.
00072 *  I just like it here for quick reference.
00073 struct Expense {
00074 struct tm date;
00075 enum ExpenseType type;
00076 enum ExpensePayment payment;
00077 int currency;
00078 char * amount;
00079 char * vendor;
00080 char * city;
00081 char * attendees;
00082 char * note;
00083 };
00084 */
00085 
00086 const char *
00087 get_entry_type(enum ExpenseType type)
00088  {
00089    switch(type) {
00090         case etAirfare:
00091                 return "Airfare";
00092         case etBreakfast:
00093                 return "Breakfast";
00094         case etBus:
00095                 return "Bus";
00096         case etBusinessMeals:
00097                 return "BusinessMeals";
00098         case etCarRental:
00099                 return "CarRental";
00100         case etDinner:
00101                 return "Dinner";
00102         case etEntertainment:
00103                 return "Entertainment";
00104         case etFax:
00105                 return "Fax";
00106         case etGas:
00107                 return "Gas";
00108     case etGifts:
00109               return "Gifts";
00110     case etHotel:
00111       return "Hotel";
00112     case etIncidentals:
00113       return "Incidentals";
00114     case etLaundry:
00115       return "Laundry";
00116     case etLimo:
00117       return "Limo";
00118     case etLodging:
00119       return "Lodging";
00120     case etLunch:
00121       return "Lunch";
00122     case etMileage:
00123       return "Mileage";
00124     case etOther:
00125       return "Other";
00126     case etParking:
00127       return "Parking";
00128     case etPostage:
00129       return "Postage";
00130     case etSnack:
00131       return "Snack";
00132     case etSubway:
00133       return "Subway";
00134     case etSupplies:
00135       return "Supplies";
00136     case etTaxi:
00137       return "Taxi";
00138     case etTelephone:
00139       return "Telephone";
00140     case etTips:
00141       return "Tips";
00142     case etTolls:
00143       return "Tolls";
00144     case etTrain:
00145       return "Train";
00146     default:
00147       return NULL;
00148    }
00149 }
00150 
00151 const char *
00152 get_pay_type(enum ExpensePayment type)
00153 {
00154    switch (type) {
00155     case epAmEx:
00156       return "AmEx";
00157     case epCash:
00158       return "Cash";
00159     case epCheck:
00160       return "Check";
00161     case epCreditCard:
00162       return "CreditCard";
00163     case epMasterCard:
00164       return "MasterCard";
00165     case epPrepaid:
00166       return "Prepaid";
00167     case epVISA:
00168       return "VISA";
00169     case epUnfiled:
00170       return "Unfiled";
00171     default:
00172       return NULL;
00173    }
00174 }
00175 
00176 
00177 // Something to allow us to check what revision
00178 // the modules are that make up a binary distribution.
00179 //
00180 //
00181 static const char *expense_id =
00182         "$Id: expense.cc,v 1.24.4.9 2003/03/12 23:31:10 adridg Exp $";
00183 
00184 
00185 
00186 
00187 ExpenseConduit::ExpenseConduit(KPilotDeviceLink *d,
00188         const char *name,
00189         const QStringList &l) :
00190         ConduitAction(d,name,l),
00191         fDatabase(0L),
00192         fCSVFile(0L),
00193         fCSVStream(0L)
00194 {
00195         FUNCTIONSETUP;
00196 #ifdef DEBUG
00197         DEBUGCONDUIT<<expense_id<<endl;
00198 #endif
00199 }
00200 
00201 ExpenseConduit::~ExpenseConduit()
00202 {
00203         FUNCTIONSETUP;
00204         cleanup();
00205 }
00206 
00207 /* virtual */ bool ExpenseConduit::exec()
00208 {
00209         FUNCTIONSETUP;
00210         DEBUGCONDUIT<<expense_id<<endl;
00211 
00212         if (!fConfig)
00213         {
00214                 kdWarning() << k_funcinfo
00215                         << ": No configuration set for expense conduit."
00216                         << endl;
00217                 cleanup();
00218                 return false;
00219         }
00220 
00221         fDatabase=new PilotSerialDatabase(pilotSocket(),CSL1("ExpenseDB"),
00222                 this,"ExpenseDB");
00223 
00224         fConfig->setGroup("Expense-conduit");
00225 
00226         fDBType = fConfig->readNumEntry("DBTypePolicy",
00227                         ExpenseWidgetSetup::PolicyNone);
00228 
00229 #ifdef DEBUG
00230         DEBUGCONDUIT << fname
00231                 << ": Syncing with policy "
00232                 << fDBType
00233                 << endl;
00234 #endif
00235 
00236         fDBnm=fConfig->readEntry("DBname");
00237         fDBsrv=fConfig->readEntry("DBServer");
00238         fDBtable=fConfig->readEntry("DBtable");
00239         fDBlogin=fConfig->readEntry("DBlogin");
00240         fDBpasswd=fConfig->readEntry("DBpasswd");
00241 
00242         fRecordCount = 0;
00243 
00244         if (isTest())
00245         {
00246                 doTest();
00247                 cleanup();
00248                 emit syncDone(this);
00249                 return true;
00250         }
00251         else
00252         {
00253                 QString CSVName=fConfig->readEntry("CSVFileName");
00254                 if (!CSVName.isEmpty())
00255                 {
00256                         fCSVFile = new QFile(CSVName);
00257 
00258                         // Change the flags value in the switch() below.
00259                         //
00260                         //
00261                         int flags = 0;
00262                         int logPolicy = fConfig->readNumEntry("CSVRotatePolicy",
00263                                 ExpenseWidgetSetup::PolicyAppend);
00264 
00265                         switch(logPolicy)
00266                         {
00267                         case ExpenseWidgetSetup::PolicyAppend :
00268                                 flags = IO_ReadWrite | IO_Append;
00269                                 break;
00270                         case ExpenseWidgetSetup::PolicyOverwrite :
00271                                 flags = IO_WriteOnly | IO_Truncate;
00272                                 break;
00273                         default :
00274                                 flags = IO_ReadWrite | IO_Append;
00275                         }
00276 
00277                         if (fCSVFile && fCSVFile->open(flags))
00278                         {
00279                                 fCSVStream = new QTextStream(fCSVFile);
00280                         }
00281                 }
00282 
00283                 // Start the mechanism for reading one record
00284                 // at a time while retaining responsiveness.
00285                 //
00286                 //
00287                 QTimer::singleShot(0,this,SLOT(slotNextRecord()));
00288         }
00289         return true;
00290 }
00291 
00292 void ExpenseConduit::doTest()
00293 {
00294 #ifdef DEBUG
00295         DEBUGCONDUIT << k_funcinfo
00296                 << ": Got settings "
00297                 << fDBType << " "
00298                 << fDBnm << " "
00299                 << fDBsrv << " "
00300                 << fDBtable << " "
00301                 << fDBlogin
00302                 << endl;
00303 #endif
00304 }
00305 
00306 void ExpenseConduit::csvOutput(QTextStream *out,Expense *e)
00307 {
00308         FUNCTIONSETUP;
00309 
00310         //format date for csv file
00311         int tmpyr=e->date.tm_year+1900;
00312         int tmpday=e->date.tm_mday;
00313         int tmpmon=e->date.tm_mon+1;
00314 
00315         (*out) << tmpyr << "-" << tmpmon << "-" << tmpday << "," ;
00316 
00317         //write rest of record
00318         (*out) << e->amount << ","
00319                 << get_pay_type(e->payment) << ","
00320                 << e->vendor << ","
00321                 << get_entry_type(e->type) << ","
00322                 << e->city << ","
00323                 ;
00324 
00325         // remove line breaks from list of attendees -
00326         // can't have in csv files
00327         //
00328         //
00329         QString tmpatt=PilotAppCategory::codec()->toUnicode(e->attendees);
00330         QString tmpatt2=tmpatt.simplifyWhiteSpace();
00331 
00332         (*out) << tmpatt2 << "," ;
00333 
00334         // remove extra formatting from notes -
00335         // can't have in csv files
00336         QString tmpnotes=PilotAppCategory::codec()->toUnicode(e->note);
00337         QString tmpnotes2=tmpnotes.simplifyWhiteSpace();
00338 
00339         (*out) << tmpnotes2 << endl;
00340 }
00341 
00342 void ExpenseConduit::slotNextRecord()
00343 {
00344         FUNCTIONSETUP;
00345 
00346         Expense e;
00347 
00348         PilotRecord *rec=fDatabase->readNextModifiedRec();
00349         if (!rec)
00350         {
00351 #ifdef DEBUG
00352                 DEBUGCONDUIT << fname
00353                         << ": No more records left."
00354                         << endl;
00355 #endif
00356 
00357                 QString msg(i18n("Synced one record.",
00358                         "Synced %n records.",fRecordCount));
00359                 addSyncLogEntry(msg);
00360 
00361                 fDatabase->resetSyncFlags();
00362 
00363                 cleanup();
00364                 emit syncDone(this);
00365                 return;
00366         }
00367         else
00368         {
00369                 fRecordCount++;
00370 #ifdef DEBUG
00371                 DEBUGCONDUIT << fname
00372                         << ": Got record "
00373                         << fRecordCount
00374                         << " @"
00375                         << (int) rec
00376                         << endl;
00377 #endif
00378         }
00379 
00380         (void) unpack_Expense(&e,
00381                 (unsigned char *)rec->getData(),rec->getLen());
00382 
00383         delete rec;
00384         rec = 0L;
00385 
00386         if (fCSVStream)
00387         {
00388                 csvOutput(fCSVStream,&e);
00389         }
00390 
00391         switch(fDBType)
00392         {
00393         case ExpenseWidgetSetup::PolicyPostgresql :
00394                 postgresOutput(&e);
00395                 break;
00396         }
00397 
00398         QTimer::singleShot(0,this,SLOT(slotNextRecord()));
00399 }
00400 
00401 
00402 void ExpenseConduit::dumpPostgresTable()
00403 {
00404         FUNCTIONSETUP;
00405 
00406 #ifdef DEBUG
00407         // next three lines just for debug purposes -
00408         // Remove for final creates a dump of table.
00409         //
00410         //
00411         QString query = CSL1("select * from \"%1\";").arg(fDBtable);
00412         
00413         QString cmd = CSL1("echo ");
00414         cmd += KShellProcess::quote(fDBpasswd);
00415         cmd += CSL1("|psql -h ");
00416         cmd += KShellProcess::quote(fDBsrv);
00417         cmd += CSL1(" -U ");
00418         cmd += KShellProcess::quote(fDBlogin);
00419         cmd += CSL1(" -c ");
00420         cmd += KShellProcess::quote(query);
00421         cmd += CSL1(" ");
00422         cmd += KShellProcess::quote(fDBnm);
00423         cmd += CSL1(" > ~/testpg.txt");
00424 
00425         KShellProcess shproc;
00426         shproc.clearArguments();
00427         shproc << cmd;
00428         shproc.start(KShellProcess::Block, KShellProcess::NoCommunication);
00429 #endif
00430 }
00431 
00432 void ExpenseConduit::postgresOutput(Expense *e)
00433 {
00434         FUNCTIONSETUP;
00435 
00436         // int recordcount=0;
00437         // int index=0;
00438         // int syscall=0;
00439 
00440 
00441         int tmpyr=e->date.tm_year+1900;
00442         char dtstng[DATESIZE];
00443         int tmpday=e->date.tm_mday;
00444         int tmpmon=e->date.tm_mon+1;
00445         sprintf(dtstng,"%d-%d-%d",tmpyr,tmpmon,tmpday);
00446         QString tmpnotes=PilotAppCategory::codec()->toUnicode(e->note);
00447         QString tmpnotes2=tmpnotes.simplifyWhiteSpace();
00448         const char* nmsg=tmpnotes2.local8Bit();
00449 
00450         QString tmpatt=PilotAppCategory::codec()->toUnicode(e->attendees);
00451         QString tmpatt2=tmpatt.simplifyWhiteSpace();
00452         const char* amesg=tmpatt2.local8Bit();
00453         const char* etmsg=get_entry_type(e->type);
00454         const char* epmsg=get_pay_type(e->payment);
00455 
00456         QString query;
00457         query.sprintf(
00458                 "INSERT INTO \"%s\" (\"fldTdate\", \"fldAmount\", \"fldPType\", "
00459                 "\"fldVName\", \"fldEType\", \"fldLocation\", \"fldAttendees\", "
00460                 "\"fldNotes\") VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');",
00461                 fDBtable.latin1(),
00462                 dtstng,
00463                 e->amount,epmsg,e->vendor,etmsg,e->city,amesg,nmsg);
00464 
00465         QString cmd = CSL1("echo ");
00466         cmd += KShellProcess::quote(fDBpasswd);
00467         cmd += CSL1("|psql -h ");
00468         cmd += KShellProcess::quote(fDBsrv);
00469         cmd += CSL1(" -U ");
00470         cmd += KShellProcess::quote(fDBlogin);
00471         cmd += CSL1(" -c ");
00472         cmd += KShellProcess::quote(query);
00473         cmd += CSL1(" ");
00474         cmd += KShellProcess::quote(fDBnm);
00475 
00476         KShellProcess shproc;
00477         shproc.clearArguments();
00478         shproc << cmd;
00479         shproc.start(KShellProcess::Block, KShellProcess::NoCommunication);
00480 }
00481 
00482 void ExpenseConduit::cleanup()
00483 {
00484         FUNCTIONSETUP;
00485 
00486         KPILOT_DELETE(fCSVStream);
00487         KPILOT_DELETE(fCSVFile);
00488         KPILOT_DELETE(fDatabase);
00489 }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 15 11:40:43 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2001