• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

ktimetracker

  • sources
  • kde-4.12
  • kdepim
  • ktimetracker
timetrackerstorage.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2003, 2004 by Mark Bucciarelli <mark@hubcapconsutling.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the
16  * Free Software Foundation, Inc.
17  * 51 Franklin Street, Fifth Floor
18  * Boston, MA 02110-1301 USA.
19  *
20  */
21 
31 #include "timetrackerstorage.h"
32 #include "ktimetrackerutility.h"
33 #include "ktimetracker.h"
34 #include "storageadaptor.h"
35 #include "task.h"
36 #include "taskview.h"
37 #include "timekard.h"
38 
39 #include <kemailsettings.h>
40 #include <kio/netaccess.h>
41 #include <KCalCore/Person>
42 #include <KDirWatch>
43 #include <KLockFile>
44 #include <KApplication> // kapp
45 #include <KDebug>
46 #include <KLocale> // i18n
47 #include <KMessageBox>
48 #include <KProgressDialog>
49 #include <KSystemTimeZones>
50 #include <KTemporaryFile>
51 #include <KUrl>
52 
53 #include <QByteArray>
54 #include <QDateTime>
55 #include <QFile>
56 #include <QList>
57 #include <QMultiHash>
58 #include <QSize>
59 #include <QString>
60 #include <QStringList>
61 #include <QTableWidget>
62 #include <QTextStream>
63 
64 #include <sys/stat.h>
65 #include <sys/types.h>
66 #include <unistd.h>
67 #include <fcntl.h>
68 #include <QMap>
69 
70 using namespace KTimeTracker;
71 
72 //@cond PRIVATE
73 class timetrackerstorage::Private
74 {
75 public:
76  Private()
77  {
78  m_fileLock = new KLockFile( QLatin1String( "ktimetrackerics.lock" ) );
79  }
80  ~Private()
81  {
82  delete m_fileLock;
83  }
84  KTimeTracker::KTTCalendar::Ptr mCalendar;
85  QString mICalFile;
86  KLockFile *m_fileLock;
87 };
88 //@endcond
89 
90 timetrackerstorage::timetrackerstorage() : d( new Private() )
91 {
92 }
93 
94 timetrackerstorage::~timetrackerstorage()
95 {
96  delete d;
97 }
98 
99 QString timetrackerstorage::load(TaskView* view, const QString &fileName)
100 // loads data from filename into view. If no filename is given, filename from preferences is used.
101 // filename might be of use if this program is run as embedded konqueror plugin.
102 {
103  // loading might create the file
104  bool removedFromDirWatch = false;
105  if ( KDirWatch::self()->contains( d->mICalFile ) ) {
106  KDirWatch::self()->removeFile( d->mICalFile );
107  removedFromDirWatch = true;
108  }
109  kDebug(5970) << "Entering function";
110  QString err;
111  KEMailSettings settings;
112  QString lFileName = fileName;
113 
114  Q_ASSERT( !( lFileName.isEmpty() ) );
115 
116  // If same file, don't reload
117  if ( lFileName == d->mICalFile ) {
118  if ( removedFromDirWatch ) {
119  KDirWatch::self()->addFile( d->mICalFile );
120  }
121  return err;
122  }
123  // If file doesn't exist, create a blank one to avoid ResourceLocal load
124  // error. We make it user and group read/write, others read. This is
125  // masked by the users umask. (See man creat)
126  const bool fileIsLocal = !isRemoteFile( lFileName );
127  if ( fileIsLocal )
128  {
129  int handle;
130  handle = open ( QFile::encodeName( lFileName ), O_CREAT | O_EXCL | O_WRONLY,
131  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH );
132  if ( handle != -1 )
133  {
134  close( handle );
135  }
136  }
137  if ( d->mCalendar )
138  closeStorage();
139  // Create local file resource and add to resources
140  d->mICalFile = lFileName;
141  d->mCalendar = KTTCalendar::createInstance( d->mICalFile, /*monitorFile=*/ fileIsLocal );
142 
143  QObject::connect( d->mCalendar.data(), SIGNAL(calendarChanged()),
144  view, SLOT(iCalFileModified()) );
145  d->mCalendar->setTimeSpec( KSystemTimeZones::local() );
146  d->mCalendar->reload();
147 
148  // Claim ownership of iCalendar file if no one else has.
149  KCalCore::Person::Ptr owner = d->mCalendar->owner();
150  if ( owner && owner->isEmpty() )
151  {
152  d->mCalendar->setOwner( KCalCore::Person::Ptr(
153  new KCalCore::Person( settings.getSetting( KEMailSettings::RealName ),
154  settings.getSetting( KEMailSettings::EmailAddress ) ) ) );
155  }
156 
157  // Build task view from iCal data
158  if (!err.isEmpty())
159  {
160  KCalCore::Todo::List todoList;
161  KCalCore::Todo::List::ConstIterator todo;
162  QMultiHash< QString, Task* > map;
163 
164  // Build dictionary to look up Task object from Todo uid. Each task is a
165  // QListViewItem, and is initially added with the view as the parent.
166  todoList = d->mCalendar->rawTodos();
167  kDebug(5970) << "timetrackerstorage::load"
168  << "rawTodo count (includes completed todos) ="
169  << todoList.count();
170  for (todo = todoList.constBegin(); todo != todoList.constEnd(); ++todo)
171  {
172  Task* task = new Task(*todo, view);
173  map.insert( (*todo)->uid(), task );
174  view->setRootIsDecorated(true);
175  task->setPixmapProgress();
176  }
177 
178  // Load each task under its parent task.
179  for (todo = todoList.constBegin(); todo != todoList.constEnd(); ++todo)
180  {
181  Task* task = map.value( (*todo)->uid() );
182  // No relatedTo incident just means this is a top-level task.
183  if ( !(*todo)->relatedTo().isEmpty() )
184  {
185  Task *newParent = map.value( (*todo)->relatedTo() );
186 
187  // Complete the loading but return a message
188  if ( !newParent )
189  err = i18n("Error loading \"%1\": could not find parent (uid=%2)",
190  task->name(), (*todo)->relatedTo() );
191 
192  if (!err.isEmpty()) task->move( newParent );
193  }
194  }
195 
196  kDebug(5970) << "timetrackerstorage::load - loaded" << view->count()
197  << "tasks from" << d->mICalFile;
198  }
199 
200  if ( view ) buildTaskView(d->mCalendar->weakPointer(), view);
201 
202  if ( removedFromDirWatch ) {
203  KDirWatch::self()->addFile( d->mICalFile );
204  }
205  return err;
206 }
207 
208 Task* timetrackerstorage::task(const QString& uid, TaskView* view)
209 // return the tasks with the uid uid out of view.
210 // If !view, return the todo with the uid uid.
211 {
212  kDebug(5970) << "Entering function";
213  KCalCore::Todo::List todoList;
214  KCalCore::Todo::List::ConstIterator todo;
215  todoList = d->mCalendar->rawTodos();
216  todo = todoList.constBegin();
217  Task* result=0;
218  bool konsolemode=false;
219  if ( view == 0 ) konsolemode=true;
220  while ( todo != todoList.constEnd() && ( (*todo)->uid() != uid ) ) ++todo;
221  if ( todo != todoList.constEnd() ) result = new Task((*todo), view, konsolemode);
222  kDebug(5970) << "Leaving function, returning " << result;
223  return result;
224 }
225 
226 QString timetrackerstorage::icalfile()
227 {
228  kDebug(5970) << "Entering function";
229  return d->mICalFile;
230 }
231 
232 QString timetrackerstorage::buildTaskView( const KTimeTracker::KTTCalendar::Ptr &calendar,
233  TaskView *view )
234 // makes *view contain the tasks out of *rc.
235 {
236  kDebug(5970) << "Entering function";
237  QString err;
238  KCalCore::Todo::List todoList;
239  KCalCore::Todo::List::ConstIterator todo;
240  QMultiHash< QString, Task* > map;
241  QVector<QString> runningTasks;
242  QVector<QDateTime> startTimes;
243 
244  // remember tasks that are running and their start times
245  QTreeWidgetItemIterator it( view );
246  while ( *it )
247  {
248  Task *task = static_cast< Task* >( *it );
249  if ( task->isRunning() )
250  {
251  runningTasks.append( task->uid() );
252  startTimes.append( task->startTime() );
253  }
254  ++it;
255  }
256 
257  view->clear();
258  todoList = calendar->rawTodos();
259  for ( todo = todoList.constBegin(); todo != todoList.constEnd(); ++todo )
260  {
261  Task* task = new Task(*todo, view);
262  task->setWhatsThis(0,i18n("The task name is what you call the task, it can be chosen freely."));
263  task->setWhatsThis(1,i18n("The session time is the time since you last chose \"start new session.\""));
264  map.insert( (*todo)->uid(), task );
265  view->setRootIsDecorated(true);
266  task->setPixmapProgress();
267  }
268 
269  // 1.1. Load each task under its parent task.
270  for( todo = todoList.constBegin(); todo != todoList.constEnd(); ++todo )
271  {
272  Task* task = map.value( (*todo)->uid() );
273  // No relatedTo incident just means this is a top-level task.
274  if ( !(*todo)->relatedTo().isEmpty() )
275  {
276  Task* newParent = map.value( (*todo)->relatedTo() );
277  // Complete the loading but return a message
278  if ( !newParent )
279  err = i18n("Error loading \"%1\": could not find parent (uid=%2)",
280  task->name(),
281  (*todo)->relatedTo());
282  else task->move( newParent );
283  }
284  }
285 
286  view->clearActiveTasks();
287  // restart tasks that have been running with their start times
288  for ( int i=0; i<view->count(); ++i)
289  {
290  for ( int n = 0; n < runningTasks.count(); ++n )
291  {
292  if ( runningTasks[n] == view->itemAt(i)->uid() )
293  {
294  view->startTimerFor( view->itemAt(i), startTimes[n] );
295  }
296  }
297  }
298 
299  view->refresh();
300  return err;
301 }
302 
303 QString timetrackerstorage::buildTaskView(TaskView *view)
304 // makes *view contain the tasks out of mCalendar
305 {
306  return buildTaskView(d->mCalendar, view);
307 }
308 
309 void timetrackerstorage::closeStorage()
310 {
311  kDebug(5970) << "Entering function";
312  if ( d->mCalendar )
313  {
314  d->mCalendar->close();
315  d->mCalendar = KTTCalendar::Ptr();
316  }
317  kDebug(5970) << "Leaving function";
318 }
319 
320 KCalCore::Event::List timetrackerstorage::rawevents()
321 {
322  kDebug(5970) << "Entering function";
323  return d->mCalendar->rawEvents();
324 }
325 
326 KCalCore::Todo::List timetrackerstorage::rawtodos()
327 {
328  kDebug(5970) << "Entering function";
329  return d->mCalendar->rawTodos();
330 }
331 
332 bool timetrackerstorage::allEventsHaveEndTiMe(Task* task)
333 {
334  kDebug(5970) << "Entering function";
335  KCalCore::Event::List eventList = d->mCalendar->rawEvents();
336  for(KCalCore::Event::List::iterator i = eventList.begin();
337  i != eventList.end(); ++i)
338  {
339  if ( (*i)->relatedTo() == task->uid() && !(*i)->hasEndDate() ) {
340  return false;
341  }
342  }
343  return true;
344 }
345 
346 bool timetrackerstorage::allEventsHaveEndTiMe()
347 {
348  kDebug(5970) << "Entering function";
349  KCalCore::Event::List eventList = d->mCalendar->rawEvents();
350  for(KCalCore::Event::List::iterator i = eventList.begin();
351  i != eventList.end(); ++i)
352  {
353  if ( !(*i)->hasEndDate() ) return false;
354  }
355  return true;
356 }
357 
358 QString timetrackerstorage::deleteAllEvents()
359 {
360  kDebug(5970) << "Entering function";
361  QString err;
362  KCalCore::Event::List eventList = d->mCalendar->rawEvents();
363  d->mCalendar->deleteAllEvents();
364  return err;
365 }
366 
367 QString timetrackerstorage::save(TaskView* taskview)
368 {
369  kDebug(5970) << "Entering function";
370  QString errorString;
371 
372  QStack<KCalCore::Todo::Ptr> parents;
373  if ( taskview ) // we may also be in konsole mode
374  {
375  for (int i = 0; i < taskview->topLevelItemCount(); ++i )
376  {
377  Task *task = static_cast< Task* >( taskview->topLevelItem( i ) );
378  kDebug( 5970 ) << "write task" << task->name();
379  errorString = writeTaskAsTodo( task, parents );
380  }
381  }
382 
383  errorString = saveCalendar();
384 
385  if ( errorString.isEmpty() ) {
386  kDebug(5970) << "timetrackerstorage::save : wrote tasks to" << d->mICalFile;
387  } else {
388  kWarning(5970) << "timetrackerstorage::save :" << errorString;
389  }
390 
391  return errorString;
392 }
393 
394 QString timetrackerstorage::setTaskParent(Task* task, Task* parent)
395 {
396  kDebug(5970) << "Entering function";
397  QString err;
398  KCalCore::Todo::Ptr todo = d->mCalendar->todo( task->uid() );
399  if ( !parent ) {
400  todo->setRelatedTo( QString() );
401  } else {
402  todo->setRelatedTo( parent->uid() );
403  }
404  kDebug(5970) << "Leaving function";
405  return err;
406 }
407 
408 QString timetrackerstorage::writeTaskAsTodo(Task* task, QStack<KCalCore::Todo::Ptr>& parents)
409 {
410  kDebug(5970) << "Entering function";
411  QString err;
412  KCalCore::Todo::Ptr todo = d->mCalendar->todo(task->uid());
413  if ( !todo )
414  {
415  kDebug(5970) << "Could not get todo from calendar";
416  return "Could not get todo from calendar";
417  }
418  task->asTodo(todo);
419  if ( !parents.isEmpty() ) todo->setRelatedTo( parents.top() ? parents.top()->uid() : QString() );
420  parents.push( todo );
421 
422  for ( int i = 0; i < task->childCount(); ++i )
423  {
424  Task *nextTask = static_cast< Task* >( task->child( i ) );
425  err = writeTaskAsTodo( nextTask, parents );
426  }
427 
428  parents.pop();
429  return err;
430 }
431 
432 bool timetrackerstorage::isEmpty()
433 {
434  kDebug(5970) << "Entering function";
435  return d->mCalendar->rawTodos().isEmpty();
436 }
437 
438 //----------------------------------------------------------------------------
439 // Routines that handle logging ktimetracker history
440 
441 
442 QString timetrackerstorage::addTask(const Task* task, const Task* parent)
443 {
444  kDebug(5970) << "Entering function";
445  KCalCore::Todo::Ptr todo;
446  QString uid;
447 
448  if ( !d->mCalendar )
449  {
450  kDebug(5970) << "mCalendar is not set";
451  return uid;
452  }
453  todo = KCalCore::Todo::Ptr( new KCalCore::Todo() );
454  if ( d->mCalendar->addTodo( todo ) )
455  {
456  task->asTodo( todo );
457  if (parent)
458  todo->setRelatedTo( parent->uid() );
459  uid = todo->uid();
460  }
461  else
462  {
463  // Most likely a lock could not be pulled, although there are other
464  // possiblities (like a really confused resource manager).
465  uid.clear();;
466  }
467  return uid;
468 }
469 
470 QStringList timetrackerstorage::taskidsfromname(QString taskname)
471 {
472  kDebug(5970) << "Entering function";
473  QStringList result;
474  KCalCore::Todo::List todoList = d->mCalendar->rawTodos();
475  for(KCalCore::Todo::List::iterator i = todoList.begin();
476  i != todoList.end(); ++i)
477  {
478  kDebug(5970) << (*i)->uid();
479  if ( (*i)->summary() == taskname ) result << (*i)->uid();
480  }
481  return result;
482 }
483 
484 QStringList timetrackerstorage::taskNames() const
485 {
486  kDebug(5970) << "Entering function";
487  QStringList result;
488  KCalCore::Todo::List todoList = d->mCalendar->rawTodos();
489  for(KCalCore::Todo::List::iterator i = todoList.begin();
490  i != todoList.end(); ++i) result << (*i)->summary();
491  return result;
492 }
493 
494 QString timetrackerstorage::removeEvent(QString uid)
495 {
496  kDebug(5970) << "Entering function";
497  QString err=QString();
498  KCalCore::Event::List eventList = d->mCalendar->rawEvents();
499  for(KCalCore::Event::List::iterator i = eventList.begin();
500  i != eventList.end(); ++i)
501  {
502  if ( (*i)->uid() == uid )
503  {
504  d->mCalendar->deleteEvent(*i);
505  }
506  }
507  return err;
508 }
509 
510 bool timetrackerstorage::removeTask(Task* task)
511 {
512  kDebug(5970) << "Entering function";
513  // delete history
514  KCalCore::Event::List eventList = d->mCalendar->rawEvents();
515  for(KCalCore::Event::List::iterator i = eventList.begin();
516  i != eventList.end(); ++i)
517  {
518  if ( (*i)->relatedTo() == task->uid() )
519  {
520  d->mCalendar->deleteEvent(*i);
521  }
522  }
523 
524  // delete todo
525  KCalCore::Todo::Ptr todo = d->mCalendar->todo(task->uid());
526  d->mCalendar->deleteTodo(todo);
527  // Save entire file
528  saveCalendar();
529 
530  return true;
531 }
532 
533 bool timetrackerstorage::removeTask(QString taskid)
534 {
535  kDebug(5970) << "Entering function";
536  // delete history
537  KCalCore::Event::List eventList = d->mCalendar->rawEvents();
538  for(KCalCore::Event::List::iterator i = eventList.begin();
539  i != eventList.end();
540  ++i)
541  {
542  if ( (*i)->relatedTo() == taskid )
543  {
544  d->mCalendar->deleteEvent(*i);
545  }
546  }
547 
548  // delete todo
549  KCalCore::Todo::Ptr todo = d->mCalendar->todo(taskid);
550  d->mCalendar->deleteTodo(todo);
551 
552  // Save entire file
553  saveCalendar();
554 
555  return true;
556 }
557 
558 void timetrackerstorage::addComment(const Task* task, const QString& comment)
559 {
560  kDebug(5970) << "Entering function";
561  KCalCore::Todo::Ptr todo = d->mCalendar->todo(task->uid());
562 
563  // Do this to avoid compiler warnings about comment not being used. once we
564  // transition to using the addComment method, we need this second param.
565  QString s = comment;
566 
567  // TODO: Use libkcalcore comments
568  // todo->addComment(comment);
569  // temporary
570  todo->setDescription(task->comment());
571 
572  saveCalendar();
573 }
574 
575 QString timetrackerstorage::report(TaskView *taskview, const ReportCriteria &rc)
576 {
577  kDebug(5970) << "Entering function";
578  QString err;
579  if ( rc.reportType == ReportCriteria::CSVHistoryExport )
580  {
581  err = exportcsvHistory( taskview, rc.from, rc.to, rc );
582  }
583  else // if ( rc.reportType == ReportCriteria::CSVTotalsExport )
584  {
585  if ( !rc.bExPortToClipBoard )
586  err = exportcsvFile( taskview, rc );
587  else
588  err = taskview->clipTotals( rc );
589  }
590  return err;
591 }
592 
593 
594 //----------------------------------------------------------------------------
595 // Routines that handle Comma-Separated Values export file format.
596 //
597 QString timetrackerstorage::exportcsvFile(TaskView *taskview, const ReportCriteria &rc)
598 {
599  kDebug(5970) << "Entering function";
600  QString delim = rc.delimiter;
601  QString dquote = rc.quote;
602  QString double_dquote = dquote + dquote;
603  bool to_quote = true;
604  QString err;
605  Task* task;
606  int maxdepth=0;
607  QString title = i18n("Export Progress");
608  KProgressDialog dialog( taskview, 0, title );
609  dialog.setAutoClose( true );
610  dialog.setAllowCancel( true );
611  dialog.progressBar()->setMaximum( 2 * taskview->count() );
612 
613  // The default dialog was not displaying all the text in the title bar.
614  int width = taskview->fontMetrics().width(title) * 3;
615  QSize dialogsize;
616  dialogsize.setWidth(width);
617  dialog.setInitialSize( dialogsize );
618 
619  if ( taskview->count() > 1 ) dialog.show();
620  QString retval;
621 
622  // Find max task depth
623  int tasknr = 0;
624  while ( tasknr < taskview->count() && !dialog.wasCancelled() )
625  {
626  dialog.progressBar()->setValue( dialog.progressBar()->value() + 1 );
627  if ( tasknr % 15 == 0 ) kapp->processEvents(); // repainting is slow
628  if ( taskview->itemAt(tasknr)->depth() > maxdepth )
629  maxdepth = taskview->itemAt(tasknr)->depth();
630  tasknr++;
631  }
632 
633  // Export to file
634  tasknr = 0;
635  while ( tasknr < taskview->count() && !dialog.wasCancelled() )
636  {
637  task = taskview->itemAt( tasknr );
638  dialog.progressBar()->setValue( dialog.progressBar()->value() + 1 );
639  if ( tasknr % 15 == 0 ) kapp->processEvents();
640 
641  // indent the task in the csv-file:
642  for ( int i=0; i < task->depth(); ++i ) retval += delim;
643 
644  /*
645  // CSV compliance
646  // Surround the field with quotes if the field contains
647  // a comma (delim) or a double quote
648  if (task->name().contains(delim) || task->name().contains(dquote))
649  to_quote = true;
650  else
651  to_quote = false;
652  */
653  to_quote = true;
654 
655  if (to_quote)
656  retval += dquote;
657 
658  // Double quotes replaced by a pair of consecutive double quotes
659  retval += task->name().replace( dquote, double_dquote );
660 
661  if (to_quote)
662  retval += dquote;
663 
664  // maybe other tasks are more indented, so to align the columns:
665  for ( int i = 0; i < maxdepth - task->depth(); ++i ) retval += delim;
666 
667  retval += delim + formatTime( task->sessionTime(),
668  rc.decimalMinutes )
669  + delim + formatTime( task->time(),
670  rc.decimalMinutes )
671  + delim + formatTime( task->totalSessionTime(),
672  rc.decimalMinutes )
673  + delim + formatTime( task->totalTime(),
674  rc.decimalMinutes )
675  + '\n';
676  tasknr++;
677  }
678 
679  // save, either locally or remote
680  if ((rc.url.isLocalFile()) || (!rc.url.url().contains("/")))
681  {
682  QString filename=rc.url.toLocalFile();
683  if (filename.isEmpty()) filename=rc.url.url();
684  QFile f( filename );
685  if( !f.open( QIODevice::WriteOnly ) )
686  {
687  err = i18n( "Could not open \"%1\".", filename );
688  }
689  if (err.length()==0)
690  {
691  QTextStream stream(&f);
692  // Export to file
693  stream << retval;
694  f.close();
695  }
696  }
697  else // use remote file
698  {
699  KTemporaryFile tmpFile;
700  if ( !tmpFile.open() ) err = QString::fromLatin1( "Unable to get temporary file" );
701  else
702  {
703  QTextStream stream ( &tmpFile );
704  stream << retval;
705  stream.flush();
706  if (!KIO::NetAccess::upload( tmpFile.fileName(), rc.url, 0 )) err=QString::fromLatin1("Could not upload");
707  }
708  }
709 
710  return err;
711 }
712 
713 int todaySeconds (const QDate &date, const KCalCore::Event::Ptr &event)
714 {
715  if ( !event )
716  return 0;
717 
718  kDebug(5970) << "found an event for task, event=" << event->uid();
719  KDateTime startTime=event->dtStart();
720  KDateTime endTime=event->dtEnd();
721  KDateTime NextMidNight=startTime;
722  NextMidNight.setTime(QTime ( 0,0 ));
723  NextMidNight=NextMidNight.addDays(1);
724  // LastMidNight := mdate.setTime(0:00) as it would read in a decent programming language
725  KDateTime LastMidNight=KDateTime::currentLocalDateTime();
726  LastMidNight.setDate(date);
727  LastMidNight.setTime(QTime(0,0));
728  int secsstartTillMidNight=startTime.secsTo(NextMidNight);
729  int secondsToAdd=0; // seconds that need to be added to the actual cell
730  if ( (startTime.date()==date) && (event->dtEnd().date()==date) ) // all the event occurred today
731  secondsToAdd=startTime.secsTo(endTime);
732  if ( (startTime.date()==date) && (endTime.date()>date) ) // the event started today, but ended later
733  secondsToAdd=secsstartTillMidNight;
734  if ( (startTime.date()<date) && (endTime.date()==date) ) // the event started before today and ended today
735  secondsToAdd=LastMidNight.secsTo(event->dtEnd());
736  if ( (startTime.date()<date) && (endTime.date()>date) ) // the event started before today and ended after
737  secondsToAdd=86400;
738 
739  return secondsToAdd;
740 }
741 
742 // export history report as csv, all tasks X all dates in one block
743 QString timetrackerstorage::exportcsvHistory (TaskView *taskview,
744  const QDate &from,
745  const QDate &to,
746  const ReportCriteria &rc)
747 {
748  kDebug(5970) << "Entering function";
749 
750  QString delim = rc.delimiter;
751  const QString cr = QString::fromLatin1("\n");
752  QString err=QString::null;
753  QString retval;
754  Task* task;
755  const int intervalLength = from.daysTo(to)+1;
756  QMap< QString, QVector<int> > secsForUid;
757  QMap< QString, QString > uidForName;
758 
759 
760  // Step 1: Prepare two hashmaps:
761  // * "uid -> seconds each day": used while traversing events, as uid is their id
762  // "seconds each day" are stored in a vector
763  // * "name -> uid", ordered by name: used when creating the csv file at the end
764  kDebug(5970) << "Taskview Count: " << taskview->count();
765  for ( int n=0; n<taskview->count(); n++ )
766  {
767  task=taskview->itemAt(n);
768  kDebug(5970) << "n: " << n << ", Task Name: " << task->name() << ", UID: " << task->uid();
769  // uid -> seconds each day
770  // * Init each element to zero
771  QVector<int> vector(intervalLength, 0);
772  secsForUid[task->uid()] = vector;
773 
774  // name -> uid
775  // * Create task fullname concatenating each parent's name
776  QString fullName;
777  Task* parentTask;
778  parentTask = task;
779  fullName += parentTask->name();
780  parentTask = parentTask->parent();
781  while (parentTask)
782  {
783  fullName = parentTask->name() + "->" + fullName;
784  kDebug(5970) << "Fullname(inside): " << fullName;
785  parentTask = parentTask->parent();
786  kDebug(5970) << "Parent task: " << parentTask;
787  }
788 
789  uidForName[fullName] = task->uid();
790 
791  kDebug(5970) << "Fullname(end): " << fullName;
792  }
793 
794  kDebug(5970) << "secsForUid" << secsForUid;
795  kDebug(5970) << "uidForName" << uidForName;
796 
797 
798  // Step 2: For each date, get the events and calculate the seconds
799  // Store the seconds using secsForUid hashmap, so we don't need to translate uids
800  // We rely on rawEventsForDate to get the events
801  kDebug(5970) << "Let's iterate for each date: ";
802  for ( QDate mdate=from; mdate.daysTo(to)>=0; mdate=mdate.addDays(1) )
803  {
804  kDebug(5970) << mdate.toString();
805  KCalCore::Event::List dateEvents = d->mCalendar->rawEventsForDate(mdate);
806 
807  for(KCalCore::Event::List::iterator i = dateEvents.begin();i != dateEvents.end(); ++i)
808  {
809  kDebug(5970) << "Summary: " << (*i)->summary() << ", Related to uid: " << (*i)->relatedTo();
810  kDebug(5970) << "Today's seconds: " << todaySeconds(mdate, *i);
811  secsForUid[(*i)->relatedTo()][from.daysTo(mdate)] += todaySeconds(mdate, *i);
812  }
813  }
814 
815 
816  // Step 3: For each task, generate the matching row for the CSV file
817  // We use the two hashmaps to have direct access using the task name
818 
819  // First CSV file line
820  // FIXME: localize strings and date formats
821  retval.append("\"Task name\"");
822  for (int i=0; i<intervalLength; ++i)
823  {
824  retval.append(delim);
825  retval.append(from.addDays(i).toString());
826  }
827  retval.append(cr);
828 
829 
830  // Rest of the CSV file
831  QMapIterator<QString, QString> nameUid(uidForName);
832  double time;
833  while (nameUid.hasNext())
834  {
835  nameUid.next();
836  retval.append("\"" + nameUid.key() + "\"");
837  kDebug(5970) << nameUid.key() << ": " << nameUid.value() << endl;
838 
839  for (int day=0; day<intervalLength; day++)
840  {
841  kDebug(5970) << "Secs for day " << day << ":" << secsForUid[nameUid.value()][day];
842  retval.append(delim);
843  time = secsForUid[nameUid.value()][day]/60.0;
844  retval.append(formatTime(time, rc.decimalMinutes));
845  }
846 
847  retval.append(cr);
848  }
849 
850  kDebug() << "Retval is \n" << retval;
851 
852  if (rc.bExPortToClipBoard)
853  taskview->setClipBoardText(retval);
854  else
855  {
856  // store the file locally or remote
857  if ((rc.url.isLocalFile()) || (!rc.url.url().contains("/")))
858  {
859  kDebug(5970) << "storing a local file";
860  QString filename=rc.url.toLocalFile();
861  if (filename.isEmpty()) filename=rc.url.url();
862  QFile f( filename );
863  if( !f.open( QIODevice::WriteOnly ) )
864  {
865  err = i18n( "Could not open \"%1\".", filename );
866  kDebug(5970) << "Could not open file";
867  }
868  kDebug() << "Err is " << err;
869  if (err.length()==0)
870  {
871  QTextStream stream(&f);
872  kDebug(5970) << "Writing to file: " << retval;
873  // Export to file
874  stream << retval;
875  f.close();
876  }
877  }
878  else // use remote file
879  {
880  KTemporaryFile tmpFile;
881  if ( !tmpFile.open() )
882  {
883  err = QString::fromLatin1( "Unable to get temporary file" );
884  }
885  else
886  {
887  QTextStream stream ( &tmpFile );
888  stream << retval;
889  stream.flush();
890  if (!KIO::NetAccess::upload( tmpFile.fileName(), rc.url, 0 )) err=QString::fromLatin1("Could not upload");
891  }
892  }
893  }
894  return err;
895 }
896 
897 void timetrackerstorage::startTimer(const Task* task, const KDateTime &when)
898 {
899  kDebug(5970) << "Entering function; when=" << when;
900  KCalCore::Event::Ptr e;
901  e = baseEvent(task);
902  e->setDtStart(when);
903  d->mCalendar->addEvent(e);
904  task->taskView()->scheduleSave();
905 }
906 
907 void timetrackerstorage::startTimer(QString taskID)
908 {
909  kDebug(5970) << "Entering function";
910  KCalCore::Todo::List todoList;
911  KCalCore::Todo::List::ConstIterator todo;
912  todoList = d->mCalendar->rawTodos();
913  for( todo = todoList.constBegin(); todo != todoList.constEnd(); ++todo )
914  {
915  kDebug(5970) << (*todo)->uid();
916  kDebug(5970) << taskID;
917  if ( (*todo)->uid() == taskID )
918  {
919  kDebug(5970) << "adding event";
920  KCalCore::Event::Ptr e;
921  e = baseEvent((*todo));
922  e->setDtStart(KDateTime::currentLocalDateTime());
923  d->mCalendar->addEvent(e);
924  }
925  }
926  saveCalendar();
927 }
928 
929 void timetrackerstorage::stopTimer(const Task* task, const QDateTime &when)
930 {
931  kDebug(5970) << "Entering function; when=" << when;
932  KCalCore::Event::List eventList = d->mCalendar->rawEvents();
933  for(KCalCore::Event::List::iterator i = eventList.begin();
934  i != eventList.end();
935  ++i)
936  {
937  if ( (*i)->relatedTo() == task->uid() )
938  {
939  kDebug(5970) << "found an event for task, event=" << (*i)->uid();
940  if (!(*i)->hasEndDate())
941  {
942  kDebug(5970) << "this event has no enddate";
943  (*i)->setDtEnd(KDateTime(when, KDateTime::Spec::LocalZone()));
944  }
945  else
946  {
947  kDebug(5970) << "this event has an enddate";
948  kDebug(5970) << "end date is " << (*i)->dtEnd();
949  }
950  };
951  }
952  saveCalendar();
953 }
954 
955 void timetrackerstorage::changeTime(const Task* task, const long deltaSeconds)
956 {
957  kDebug(5970) << "Entering function; deltaSeconds=" << deltaSeconds;
958  KCalCore::Event::Ptr e;
959  QDateTime end;
960  e = baseEvent(task);
961 
962  // Don't use duration, as ICalFormatImpl::writeIncidence never writes a
963  // duration, even though it looks like it's used in event.cpp.
964  end = task->startTime();
965  if ( deltaSeconds > 0 ) end = task->startTime().addSecs(deltaSeconds);
966  e->setDtEnd(KDateTime(end, KDateTime::Spec::LocalZone()));
967 
968  // Use a custom property to keep a record of negative durations
969  e->setCustomProperty( KGlobal::mainComponent().componentName().toUtf8(),
970  QByteArray("duration"),
971  QString::number(deltaSeconds));
972 
973  d->mCalendar->addEvent(e);
974  task->taskView()->scheduleSave();
975 }
976 
977 
978 KCalCore::Event::Ptr timetrackerstorage::baseEvent(const Task *task)
979 {
980  kDebug(5970) << "Entering function";
981  KCalCore::Event::Ptr e( new KCalCore::Event() );
982  QStringList categories;
983  e->setSummary(task->name());
984 
985  // Can't use setRelatedToUid()--no error, but no RelatedTo written to disk
986  e->setRelatedTo( task->uid() );
987 
988  // Debugging: some events where not getting a related-to field written.
989  Q_ASSERT(e->relatedTo() == task->uid());
990 
991  // Have to turn this off to get datetimes in date fields.
992  e->setAllDay(false);
993  e->setDtStart(KDateTime(task->startTime(), KDateTime::Spec::LocalZone()));
994 
995  // So someone can filter this mess out of their calendar display
996  categories.append(i18n("KTimeTracker"));
997  e->setCategories(categories);
998 
999  return e;
1000 }
1001 
1002 KCalCore::Event::Ptr timetrackerstorage::baseEvent(const KCalCore::Todo::Ptr &todo)
1003 {
1004  kDebug(5970) << "Entering function";
1005  KCalCore::Event::Ptr e( new KCalCore::Event() );
1006  QStringList categories;
1007  e->setSummary(todo->summary());
1008 
1009  // Can't use setRelatedToUid()--no error, but no RelatedTo written to disk
1010  e->setRelatedTo( todo->uid() );
1011 
1012  // Have to turn this off to get datetimes in date fields.
1013  e->setAllDay(false);
1014  e->setDtStart(KDateTime(todo->dtStart()));
1015 
1016  // So someone can filter this mess out of their calendar display
1017  categories.append(i18n("KTimeTracker"));
1018  e->setCategories(categories);
1019 
1020  return e;
1021 }
1022 
1023 HistoryEvent::HistoryEvent(const QString &uid, const QString &name,
1024  long duration, const KDateTime &start,
1025  const KDateTime &stop, const QString &todoUid)
1026 {
1027  _uid = uid;
1028  _name = name;
1029  _duration = duration;
1030  _start = start;
1031  _stop = stop;
1032  _todoUid = todoUid;
1033 }
1034 
1035 bool timetrackerstorage::isRemoteFile( const QString &file ) const
1036 {
1037  kDebug(5970) << "Entering function";
1038  QString f = file.toLower();
1039  bool rval = f.startsWith( QLatin1String("http://") ) || f.startsWith( QLatin1String("ftp://") );
1040  kDebug(5970) << "timetrackerstorage::isRemoteFile(" << file <<" ) returns" << rval;
1041  return rval;
1042 }
1043 
1044 QString timetrackerstorage::saveCalendar()
1045 {
1046  kDebug(5970) << "Entering function";
1047  bool removedFromDirWatch = false;
1048  if ( KDirWatch::self()->contains( d->mICalFile ) ) {
1049  KDirWatch::self()->removeFile( d->mICalFile );
1050  removedFromDirWatch = true;
1051  }
1052 
1053  QString errorMessage;
1054  if ( d->mCalendar ) {
1055  d->m_fileLock->lock();
1056  } else {
1057  kDebug() << "mCalendar not set";
1058  return errorMessage;
1059  }
1060 
1061  if ( !d->mCalendar->save() ) {
1062  errorMessage = QString("Could not save. Could lock file.");
1063  }
1064  d->m_fileLock->unlock();
1065 
1066  if ( removedFromDirWatch ) {
1067  KDirWatch::self()->addFile( d->mICalFile );
1068  }
1069 
1070  return errorMessage;
1071 }
1072 
1073 KTTCalendar::Ptr timetrackerstorage::calendar() const
1074 {
1075  return _calendar;
1076 }
ReportCriteria::from
QDate from
For history reports, the lower bound of the date range to report on.
Definition: reportcriteria.h:63
Task::taskView
TaskView * taskView() const
Return task view for this task.
Definition: task.h:74
timetrackerstorage::closeStorage
void closeStorage()
Close calendar and clear view.
Definition: timetrackerstorage.cpp:309
Task::totalSessionTime
long totalSessionTime() const
Definition: task.h:208
ReportCriteria::quote
QString quote
The quote to use for text fields when outputting comma-separated reports.
Definition: reportcriteria.h:99
timetrackerstorage::startTimer
void startTimer(const Task *task, const KDateTime &when=KDateTime::currentLocalDateTime())
Log the event that a timer has started for a task.
Definition: timetrackerstorage.cpp:897
timetrackerstorage::report
QString report(TaskView *taskview, const ReportCriteria &rc)
Output a report based on contents of ReportCriteria.
Definition: timetrackerstorage.cpp:575
HistoryEvent::name
QString name()
Definition: timetrackerstorage.h:353
timetrackerstorage::timetrackerstorage
timetrackerstorage()
Definition: timetrackerstorage.cpp:90
KTimeTracker::KTTCalendar::Ptr
QSharedPointer< KTimeTracker::KTTCalendar > Ptr
Definition: kttcalendar.h:32
timetrackerstorage::deleteAllEvents
QString deleteAllEvents()
Definition: timetrackerstorage.cpp:358
Task::name
QString name() const
returns the name of this task.
Definition: task.cpp:680
timetrackerstorage::isEmpty
bool isEmpty()
Check if the iCalendar file currently loaded has any Todos in it.
Definition: timetrackerstorage.cpp:432
timetrackerstorage::addComment
void addComment(const Task *task, const QString &comment)
Log a new comment for this task.
Definition: timetrackerstorage.cpp:558
TaskView::itemAt
Task * itemAt(int i)
Return the i'th item (zero-based), cast to a Task pointer.
Definition: taskview.cpp:405
ReportCriteria::url
KUrl url
For reports that write to a file, the filename to write to.
Definition: reportcriteria.h:58
TaskView::count
long count()
Return the total number of items in the view.
Definition: taskview.cpp:653
timetrackerstorage::rawtodos
KCalCore::Todo::List rawtodos()
list of all todos
Definition: timetrackerstorage.cpp:326
TaskView::startTimerFor
void startTimerFor(Task *task, const QDateTime &startTime=QDateTime::currentDateTime())
starts timer for task.
Definition: taskview.cpp:721
TaskView::setClipBoardText
QString setClipBoardText(const QString &s)
Set the text of the application's clipboard.
Definition: taskview.cpp:1090
HistoryEvent::HistoryEvent
HistoryEvent()
Needed to be used in a value list.
Definition: timetrackerstorage.h:348
timetrackerstorage::taskidsfromname
QStringList taskidsfromname(QString taskname)
Return a list of all task ids for taskname.
Definition: timetrackerstorage.cpp:470
TaskView::scheduleSave
void scheduleSave()
Schedule that we should save very soon.
Definition: taskview.cpp:693
formatTime
QString formatTime(double minutes, bool decimal)
Format time for output.
Definition: ktimetrackerutility.cpp:37
TaskView::clipTotals
QString clipTotals(const ReportCriteria &rc)
Copy totals for current and all sub tasks to clipboard.
Definition: taskview.cpp:1079
ReportCriteria::delimiter
QString delimiter
The delimiter to use when outputting comma-separated value reports.
Definition: reportcriteria.h:94
TaskView::clearActiveTasks
void clearActiveTasks()
clears all active tasks.
Definition: taskview.cpp:740
taskview.h
todaySeconds
int todaySeconds(const QDate &date, const KCalCore::Event::Ptr &event)
Definition: timetrackerstorage.cpp:713
Task::comment
QString comment() const
Retrieve the entire comment for the task.
Definition: task.cpp:665
ReportCriteria::reportType
REPORTTYPE reportType
The type of report we are running.
Definition: reportcriteria.h:53
TaskView::refresh
void refresh()
Used to refresh (e.g.
Definition: taskview.cpp:526
Task::depth
int depth()
Definition: task.cpp:73
timetrackerstorage::removeTask
bool removeTask(Task *task)
Remove this task from iCalendar file.
Definition: timetrackerstorage.cpp:510
Task::uid
QString uid() const
Return unique iCalendar Todo ID for this task.
Definition: task.cpp:660
HistoryEvent::uid
QString uid()
Definition: timetrackerstorage.h:352
ReportCriteria
Stores entries from export dialog.
Definition: reportcriteria.h:41
ktimetracker.h
Task::isRunning
bool isRunning() const
return the state of a task - if it's running or not
Definition: task.cpp:208
timetrackerstorage::rawevents
KCalCore::Event::List rawevents()
list of all events
Definition: timetrackerstorage.cpp:320
HistoryEvent::start
KDateTime start()
Definition: timetrackerstorage.h:356
Task::sessionTime
long sessionTime() const
Definition: task.h:207
timetrackerstorage::allEventsHaveEndTiMe
bool allEventsHaveEndTiMe()
Deliver if all events of the actual calendar have an endtime.
Definition: timetrackerstorage.cpp:346
timetrackerstorage::changeTime
void changeTime(const Task *task, const long deltaSeconds)
Log the change in a task's time.
Definition: timetrackerstorage.cpp:955
ReportCriteria::bExPortToClipBoard
bool bExPortToClipBoard
True if a clipboard export is wished, not an export to a file.
Definition: reportcriteria.h:89
cr
const QString cr
Definition: timekard.cpp:45
timetrackerstorage::~timetrackerstorage
~timetrackerstorage()
Definition: timetrackerstorage.cpp:94
ReportCriteria::CSVHistoryExport
Definition: reportcriteria.h:48
ReportCriteria::decimalMinutes
bool decimalMinutes
True if the durations should be output in decimal hours.
Definition: reportcriteria.h:74
task.h
timetrackerstorage::setTaskParent
QString setTaskParent(Task *task, Task *parent)
Definition: timetrackerstorage.cpp:394
timekard.h
name
const char * name
Definition: toolicons.h:159
HistoryEvent::stop
KDateTime stop()
Definition: timetrackerstorage.h:357
timetrackerstorage::stopTimer
void stopTimer(const Task *task, const QDateTime &when=QDateTime::currentDateTime())
Log the event that the timer has stopped for this task.
Definition: timetrackerstorage.cpp:929
timetrackerstorage::calendar
KTimeTracker::KTTCalendar::Ptr calendar() const
Definition: timetrackerstorage.cpp:1073
timetrackerstorage::task
Task * task(const QString &uid, TaskView *view)
Find the task with the given uid.
Definition: timetrackerstorage.cpp:208
timetrackerstorage::removeEvent
QString removeEvent(QString uid)
Definition: timetrackerstorage.cpp:494
Task::parent
Task * parent() const
return parent Task or null in case of TaskView.
Definition: task.h:71
HistoryEvent::todoUid
QString todoUid()
Definition: timetrackerstorage.h:358
Task::move
void move(Task *destination)
cut Task out of parent Task or the TaskView and into the destination Task
Definition: task.cpp:623
ktimetrackerutility.h
timetrackerstorage::save
QString save(TaskView *taskview)
Save all tasks and their totals to an iCalendar file.
Definition: timetrackerstorage.cpp:367
HistoryEvent::duration
long duration()
In seconds.
Definition: timetrackerstorage.h:355
ReportCriteria::to
QDate to
For history reports, the upper bound of the date range to report on.
Definition: reportcriteria.h:68
timetrackerstorage::addTask
QString addTask(const Task *task, const Task *parent=0)
Add this task from iCalendar file.
Definition: timetrackerstorage.cpp:442
Task::setPixmapProgress
void setPixmapProgress()
Sets an appropriate icon for this task based on its level of completion.
Definition: task.cpp:284
TaskView
Container and interface for the tasks.
Definition: taskview.h:50
timetrackerstorage::icalfile
QString icalfile()
Return the name of the iCal file.
Definition: timetrackerstorage.cpp:226
Task
A class representing a task.
Definition: task.h:54
timetrackerstorage::buildTaskView
QString buildTaskView(const KTimeTracker::KTTCalendar::Ptr &calendar, TaskView *view)
Build up the taskview.
Definition: timetrackerstorage.cpp:232
Task::asTodo
KCalCore::Todo::Ptr asTodo(const KCalCore::Todo::Ptr &calendar) const
Load the todo passed in with this tasks info.
Definition: task.cpp:480
timetrackerstorage::taskNames
QStringList taskNames() const
Return a list of all task names.
Definition: timetrackerstorage.cpp:484
timetrackerstorage.h
Task::startTime
QDateTime startTime() const
Return time the task was started.
Definition: task.cpp:690
timetrackerstorage::load
QString load(TaskView *taskview, const QString &fileName)
Load the list view with tasks read from iCalendar file.
Definition: timetrackerstorage.cpp:99
Task::time
long time() const
Definition: task.h:204
Task::totalTime
long totalTime() const
Definition: task.h:206
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:55:10 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

ktimetracker

Skip menu "ktimetracker"
  • Main Page
  • Namespace List
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal