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