summaryrefslogtreecommitdiffstats
path: root/karm/timekard.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch)
tree67208f7c145782a7e90b123b982ca78d88cc2c87 /karm/timekard.cpp
downloadtdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz
tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'karm/timekard.cpp')
-rw-r--r--karm/timekard.cpp382
1 files changed, 382 insertions, 0 deletions
diff --git a/karm/timekard.cpp b/karm/timekard.cpp
new file mode 100644
index 00000000..b7ff21f0
--- /dev/null
+++ b/karm/timekard.cpp
@@ -0,0 +1,382 @@
+/*
+ * 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.
+ *
+ */
+
+// #include <iostream>
+
+#include <qdatetime.h>
+#include <qpaintdevicemetrics.h>
+#include <qpainter.h>
+#include <qmap.h>
+
+#include <kglobal.h>
+#include <kdebug.h>
+#include <klocale.h> // i18n
+#include <event.h>
+
+#include "karmutility.h" // formatTime()
+#include "timekard.h"
+#include "task.h"
+#include "taskview.h"
+#include <assert.h>
+
+const int taskWidth = 40;
+const int timeWidth = 6;
+const int totalTimeWidth = 7;
+const int reportWidth = taskWidth + timeWidth;
+
+const QString cr = QString::fromLatin1("\n");
+
+QString TimeKard::totalsAsText(TaskView* taskview, bool justThisTask, WhichTime which)
+// Print the total Times as text. If justThisTask, use activeTask, else, all tasks
+{
+ kdDebug(5970) << "Entering TimeKard::totalsAsText" << endl;
+ QString retval;
+ QString line;
+ QString buf;
+ long sum;
+
+ line.fill('-', reportWidth);
+ line += cr;
+
+ // header
+ retval += i18n("Task Totals") + cr;
+ retval += KGlobal::locale()->formatDateTime(QDateTime::currentDateTime());
+ retval += cr + cr;
+ retval += QString(QString::fromLatin1("%1 %2"))
+ .arg(i18n("Time"), timeWidth)
+ .arg(i18n("Task"));
+ retval += cr;
+ retval += line;
+
+ // tasks
+ if (taskview->current_item())
+ {
+ if (justThisTask)
+ {
+ // a task's total time includes the sum of all subtask times
+ sum = which == TotalTime ? taskview->current_item()->totalTime() : taskview->current_item()->sessionTime();
+ printTask(taskview->current_item(), retval, 0, which);
+ }
+ else
+ {
+ sum = 0;
+ for (Task* task= taskview->item_at_index(0); task;
+ task= task->nextSibling())
+ {
+ kdDebug(5970) << "Copying task " << task->name() << endl;
+ int time = which == TotalTime ? task->totalTime() : task->totalSessionTime();
+ sum += time;
+ if ( time || task->firstChild() )
+ printTask(task, retval, 0, which);
+ }
+ }
+
+ // total
+ buf.fill('-', reportWidth);
+ retval += QString(QString::fromLatin1("%1")).arg(buf, timeWidth) + cr;
+ retval += QString(QString::fromLatin1("%1 %2"))
+ .arg(formatTime(sum),timeWidth)
+ .arg(i18n("Total"));
+ }
+ else
+ retval += i18n("No tasks.");
+
+ return retval;
+}
+
+// Print out "<indent for level> <task total> <task>", for task and subtasks. Used by totalsAsText.
+void TimeKard::printTask(Task *task, QString &s, int level, WhichTime which)
+{
+ QString buf;
+
+ s += buf.fill(' ', level);
+ s += QString(QString::fromLatin1("%1 %2"))
+ .arg(formatTime(which == TotalTime?task->totalTime():task->totalSessionTime()), timeWidth)
+ .arg(task->name());
+ s += cr;
+
+ for (Task* subTask = task->firstChild();
+ subTask;
+ subTask = subTask->nextSibling())
+ {
+ int time = which == TotalTime ? subTask->totalTime() : subTask->totalSessionTime();
+ if (time)
+ printTask(subTask, s, level+1, which);
+ }
+}
+
+void TimeKard::printTaskHistory(const Task *task,
+ const QMap<QString,long>& taskdaytotals,
+ QMap<QString,long>& daytotals,
+ const QDate& from,
+ const QDate& to,
+ const int level, QString& s, bool totalsOnly)
+{
+ long sectionsum = 0;
+ for ( QDate day = from; day <= to; day = day.addDays(1) )
+ {
+ QString daykey = day.toString(QString::fromLatin1("yyyyMMdd"));
+ QString daytaskkey = QString::fromLatin1("%1_%2")
+ .arg(daykey)
+ .arg(task->uid());
+
+ if (taskdaytotals.contains(daytaskkey))
+ {
+ if ( !totalsOnly )
+ {
+ s += QString::fromLatin1("%1")
+ .arg(formatTime(taskdaytotals[daytaskkey]/60), timeWidth);
+ }
+ sectionsum += taskdaytotals[daytaskkey]; // in seconds
+
+ if (daytotals.contains(daykey))
+ daytotals.replace(daykey, daytotals[daykey] + taskdaytotals[daytaskkey]);
+ else
+ daytotals.insert(daykey, taskdaytotals[daytaskkey]);
+ }
+ else if ( !totalsOnly )
+ {
+ QString buf;
+ buf.fill(' ', timeWidth);
+ s += buf;
+ }
+ }
+
+ // Total for task this section (e.g. week)
+ s += QString::fromLatin1("%1").arg(formatTime(sectionsum/60), totalTimeWidth);
+
+ // Task name
+ QString buf;
+ s += buf.fill(' ', level + 1);
+ s += QString::fromLatin1("%1").arg(task->name());
+ s += cr;
+
+ for (Task* subTask = task->firstChild();
+ subTask;
+ subTask = subTask->nextSibling())
+ {
+ // recursive
+ printTaskHistory(subTask, taskdaytotals, daytotals, from, to, level+1, s, totalsOnly);
+ }
+}
+
+QString TimeKard::sectionHistoryAsText(
+ TaskView* taskview,
+ const QDate& sectionFrom, const QDate& sectionTo,
+ const QDate& from, const QDate& to,
+ const QString& name,
+ bool justThisTask, bool totalsOnly)
+{
+
+ const int sectionReportWidth = taskWidth + ( totalsOnly ? 0 : sectionFrom.daysTo(sectionTo) * timeWidth ) + totalTimeWidth;
+ assert( sectionReportWidth > 0 );
+ QString line;
+ line.fill('-', sectionReportWidth);
+ line += cr;
+
+ QValueList<HistoryEvent> events;
+ if ( sectionFrom < from && sectionTo > to)
+ {
+ events = taskview->getHistory(from, to);
+ }
+ else if ( sectionFrom < from )
+ {
+ events = taskview->getHistory(from, sectionTo);
+ }
+ else if ( sectionTo > to)
+ {
+ events = taskview->getHistory(sectionFrom, to);
+ }
+ else
+ {
+ events = taskview->getHistory(sectionFrom, sectionTo);
+ }
+
+ QMap<QString, long> taskdaytotals;
+ QMap<QString, long> daytotals;
+
+ // Build lookup dictionary used to output data in table cells. keys are
+ // in this format: YYYYMMDD_NNNNNN, where Y = year, M = month, d = day and
+ // NNNNN = the VTODO uid. The value is the total seconds logged against
+ // that task on that day. Note the UID is the todo id, not the event id,
+ // so times are accumulated for each task.
+ for (QValueList<HistoryEvent>::iterator event = events.begin(); event != events.end(); ++event)
+ {
+ QString daykey = (*event).start().date().toString(QString::fromLatin1("yyyyMMdd"));
+ QString daytaskkey = QString::fromLatin1("%1_%2")
+ .arg(daykey)
+ .arg((*event).todoUid());
+
+ if (taskdaytotals.contains(daytaskkey))
+ taskdaytotals.replace(daytaskkey,
+ taskdaytotals[daytaskkey] + (*event).duration());
+ else
+ taskdaytotals.insert(daytaskkey, (*event).duration());
+ }
+
+ QString retval;
+ // section name (e.g. week name)
+ retval += cr + cr;
+ QString buf;
+ if ( name.length() < (unsigned int)sectionReportWidth )
+ buf.fill(' ', int((sectionReportWidth - name.length()) / 2));
+ retval += buf + name + cr;
+
+ if ( !totalsOnly )
+ {
+ // day headings
+ for (QDate day = sectionFrom; day <= sectionTo; day = day.addDays(1))
+ {
+ retval += QString::fromLatin1("%1").arg(day.day(), timeWidth);
+ }
+ retval += cr;
+ retval += line;
+ }
+
+ // the tasks
+ if (events.empty())
+ {
+ retval += " ";
+ retval += i18n("No hours logged.");
+ }
+ else
+ {
+ if (justThisTask)
+ {
+ printTaskHistory(taskview->current_item(), taskdaytotals, daytotals,
+ sectionFrom, sectionTo, 0, retval, totalsOnly);
+ }
+ else
+ {
+ for (Task* task= taskview->current_item(); task;
+ task= task->nextSibling())
+ {
+ printTaskHistory(task, taskdaytotals, daytotals,
+ sectionFrom, sectionTo, 0, retval, totalsOnly);
+ }
+ }
+ retval += line;
+
+ // per-day totals at the bottom of the section
+ long sum = 0;
+ for (QDate day = sectionFrom; day <= sectionTo; day = day.addDays(1))
+ {
+ QString daykey = day.toString(QString::fromLatin1("yyyyMMdd"));
+
+ if (daytotals.contains(daykey))
+ {
+ if ( !totalsOnly )
+ {
+ retval += QString::fromLatin1("%1")
+ .arg(formatTime(daytotals[daykey]/60), timeWidth);
+ }
+ sum += daytotals[daykey]; // in seconds
+ }
+ else if ( !totalsOnly )
+ {
+ buf.fill(' ', timeWidth);
+ retval += buf;
+ }
+ }
+
+ retval += QString::fromLatin1("%1 %2")
+ .arg(formatTime(sum/60), totalTimeWidth)
+ .arg(i18n("Total"));
+ }
+ return retval;
+}
+
+QString TimeKard::historyAsText(TaskView* taskview, const QDate& from,
+ const QDate& to, bool justThisTask, bool perWeek, bool totalsOnly)
+{
+ // header
+ QString retval;
+ retval += totalsOnly ? i18n("Task Totals") : i18n("Task History");
+ retval += cr;
+ retval += i18n("From %1 to %2")
+ .arg(KGlobal::locale()->formatDate(from))
+ .arg(KGlobal::locale()->formatDate(to));
+ retval += cr;
+ retval += i18n("Printed on: %1")
+ .arg(KGlobal::locale()->formatDateTime(QDateTime::currentDateTime()));
+
+ if ( perWeek )
+ {
+ // output one time card table for each week in the date range
+ QValueList<Week> weeks = Week::weeksFromDateRange(from, to);
+ for (QValueList<Week>::iterator week = weeks.begin(); week != weeks.end(); ++week)
+ {
+ retval += sectionHistoryAsText( taskview, (*week).start(), (*week).end(), from, to, (*week).name(), justThisTask, totalsOnly );
+ }
+ } else
+ {
+ retval += sectionHistoryAsText( taskview, from, to, from, to, "", justThisTask, totalsOnly );
+ }
+ return retval;
+}
+
+Week::Week() {}
+
+Week::Week(QDate from)
+{
+ _start = from;
+}
+
+QDate Week::start() const
+{
+ return _start;
+}
+
+QDate Week::end() const
+{
+ return _start.addDays(6);
+}
+
+QString Week::name() const
+{
+ return i18n("Week of %1").arg(KGlobal::locale()->formatDate(start()));
+}
+
+QValueList<Week> Week::weeksFromDateRange(const QDate& from, const QDate& to)
+{
+ QDate start;
+ QValueList<Week> weeks;
+
+ // The QDate weekNumber() method always puts monday as the first day of the
+ // week.
+ //
+ // Not that it matters here, but week 1 always includes the first Thursday
+ // of the year. For example, January 1, 2000 was a Saturday, so
+ // QDate(2000,1,1).weekNumber() returns 52.
+
+ // Since report always shows a full week, we generate a full week of dates,
+ // even if from and to are the same date. The week starts on the day
+ // that is set in the locale settings.
+ start = from.addDays(
+ -((7 - KGlobal::locale()->weekStartDay() + from.dayOfWeek()) % 7));
+
+ for (QDate d = start; d <= to; d = d.addDays(7))
+ weeks.append(Week(d));
+
+ return weeks;
+}
+