1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
|
/*
* This file only:
* Copyright (C) 2003 Mark Bucciarelli <mark@hubcapconsutling.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA.
*
*/
#ifndef KARM_STORAGE_H
#define KARM_STORAGE_H
#include <qdict.h>
#include <qptrstack.h>
#include "journal.h"
#include "reportcriteria.h"
#include "desktoplist.h"
#include <calendarresources.h>
#include <vector>
#include "resourcecalendar.h"
#include <kdepimmacros.h>
class QDateTime;
class Preferences;
class Task;
class TaskView;
class HistoryEvent;
class KCal::Todo;
/**
* Singleton to store/retrieve KArm data to/from persistent storage.
*
* The storage is an iCalendar file. Also included are methods to
* import KArm data from the two legacy file formats.
*
* All logic that deals with getting and saving data should go here. The
* storage logic has changed at least twice already in KArm's history, and
* chances are good it will change again (for example, allowing KOrganizer and
* KArm to access the same iCalendar file simultaneously).
*
* Prior to KDE 3.2, KArm just stored totals for each task--a session total
* and a task total. The session total was reset to zero each time KArm
* started up or after the user reset the session times to zero. With the
* release of KDE 3.2, KArm now stores these task totals as well as logging
* the history of each start/stop event; that is, every time you start a timer
* and then stop a timer on a task, KArm records this as an iCalendar event.
*
* @short Logic that gets and stores KArm data to disk.
* @author Mark Bucciarelli <mark@hubcapconsulting.com>
*/
class KarmStorage
{
public:
/*
* Return reference to storage singleton.
*
* The constructors are private, so this must be used to create a
* KarmStorage instance.
*/
static KarmStorage *instance();
/*
* Load the list view with tasks read from iCalendar file.
*
* Parses iCalendar file, builds list view items in the proper
* hierarchy, and loads them into the list view widget.
*
* If the file name passed in is the same as the last file name that was
* loaded, this method does nothing.
*
* This method considers any of the following conditions errors:
*
* @li the iCalendar file does not exist
* @li the iCalendar file is not readable
* @li the list group currently has list items
* @li an iCalendar todo has no related to attribute
* @li a todo is related to another todo which does not exist
*
* @param taskview The list group used in the TaskView
* @param preferences The current KArm preferences.
* @param fileName Override preferences' filename
*
* @return empty string if success, error message if error.
*
*/
QString load(TaskView* taskview, const Preferences* preferences, QString fileName="" );
/*
* Return the name of the iCal file
*/
QString icalfile();
/*
* Build up the taskview.
*
* This is needed if the iCal file has been modified
*/
QString buildTaskView(KCal::ResourceCalendar *rc, TaskView *view);
/* Close calendar and clear view. Release lock if holding one. */
void closeStorage(TaskView* view);
/*
* Save all tasks and their totals to an iCalendar file.
*
* All tasks must have an associated VTODO object already created in the
* calendar file; that is, the task->uid() must refer to a valid VTODO in
* the calender.
* Delivers empty string if successful, else error msg.
*
* @param taskview The list group used in the TaskView
*/
QString save(TaskView* taskview);
/**
* Read tasks and their total times from a text file (legacy storage).
*
* This reads from one of the two legacy file formats. In this version,
* the parent task times do not include the sum of all their children's
* times.
*
* The format of the file is zero or more lines of:
* 1 task id (a number)
* time in minutes
* string task name
* [string] desktops, in which to count. e.g. "1,2,5" (optional)
*/
QString loadFromFlatFile(TaskView* taskview, const QString& filename);
/**
* Reads tasks and their total times from text file (legacy).
*
* This is the older legacy format, where the task totals included the
* children totals.
*
* @see loadFromFlatFile
*/
QString loadFromFlatFileCumulative(TaskView* taskview,
const QString& filename);
/**
Output a report based on contents of ReportCriteria.
*/
QString report( TaskView *taskview, const ReportCriteria &rc );
/**
* Log the change in a task's time.
*
* We create an iCalendar event to store each change. The event start
* date is set to the current datetime. If time is added to the task, the
* task end date is set to start time + delta. If the time is negative,
* the end date is set to the start time.
*
* In both cases (postive or negative delta), we create a custom iCalendar
* property that stores the delta (in seconds). This property is called
* X-KDE-karm-duration.
*
* Note that the KArm UI allows the user to change both the session and
* the total task time, and this routine does not account for all posibile
* cases. For example, it is possible for the user to do something crazy
* like add 10 minutes to the session time and subtract 50 minutes from
* the total time. Although this change violates a basic law of physics,
* it is allowed.
*
* For now, you should pass in the change to the total task time.
* Eventually, the UI should be changed.
*
* @param task The task the change is for.
* @param delta Change in task time, in seconds. Can be negative.
*/
void changeTime(const Task* task, const long deltaSeconds);
/**
* Book time to a task.
*
* Creates an iCalendar event and adds it to the calendar. Does not write
* calender to disk, just adds event to calendar in memory. However, the
* resource framework does try to get a lock on the file. After a
* succesful lock, the calendar marks this incidence as modified and then
* releases the lock.
*
* @param task Task
* @param startDateTime Date and time the booking starts.
* @param durationInSeconds Duration of time to book, in seconds.
*
* @return true if event was added, false if not (if, for example, the
* attempted file lock failed).
*/
bool bookTime(const Task* task, const QDateTime& startDateTime,
long durationInSeconds);
/**
* Log a change to a task name.
*
* For iCalendar storage, there is no need to log an Event for this event,
* since unique id's are used to link Events to Todos. No matter how many
* times you change a task's name, the uid stays the same.
*
* @param task The task
* @param oldname The old name of the task. The new name is in the task
* object already.
*/
void setName(const Task* task, const QString& oldname) { Q_UNUSED(task); Q_UNUSED(oldname); }
/**
* Log the event that a timer has started for a task.
*
* For the iCalendar storage, there is no need to log anything for this
* event. We log an event when the timer is stopped.
*
* @param task The task the timer was started for.
*/
void startTimer(const Task* task) { Q_UNUSED(task); }
/**
* Log the event that the timer has stopped for this task.
*
* The task stores the last time a timer was started, so we log a new iCal
* Event with the start and end times for this task.
* @see KarmStorage::changeTime
*
* @param task The task the timer was stopped for.
*/
void stopTimer(const Task* task, QDateTime when=QDateTime::currentDateTime());
/**
* Log a new comment for this task.
*
* iCal allows multiple comment tags. So we just add a new comment to the
* todo for this task and write the calendar.
*
* @param task The task that gets the comment
* @param comment The comment
*/
void addComment(const Task* task, const QString& comment);
/**
* Remove this task from iCalendar file.
*
* Removes task as well as all event history for this task.
*
* @param task The task to be removed.
* @return true if change was saved, false otherwise
*/
bool removeTask(Task* task);
/**
* Add this task from iCalendar file.
*
* Create a new KCal::Todo object and load with task information. If
* parent is not zero, then set the RELATED-TO attribute for this Todo.
*
* @param task The task to be removed.
* @param parent The parent of this task. Must have a uid() that is in
* the existing calendar. If zero, this task is considered a root task.
* @return The unique ID for the new VTODO. Return an null QString if
* there was an error creating the new calendar object.
*/
QString addTask(const Task* task, const Task* parent);
/**
* Check if the iCalendar file currently loaded has any Todos in it.
*
* @return true if iCalendar file has any todos
*/
bool isEmpty();
/**
* Check if iCalendar file name in the preferences has changed since the
* last call to load. If there is no calendar file currently loaded,
* return false.
*
* @param preferences Set of KArm preferences.
*
* @return true if a previous file has been loaded and the iCalendar file
* specified in the preferences is different.
*/
bool isNewStorage(const Preferences* preferences) const;
/** Return a list of start/stop events for the given date range. */
QValueList<HistoryEvent> getHistory(const QDate& from, const QDate& to);
private:
static KarmStorage *_instance;
KCal::ResourceCalendar *_calendar;
QString _icalfile;
KarmStorage();
void adjustFromLegacyFileFormat(Task* task);
bool parseLine(QString line, long *time, QString *name, int *level,
DesktopList* desktopList);
QString writeTaskAsTodo
(Task* task, const int level, QPtrStack< KCal::Todo >& parents);
bool saveCalendar();
KCal::Event* baseEvent(const Task*);
bool remoteResource( const QString& file ) const;
/**
* Writes all tasks and their totals to a Comma-Separated Values file.
*
* The format of this file is zero or more lines of:
* taskName,subtaskName,..,sessionTime,time,totalSessionTime,totalTime
* the number of subtasks is determined at runtime.
*/
QString exportcsvFile( TaskView *taskview, const ReportCriteria &rc );
/**
* Write task history to file as comma-delimited data.
*/
QString exportcsvHistory (
TaskView* taskview,
const QDate& from,
const QDate& to,
const ReportCriteria &rc
);
long printTaskHistory (
const Task *task,
const QMap<QString,long>& taskdaytotals,
QMap<QString,long>& daytotals,
const QDate& from,
const QDate& to,
const int level,
std::vector <QString> &matrix,
const ReportCriteria &rc
);
};
/**
* One start/stop event that has been logged.
*
* When a task is running and the user stops it, KArm logs this event and
* saves it in the history. This class represents such an event read from
* storage, and abstracts it from the specific storage used.
*/
class HistoryEvent
{
public:
/** Needed to be used in a value list. */
HistoryEvent() {}
HistoryEvent(QString uid, QString name, long duration,
QDateTime start, QDateTime stop, QString todoUid);
QString uid() {return _uid; }
QString name() {return _name; }
/** In seconds. */
long duration() {return _duration; }
QDateTime start() {return _start; }
QDateTime stop() { return _stop; }
QString todoUid() {return _todoUid; }
private:
QString _uid;
QString _todoUid;
QString _name;
long _duration;
QDateTime _start;
QDateTime _stop;
};
#endif // KARM_STORAGE_H
|