00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 #include <fcntl.h>
00026 #include <unistd.h>
00027
00028 #include <cassert>
00029
00030 #include <qfile.h>
00031 #include <qsize.h>
00032 #include <qdict.h>
00033 #include <qdatetime.h>
00034 #include <qstring.h>
00035 #include <qstringlist.h>
00036
00037 #include "incidence.h"
00038 #include "kapplication.h"
00039 #include <kdebug.h>
00040 #include <kemailsettings.h>
00041 #include <klocale.h>
00042 #include <kmessagebox.h>
00043 #include <kprogress.h>
00044 #include <ktempfile.h>
00045 #include <resourcecalendar.h>
00046 #include <resourcelocal.h>
00047 #include <resourceremote.h>
00048 #include <kpimprefs.h>
00049 #include <taskview.h>
00050 #include <timekard.h>
00051 #include <karmutility.h>
00052 #include <kio/netaccess.h>
00053 #include <kurl.h>
00054 #include <vector>
00055
00056
00057
00058
00059
00060
00061 #include "karmstorage.h"
00062 #include "preferences.h"
00063 #include "task.h"
00064 #include "reportcriteria.h"
00065
00066 using namespace std;
00067
00068 KarmStorage *KarmStorage::_instance = 0;
00069 static long linenr;
00070
00071
00072 KarmStorage *KarmStorage::instance()
00073 {
00074 if (_instance == 0) _instance = new KarmStorage();
00075 return _instance;
00076 }
00077
00078 KarmStorage::KarmStorage()
00079 {
00080 _calendar = 0;
00081 }
00082
00083 QString KarmStorage::load (TaskView* view, const Preferences* preferences, QString fileName )
00084
00085
00086 {
00087
00088
00089
00090
00091
00092
00093
00094
00095 QString err;
00096 KEMailSettings settings;
00097 if ( fileName.isEmpty() ) fileName = preferences->iCalFile();
00098
00099
00100 if ( fileName == _icalfile ) return err;
00101
00102
00103
00104
00105
00106 if ( ! remoteResource( _icalfile ) )
00107 {
00108 int handle;
00109 handle = open (
00110 QFile::encodeName( fileName ),
00111 O_CREAT|O_EXCL|O_WRONLY,
00112 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH
00113 );
00114 if (handle != -1) close(handle);
00115 }
00116
00117 if ( _calendar)
00118 closeStorage(view);
00119
00120
00121 _icalfile = fileName;
00122
00123 KCal::ResourceCached *resource;
00124 if ( remoteResource( _icalfile ) )
00125 {
00126 KURL url( _icalfile );
00127 resource = new KCal::ResourceRemote( url, url );
00128 }
00129 else
00130 {
00131 resource = new KCal::ResourceLocal( _icalfile );
00132 }
00133 _calendar = resource;
00134
00135 QObject::connect (_calendar, SIGNAL(resourceChanged(ResourceCalendar *)),
00136 view, SLOT(iCalFileModified(ResourceCalendar *)));
00137 _calendar->setTimeZoneId( KPimPrefs::timezone() );
00138 _calendar->setResourceName( QString::fromLatin1("KArm") );
00139 _calendar->open();
00140 _calendar->load();
00141
00142
00143 KCal::Person owner = resource->getOwner();
00144 if ( owner.isEmpty() )
00145 {
00146 resource->setOwner( KCal::Person(
00147 settings.getSetting( KEMailSettings::RealName ),
00148 settings.getSetting( KEMailSettings::EmailAddress ) ) );
00149 }
00150
00151
00152 if (!err)
00153 {
00154 KCal::Todo::List todoList;
00155 KCal::Todo::List::ConstIterator todo;
00156 QDict< Task > map;
00157
00158
00159
00160 todoList = _calendar->rawTodos();
00161 kdDebug(5970) << "KarmStorage::load "
00162 << "rawTodo count (includes completed todos) ="
00163 << todoList.count() << endl;
00164 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
00165 {
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183 Task* task = new Task(*todo, view);
00184 map.insert( (*todo)->uid(), task );
00185 view->setRootIsDecorated(true);
00186 task->setPixmapProgress();
00187 }
00188
00189
00190 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
00191 {
00192 Task* task = map.find( (*todo)->uid() );
00193
00194
00195 if ( (*todo)->relatedTo() )
00196 {
00197 Task* newParent = map.find( (*todo)->relatedToUid() );
00198
00199
00200 if ( !newParent )
00201 err = i18n("Error loading \"%1\": could not find parent (uid=%2)")
00202 .arg(task->name())
00203 .arg((*todo)->relatedToUid());
00204
00205 if (!err) task->move( newParent);
00206 }
00207 }
00208
00209 kdDebug(5970) << "KarmStorage::load - loaded " << view->count()
00210 << " tasks from " << _icalfile << endl;
00211 }
00212
00213 return err;
00214 }
00215
00216 QString KarmStorage::buildTaskView(KCal::ResourceCalendar *rc, TaskView *view)
00217
00218 {
00219 QString err;
00220 KCal::Todo::List todoList;
00221 KCal::Todo::List::ConstIterator todo;
00222 QDict< Task > map;
00223 vector<QString> runningTasks;
00224 vector<QDateTime> startTimes;
00225
00226
00227 for ( int i=0; i<view->count(); i++)
00228 {
00229 if ( view->item_at_index(i)->isRunning() )
00230 {
00231 runningTasks.push_back( view->item_at_index(i)->uid() );
00232 startTimes.push_back( view->item_at_index(i)->lastStart() );
00233 }
00234 }
00235
00236
00237
00238 while (view->item_at_index(0)) view->item_at_index(0)->cut();
00239
00240
00241
00242
00243 todoList = rc->rawTodos();
00244 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
00245 {
00246 Task* task = new Task(*todo, view);
00247 map.insert( (*todo)->uid(), task );
00248 view->setRootIsDecorated(true);
00249 task->setPixmapProgress();
00250 }
00251
00252
00253 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
00254 {
00255 Task* task = map.find( (*todo)->uid() );
00256
00257
00258 if ( (*todo)->relatedTo() )
00259 {
00260 Task* newParent = map.find( (*todo)->relatedToUid() );
00261
00262
00263 if ( !newParent )
00264 err = i18n("Error loading \"%1\": could not find parent (uid=%2)")
00265 .arg(task->name())
00266 .arg((*todo)->relatedToUid());
00267
00268 if (!err) task->move( newParent);
00269 }
00270 }
00271
00272 view->clearActiveTasks();
00273
00274 for ( int i=0; i<view->count(); i++)
00275 {
00276 for ( int n=0; n<runningTasks.size(); n++)
00277 {
00278 if ( runningTasks[n] == view->item_at_index(i)->uid() )
00279 {
00280 view->startTimerFor( view->item_at_index(i), startTimes[n] );
00281 }
00282 }
00283 }
00284
00285 view->refresh();
00286
00287 return err;
00288 }
00289
00290 void KarmStorage::closeStorage(TaskView* view)
00291 {
00292 if ( _calendar )
00293 {
00294 _calendar->close();
00295 delete _calendar;
00296 _calendar = 0;
00297
00298 view->clear();
00299 }
00300 }
00301
00302 QString KarmStorage::save(TaskView* taskview)
00303 {
00304 kdDebug(5970) << "entering KarmStorage::save" << endl;
00305 QString err;
00306
00307 QPtrStack< KCal::Todo > parents;
00308
00309 for (Task* task=taskview->first_child(); task; task = task->nextSibling())
00310 {
00311 err=writeTaskAsTodo(task, 1, parents );
00312 }
00313
00314 if ( !saveCalendar() )
00315 {
00316 err="Could not save";
00317 }
00318
00319 if ( err.isEmpty() )
00320 {
00321 kdDebug(5970)
00322 << "KarmStorage::save : wrote "
00323 << taskview->count() << " tasks to " << _icalfile << endl;
00324 }
00325 else
00326 {
00327 kdWarning(5970) << "KarmStorage::save : " << err << endl;
00328 }
00329
00330 return err;
00331 }
00332
00333 QString KarmStorage::writeTaskAsTodo(Task* task, const int level,
00334 QPtrStack< KCal::Todo >& parents )
00335 {
00336 QString err;
00337 KCal::Todo* todo;
00338
00339 todo = _calendar->todo(task->uid());
00340 if ( !todo )
00341 {
00342 kdDebug(5970) << "Could not get todo from calendar" << endl;
00343 return "Could not get todo from calendar";
00344 }
00345 task->asTodo(todo);
00346 if ( !parents.isEmpty() ) todo->setRelatedTo( parents.top() );
00347 parents.push( todo );
00348
00349 for ( Task* nextTask = task->firstChild(); nextTask;
00350 nextTask = nextTask->nextSibling() )
00351 {
00352 err = writeTaskAsTodo(nextTask, level+1, parents );
00353 }
00354
00355 parents.pop();
00356 return err;
00357 }
00358
00359 bool KarmStorage::isEmpty()
00360 {
00361 KCal::Todo::List todoList;
00362
00363 todoList = _calendar->rawTodos();
00364 return todoList.empty();
00365 }
00366
00367 bool KarmStorage::isNewStorage(const Preferences* preferences) const
00368 {
00369 if ( !_icalfile.isNull() ) return preferences->iCalFile() != _icalfile;
00370 else return false;
00371 }
00372
00373
00374
00375
00376
00377
00378 QString KarmStorage::loadFromFlatFile(TaskView* taskview,
00379 const QString& filename)
00380 {
00381 QString err;
00382
00383 kdDebug(5970)
00384 << "KarmStorage::loadFromFlatFile: " << filename << endl;
00385
00386 QFile f(filename);
00387 if( !f.exists() )
00388 err = i18n("File \"%1\" not found.").arg(filename);
00389
00390 if (!err)
00391 {
00392 if( !f.open( IO_ReadOnly ) )
00393 err = i18n("Could not open \"%1\".").arg(filename);
00394 }
00395
00396 if (!err)
00397 {
00398
00399 QString line;
00400
00401 QPtrStack<Task> stack;
00402 Task *task;
00403
00404 QTextStream stream(&f);
00405
00406 while( !stream.atEnd() ) {
00407
00408
00409
00410
00411 line = stream.readLine();
00412 kdDebug(5970) << "DEBUG: line: " << line << "\n";
00413
00414 if (line.isNull())
00415 break;
00416
00417 long minutes;
00418 int level;
00419 QString name;
00420 DesktopList desktopList;
00421 if (!parseLine(line, &minutes, &name, &level, &desktopList))
00422 continue;
00423
00424 unsigned int stackLevel = stack.count();
00425 for (unsigned int i = level; i<=stackLevel ; i++) {
00426 stack.pop();
00427 }
00428
00429 if (level == 1) {
00430 kdDebug(5970) << "KarmStorage::loadFromFlatFile - toplevel task: "
00431 << name << " min: " << minutes << "\n";
00432 task = new Task(name, minutes, 0, desktopList, taskview);
00433 task->setUid(addTask(task, 0));
00434 }
00435 else {
00436 Task *parent = stack.top();
00437 kdDebug(5970) << "KarmStorage::loadFromFlatFile - task: " << name
00438 << " min: " << minutes << " parent" << parent->name() << "\n";
00439 task = new Task(name, minutes, 0, desktopList, parent);
00440
00441 task->setUid(addTask(task, parent));
00442
00443
00444 parent->changeTimes(0, -minutes);
00445 taskview->setRootIsDecorated(true);
00446 parent->setOpen(true);
00447 }
00448 if (!task->uid().isNull())
00449 stack.push(task);
00450 else
00451 delete task;
00452 }
00453
00454 f.close();
00455
00456 }
00457
00458 return err;
00459 }
00460
00461 QString KarmStorage::loadFromFlatFileCumulative(TaskView* taskview,
00462 const QString& filename)
00463 {
00464 QString err = loadFromFlatFile(taskview, filename);
00465 if (!err)
00466 {
00467 for (Task* task = taskview->first_child(); task;
00468 task = task->nextSibling())
00469 {
00470 adjustFromLegacyFileFormat(task);
00471 }
00472 }
00473 return err;
00474 }
00475
00476 bool KarmStorage::parseLine(QString line, long *time, QString *name,
00477 int *level, DesktopList* desktopList)
00478 {
00479 if (line.find('#') == 0) {
00480
00481 return false;
00482 }
00483
00484 int index = line.find('\t');
00485 if (index == -1) {
00486
00487 return false;
00488 }
00489
00490 QString levelStr = line.left(index);
00491 QString rest = line.remove(0,index+1);
00492
00493 index = rest.find('\t');
00494 if (index == -1) {
00495
00496 return false;
00497 }
00498
00499 QString timeStr = rest.left(index);
00500 rest = rest.remove(0,index+1);
00501
00502 bool ok;
00503
00504 index = rest.find('\t');
00505 if (index >= 0) {
00506 *name = rest.left(index);
00507 QString deskLine = rest.remove(0,index+1);
00508
00509
00510
00511 QString ds;
00512 int d;
00513 int commaIdx = deskLine.find(',');
00514 while (commaIdx >= 0) {
00515 ds = deskLine.left(commaIdx);
00516 d = ds.toInt(&ok);
00517 if (!ok)
00518 return false;
00519
00520 desktopList->push_back(d);
00521 deskLine.remove(0,commaIdx+1);
00522 commaIdx = deskLine.find(',');
00523 }
00524
00525 d = deskLine.toInt(&ok);
00526
00527 if (!ok)
00528 return false;
00529
00530 desktopList->push_back(d);
00531 }
00532 else {
00533 *name = rest.remove(0,index+1);
00534 }
00535
00536 *time = timeStr.toLong(&ok);
00537
00538 if (!ok) {
00539
00540 return false;
00541 }
00542 *level = levelStr.toInt(&ok);
00543 if (!ok) {
00544
00545 return false;
00546 }
00547
00548 return true;
00549 }
00550
00551 void KarmStorage::adjustFromLegacyFileFormat(Task* task)
00552 {
00553
00554 if ( task->parent() )
00555 task->parent()->changeTimes(-task->sessionTime(), -task->time());
00556
00557
00558
00559
00560
00561 for ( Task* subtask = task->firstChild(); subtask;
00562 subtask = subtask->nextSibling() )
00563 adjustFromLegacyFileFormat(subtask);
00564 }
00565
00566
00567
00568
00569 QString KarmStorage::exportcsvFile( TaskView *taskview,
00570 const ReportCriteria &rc )
00571 {
00572 QString delim = rc.delimiter;
00573 QString dquote = rc.quote;
00574 QString double_dquote = dquote + dquote;
00575 bool to_quote = true;
00576
00577 QString err;
00578 Task* task;
00579 int maxdepth=0;
00580
00581 kdDebug(5970)
00582 << "KarmStorage::exportcsvFile: " << rc.url << endl;
00583
00584 QString title = i18n("Export Progress");
00585 KProgressDialog dialog( taskview, 0, title );
00586 dialog.setAutoClose( true );
00587 dialog.setAllowCancel( true );
00588 dialog.progressBar()->setTotalSteps( 2 * taskview->count() );
00589
00590
00591 int width = taskview->fontMetrics().width(title) * 3;
00592 QSize dialogsize;
00593 dialogsize.setWidth(width);
00594 dialog.setInitialSize( dialogsize, true );
00595
00596 if ( taskview->count() > 1 ) dialog.show();
00597
00598 QString retval;
00599
00600
00601 int tasknr = 0;
00602 while ( tasknr < taskview->count() && !dialog.wasCancelled() )
00603 {
00604 dialog.progressBar()->advance( 1 );
00605 if ( tasknr % 15 == 0 ) kapp->processEvents();
00606 if ( taskview->item_at_index(tasknr)->depth() > maxdepth )
00607 maxdepth = taskview->item_at_index(tasknr)->depth();
00608 tasknr++;
00609 }
00610
00611
00612 tasknr = 0;
00613 while ( tasknr < taskview->count() && !dialog.wasCancelled() )
00614 {
00615 task = taskview->item_at_index( tasknr );
00616 dialog.progressBar()->advance( 1 );
00617 if ( tasknr % 15 == 0 ) kapp->processEvents();
00618
00619
00620 for ( int i=0; i < task->depth(); ++i ) retval += delim;
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631 to_quote = true;
00632
00633 if (to_quote)
00634 retval += dquote;
00635
00636
00637 retval += task->name().replace( dquote, double_dquote );
00638
00639 if (to_quote)
00640 retval += dquote;
00641
00642
00643 for ( int i = 0; i < maxdepth - task->depth(); ++i ) retval += delim;
00644
00645 retval += delim + formatTime( task->sessionTime(),
00646 rc.decimalMinutes )
00647 + delim + formatTime( task->time(),
00648 rc.decimalMinutes )
00649 + delim + formatTime( task->totalSessionTime(),
00650 rc.decimalMinutes )
00651 + delim + formatTime( task->totalTime(),
00652 rc.decimalMinutes )
00653 + "\n";
00654 tasknr++;
00655 }
00656
00657
00658 if ((rc.url.isLocalFile()) || (!rc.url.url().contains("/")))
00659 {
00660 QString filename=rc.url.path();
00661 if (filename.isEmpty()) filename=rc.url.url();
00662 QFile f( filename );
00663 if( !f.open( IO_WriteOnly ) ) {
00664 err = i18n( "Could not open \"%1\"." ).arg( filename );
00665 }
00666 if (!err)
00667 {
00668 QTextStream stream(&f);
00669
00670 stream << retval;
00671 f.close();
00672 }
00673 }
00674 else
00675 {
00676 KTempFile tmpFile;
00677 if ( tmpFile.status() != 0 ) err = QString::fromLatin1( "Unable to get temporary file" );
00678 else
00679 {
00680 QTextStream *stream=tmpFile.textStream();
00681 *stream << retval;
00682 tmpFile.close();
00683 if (!KIO::NetAccess::upload( tmpFile.name(), rc.url, 0 )) err=QString::fromLatin1("Could not upload");
00684 }
00685 }
00686
00687 return err;
00688 }
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698 QString KarmStorage::addTask(const Task* task, const Task* parent)
00699 {
00700 KCal::Todo* todo;
00701 QString uid;
00702
00703 todo = new KCal::Todo();
00704 if ( _calendar->addTodo( todo ) )
00705 {
00706 task->asTodo( todo );
00707 if (parent)
00708 todo->setRelatedTo(_calendar->todo(parent->uid()));
00709 uid = todo->uid();
00710 }
00711 else
00712 {
00713
00714
00715 uid = "";
00716 }
00717
00718 return uid;
00719 }
00720
00721 bool KarmStorage::removeTask(Task* task)
00722 {
00723
00724
00725 KCal::Event::List eventList = _calendar->rawEvents();
00726 for(KCal::Event::List::iterator i = eventList.begin();
00727 i != eventList.end();
00728 ++i)
00729 {
00730
00731
00732
00733
00734 if ( (*i)->relatedToUid() == task->uid()
00735 || ( (*i)->relatedTo()
00736 && (*i)->relatedTo()->uid() == task->uid()))
00737 {
00738 _calendar->deleteEvent(*i);
00739 }
00740 }
00741
00742
00743 KCal::Todo *todo = _calendar->todo(task->uid());
00744 _calendar->deleteTodo(todo);
00745
00746
00747 saveCalendar();
00748
00749 return true;
00750 }
00751
00752 void KarmStorage::addComment(const Task* task, const QString& comment)
00753 {
00754
00755 KCal::Todo* todo;
00756
00757 todo = _calendar->todo(task->uid());
00758
00759
00760
00761 QString s = comment;
00762
00763
00764
00765
00766 todo->setDescription(task->comment());
00767
00768 saveCalendar();
00769 }
00770
00771 long KarmStorage::printTaskHistory (
00772 const Task *task,
00773 const QMap<QString,long> &taskdaytotals,
00774 QMap<QString,long> &daytotals,
00775 const QDate &from,
00776 const QDate &to,
00777 const int level,
00778 vector <QString> &matrix,
00779 const ReportCriteria &rc)
00780
00781 {
00782 long ownline=linenr++;
00783 long colrectot=0;
00784 vector <QString> cell;
00785 long add;
00786 QString delim = rc.delimiter;
00787 QString dquote = rc.quote;
00788 QString double_dquote = dquote + dquote;
00789 bool to_quote = true;
00790
00791 const QString cr = QString::fromLatin1("\n");
00792 QString buf;
00793 QString daytaskkey, daykey;
00794 QDate day;
00795 long sum;
00796
00797 if ( !task ) return 0;
00798
00799 day = from;
00800 sum = 0;
00801 while (day <= to)
00802 {
00803
00804 daykey = day.toString(QString::fromLatin1("yyyyMMdd"));
00805 daytaskkey = QString::fromLatin1("%1_%2")
00806 .arg(daykey)
00807 .arg(task->uid());
00808
00809 if (taskdaytotals.contains(daytaskkey))
00810 {
00811 cell.push_back(QString::fromLatin1("%1")
00812 .arg(formatTime(taskdaytotals[daytaskkey]/60, rc.decimalMinutes)));
00813 sum += taskdaytotals[daytaskkey];
00814
00815 if (daytotals.contains(daykey))
00816 daytotals.replace(daykey, daytotals[daykey]+taskdaytotals[daytaskkey]);
00817 else
00818 daytotals.insert(daykey, taskdaytotals[daytaskkey]);
00819 }
00820 cell.push_back(delim);
00821
00822 day = day.addDays(1);
00823 }
00824
00825
00826 cell.push_back(QString::fromLatin1("%1").arg(formatTime(sum/60, rc.decimalMinutes)));
00827
00828
00829 cell.push_back(delim);
00830 colrectot = cell.size();
00831 cell.push_back("???");
00832 cell.push_back(delim);
00833
00834
00835 for ( int i = level + 1; i > 0; i-- ) cell.push_back(delim);
00836
00837
00838
00839
00840
00841
00842
00843 to_quote = true;
00844 if ( to_quote) cell.push_back(dquote);
00845
00846
00847
00848 cell.push_back(task->name().replace( dquote, double_dquote ));
00849
00850 if ( to_quote) cell.push_back(dquote);
00851
00852 cell.push_back(cr);
00853
00854 add=0;
00855 for (Task* subTask = task->firstChild();
00856 subTask;
00857 subTask = subTask->nextSibling())
00858 {
00859 add += printTaskHistory( subTask, taskdaytotals, daytotals, from, to , level+1, matrix,
00860 rc );
00861 }
00862 cell[colrectot]=(QString::fromLatin1("%1").arg(formatTime((add+sum)/60, rc.decimalMinutes )));
00863 for (unsigned int i=0; i < cell.size(); i++) matrix[ownline]+=cell[i];
00864 return add+sum;
00865 }
00866
00867 QString KarmStorage::report( TaskView *taskview, const ReportCriteria &rc )
00868 {
00869 QString err;
00870 if ( rc.reportType == ReportCriteria::CSVHistoryExport )
00871 err = exportcsvHistory( taskview, rc.from, rc.to, rc );
00872 else if ( rc.reportType == ReportCriteria::CSVTotalsExport )
00873 err = exportcsvFile( taskview, rc );
00874 else {
00875
00876 }
00877 return err;
00878 }
00879
00880
00881 QString KarmStorage::exportcsvHistory ( TaskView *taskview,
00882 const QDate &from,
00883 const QDate &to,
00884 const ReportCriteria &rc)
00885 {
00886 QString delim = rc.delimiter;
00887 const QString cr = QString::fromLatin1("\n");
00888 QString err;
00889
00890
00891 QString retval;
00892 QString taskhdr, totalhdr;
00893 QString line, buf;
00894 long sum;
00895
00896 QValueList<HistoryEvent> events;
00897 QValueList<HistoryEvent>::iterator event;
00898 QMap<QString, long> taskdaytotals;
00899 QMap<QString, long> daytotals;
00900 QString daytaskkey, daykey;
00901 QDate day;
00902 QDate dayheading;
00903
00904
00905 if ( from > to )
00906 {
00907 err = QString::fromLatin1 (
00908 "'to' has to be a date later than or equal to 'from'.");
00909 }
00910
00911
00912 retval += i18n("Task History\n");
00913 retval += i18n("From %1 to %2")
00914 .arg(KGlobal::locale()->formatDate(from))
00915 .arg(KGlobal::locale()->formatDate(to));
00916 retval += cr;
00917 retval += i18n("Printed on: %1")
00918 .arg(KGlobal::locale()->formatDateTime(QDateTime::currentDateTime()));
00919 retval += cr;
00920
00921 day=from;
00922 events = taskview->getHistory(from, to);
00923 taskdaytotals.clear();
00924 daytotals.clear();
00925
00926
00927
00928
00929
00930
00931 for (event = events.begin(); event != events.end(); ++event)
00932 {
00933 daykey = (*event).start().date().toString(QString::fromLatin1("yyyyMMdd"));
00934 daytaskkey = QString(QString::fromLatin1("%1_%2"))
00935 .arg(daykey)
00936 .arg((*event).todoUid());
00937
00938 if (taskdaytotals.contains(daytaskkey))
00939 taskdaytotals.replace(daytaskkey,
00940 taskdaytotals[daytaskkey] + (*event).duration());
00941 else
00942 taskdaytotals.insert(daytaskkey, (*event).duration());
00943 }
00944
00945
00946 dayheading = from;
00947 while ( dayheading <= to )
00948 {
00949
00950 retval += dayheading.toString(QString::fromLatin1("yyyy-MM-dd"));
00951 retval += delim;
00952 dayheading=dayheading.addDays(1);
00953 }
00954 retval += i18n("Sum") + delim + i18n("Total Sum") + delim + i18n("Task Hierarchy");
00955 retval += cr;
00956 retval += line;
00957
00958
00959 vector <QString> matrix;
00960 linenr=0;
00961 for (int i=0; i<=taskview->count()+1; i++) matrix.push_back("");
00962 if (events.empty())
00963 {
00964 retval += i18n(" No hours logged.");
00965 }
00966 else
00967 {
00968 if ( rc.allTasks )
00969 {
00970 for ( Task* task= taskview->item_at_index(0);
00971 task; task= task->nextSibling() )
00972 {
00973 printTaskHistory( task, taskdaytotals, daytotals, from, to, 0,
00974 matrix, rc );
00975 }
00976 }
00977 else
00978 {
00979 printTaskHistory( taskview->current_item(), taskdaytotals, daytotals,
00980 from, to, 0, matrix, rc );
00981 }
00982 for (unsigned int i=0; i<matrix.size(); i++) retval+=matrix[i];
00983 retval += line;
00984
00985
00986 sum = 0;
00987 day = from;
00988 while (day<=to)
00989 {
00990 daykey = day.toString(QString::fromLatin1("yyyyMMdd"));
00991
00992 if (daytotals.contains(daykey))
00993 {
00994 retval += QString::fromLatin1("%1")
00995 .arg(formatTime(daytotals[daykey]/60, rc.decimalMinutes));
00996 sum += daytotals[daykey];
00997 }
00998 retval += delim;
00999 day = day.addDays(1);
01000 }
01001
01002 retval += QString::fromLatin1("%1%2%3%4")
01003 .arg( formatTime( sum/60, rc.decimalMinutes ) )
01004 .arg( delim ).arg( delim )
01005 .arg( i18n( "Total" ) );
01006 }
01007
01008
01009
01010
01011
01012 if ((rc.url.isLocalFile()) || (!rc.url.url().contains("/")))
01013 {
01014 QString filename=rc.url.path();
01015 if (filename.isEmpty()) filename=rc.url.url();
01016 QFile f( filename );
01017 if( !f.open( IO_WriteOnly ) ) {
01018 err = i18n( "Could not open \"%1\"." ).arg( filename );
01019 }
01020 if (!err)
01021 {
01022 QTextStream stream(&f);
01023
01024 stream << retval;
01025 f.close();
01026 }
01027 }
01028 else
01029 {
01030 KTempFile tmpFile;
01031 if ( tmpFile.status() != 0 )
01032 {
01033 err = QString::fromLatin1( "Unable to get temporary file" );
01034 }
01035 else
01036 {
01037 QTextStream *stream=tmpFile.textStream();
01038 *stream << retval;
01039 tmpFile.close();
01040 if (!KIO::NetAccess::upload( tmpFile.name(), rc.url, 0 )) err=QString::fromLatin1("Could not upload");
01041 }
01042 }
01043 return err;
01044 }
01045
01046 void KarmStorage::stopTimer(const Task* task)
01047 {
01048 long delta = task->startTime().secsTo(QDateTime::currentDateTime());
01049 changeTime(task, delta);
01050 }
01051
01052 bool KarmStorage::bookTime(const Task* task,
01053 const QDateTime& startDateTime,
01054 const long durationInSeconds)
01055 {
01056
01057 KCal::Event* e;
01058 QDateTime end;
01059
01060 e = baseEvent( task );
01061 e->setDtStart( startDateTime );
01062 e->setDtEnd( startDateTime.addSecs( durationInSeconds ) );
01063
01064
01065 e->setCustomProperty( kapp->instanceName(),
01066 QCString("duration"),
01067 QString::number(durationInSeconds));
01068
01069 return _calendar->addEvent(e);
01070 }
01071
01072 void KarmStorage::changeTime(const Task* task, const long deltaSeconds)
01073 {
01074 KCal::Event* e;
01075 QDateTime end;
01076
01077
01078
01079 if ( ! task->taskView()->preferences()->logging() ) return;
01080
01081 e = baseEvent(task);
01082
01083
01084
01085 end = task->startTime();
01086 if ( deltaSeconds > 0 ) end = task->startTime().addSecs(deltaSeconds);
01087 e->setDtEnd(end);
01088
01089
01090 e->setCustomProperty( kapp->instanceName(),
01091 QCString("duration"),
01092 QString::number(deltaSeconds));
01093
01094 _calendar->addEvent(e);
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104 task->taskView()->scheduleSave();
01105 }
01106
01107
01108 KCal::Event* KarmStorage::baseEvent(const Task * task)
01109 {
01110 KCal::Event* e;
01111 QStringList categories;
01112
01113 e = new KCal::Event;
01114 e->setSummary(task->name());
01115
01116
01117 e->setRelatedTo(_calendar->todo(task->uid()));
01118
01119
01120 assert(e->relatedTo()->uid() == task->uid());
01121
01122
01123 e->setFloats(false);
01124 e->setDtStart(task->startTime());
01125
01126
01127 categories.append(i18n("KArm"));
01128 e->setCategories(categories);
01129
01130 return e;
01131 }
01132
01133 HistoryEvent::HistoryEvent(QString uid, QString name, long duration,
01134 QDateTime start, QDateTime stop, QString todoUid)
01135 {
01136 _uid = uid;
01137 _name = name;
01138 _duration = duration;
01139 _start = start;
01140 _stop = stop;
01141 _todoUid = todoUid;
01142 }
01143
01144
01145 QValueList<HistoryEvent> KarmStorage::getHistory(const QDate& from,
01146 const QDate& to)
01147 {
01148 QValueList<HistoryEvent> retval;
01149 QStringList processed;
01150 KCal::Event::List events;
01151 KCal::Event::List::iterator event;
01152 QString duration;
01153
01154 for(QDate d = from; d <= to; d = d.addDays(1))
01155 {
01156 events = _calendar->rawEventsForDate( d );
01157 for (event = events.begin(); event != events.end(); ++event)
01158 {
01159
01160
01161 if (! processed.contains( (*event)->uid()))
01162 {
01163
01164
01165
01166
01167
01168 processed.append( (*event)->uid());
01169
01170 duration = (*event)->customProperty(kapp->instanceName(),
01171 QCString("duration"));
01172 if ( ! duration.isNull() )
01173 {
01174 if ( (*event)->relatedTo()
01175 && ! (*event)->relatedTo()->uid().isEmpty() )
01176 {
01177 retval.append(HistoryEvent(
01178 (*event)->uid(),
01179 (*event)->summary(),
01180 duration.toLong(),
01181 (*event)->dtStart(),
01182 (*event)->dtEnd(),
01183 (*event)->relatedTo()->uid()
01184 ));
01185 }
01186 else
01187
01188
01189
01190 kdDebug(5970) << "KarmStorage::getHistory(): "
01191 << "The event " << (*event)->uid()
01192 << " is not related to a todo. Dropped." << endl;
01193 }
01194 }
01195 }
01196 }
01197
01198 return retval;
01199 }
01200
01201 bool KarmStorage::remoteResource( const QString& file ) const
01202 {
01203 QString f = file.lower();
01204 bool rval = f.startsWith( "http://" ) || f.startsWith( "ftp://" );
01205
01206 kdDebug(5970) << "KarmStorage::remoteResource( " << file << " ) returns " << rval << endl;
01207 return rval;
01208 }
01209
01210 bool KarmStorage::saveCalendar()
01211 {
01212 kdDebug(5970) << "KarmStorage::saveCalendar" << endl;
01213
01214 KABC::Lock *lock = _calendar->lock();
01215 if ( !lock || !lock->lock() )
01216 return false;
01217
01218 if ( _calendar && _calendar->save() ) {
01219 lock->unlock();
01220 return true;
01221 }
01222
01223 lock->unlock();
01224 return false;
01225 }