/* * This file only: * Copyright (C) 2003 Mark Bucciarelli * * 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 #include #include #include #include #include #include #include // i18n #include #include "karmutility.h" // formatTime() #include "timekard.h" #include "task.h" #include "taskview.h" #include const int taskWidth = 40; const int timeWidth = 6; const int totalTimeWidth = 7; const int reportWidth = taskWidth + timeWidth; const TQString cr = TQString::fromLatin1("\n"); TQString 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; TQString retval; TQString line; TQString buf; long sum; line.fill('-', reportWidth); line += cr; // header retval += i18n("Task Totals") + cr; retval += TDEGlobal::locale()->formatDateTime(TQDateTime::currentDateTime()); retval += cr + cr; retval += TQString(TQString::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 += TQString(TQString::fromLatin1("%1")).arg(buf, timeWidth) + cr; retval += TQString(TQString::fromLatin1("%1 %2")) .arg(formatTime(sum),timeWidth) .arg(i18n("Total")); } else retval += i18n("No tasks."); return retval; } // Print out " ", for task and subtasks. Used by totalsAsText. void TimeKard::printTask(Task *task, TQString &s, int level, WhichTime which) { TQString buf; s += buf.fill(' ', level); s += TQString(TQString::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 TQMap& taskdaytotals, TQMap& daytotals, const TQDate& from, const TQDate& to, const int level, TQString& s, bool totalsOnly) { long sectionsum = 0; for ( TQDate day = from; day <= to; day = day.addDays(1) ) { TQString daykey = day.toString(TQString::fromLatin1("yyyyMMdd")); TQString daytaskkey = TQString::fromLatin1("%1_%2") .arg(daykey) .arg(task->uid()); if (taskdaytotals.contains(daytaskkey)) { if ( !totalsOnly ) { s += TQString::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 ) { TQString buf; buf.fill(' ', timeWidth); s += buf; } } // Total for task this section (e.g. week) s += TQString::fromLatin1("%1").arg(formatTime(sectionsum/60), totalTimeWidth); // Task name TQString buf; s += buf.fill(' ', level + 1); s += TQString::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); } } TQString TimeKard::sectionHistoryAsText( TaskView* taskview, const TQDate& sectionFrom, const TQDate& sectionTo, const TQDate& from, const TQDate& to, const TQString& name, bool justThisTask, bool totalsOnly) { const int sectionReportWidth = taskWidth + ( totalsOnly ? 0 : sectionFrom.daysTo(sectionTo) * timeWidth ) + totalTimeWidth; assert( sectionReportWidth > 0 ); TQString line; line.fill('-', sectionReportWidth); line += cr; TQValueList 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); } TQMap taskdaytotals; TQMap 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 (TQValueList::iterator event = events.begin(); event != events.end(); ++event) { TQString daykey = (*event).start().date().toString(TQString::fromLatin1("yyyyMMdd")); TQString daytaskkey = TQString::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()); } TQString retval; // section name (e.g. week name) retval += cr + cr; TQString buf; if ( name.length() < (unsigned int)sectionReportWidth ) buf.fill(' ', int((sectionReportWidth - name.length()) / 2)); retval += buf + name + cr; if ( !totalsOnly ) { // day headings for (TQDate day = sectionFrom; day <= sectionTo; day = day.addDays(1)) { retval += TQString::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 (TQDate day = sectionFrom; day <= sectionTo; day = day.addDays(1)) { TQString daykey = day.toString(TQString::fromLatin1("yyyyMMdd")); if (daytotals.contains(daykey)) { if ( !totalsOnly ) { retval += TQString::fromLatin1("%1") .arg(formatTime(daytotals[daykey]/60), timeWidth); } sum += daytotals[daykey]; // in seconds } else if ( !totalsOnly ) { buf.fill(' ', timeWidth); retval += buf; } } retval += TQString::fromLatin1("%1 %2") .arg(formatTime(sum/60), totalTimeWidth) .arg(i18n("Total")); } return retval; } TQString TimeKard::historyAsText(TaskView* taskview, const TQDate& from, const TQDate& to, bool justThisTask, bool perWeek, bool totalsOnly) { // header TQString retval; retval += totalsOnly ? i18n("Task Totals") : i18n("Task History"); retval += cr; retval += i18n("From %1 to %2") .arg(TDEGlobal::locale()->formatDate(from)) .arg(TDEGlobal::locale()->formatDate(to)); retval += cr; retval += i18n("Printed on: %1") .arg(TDEGlobal::locale()->formatDateTime(TQDateTime::currentDateTime())); if ( perWeek ) { // output one time card table for each week in the date range TQValueList weeks = Week::weeksFromDateRange(from, to); for (TQValueList::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(TQDate from) { _start = from; } TQDate Week::start() const { return _start; } TQDate Week::end() const { return _start.addDays(6); } TQString Week::name() const { return i18n("Week of %1").arg(TDEGlobal::locale()->formatDate(start())); } TQValueList Week::weeksFromDateRange(const TQDate& from, const TQDate& to) { TQDate start; TQValueList weeks; // The TQDate 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 // TQDate(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 - TDEGlobal::locale()->weekStartDay() + from.dayOfWeek()) % 7)); for (TQDate d = start; d <= to; d = d.addDays(7)) weeks.append(Week(d)); return weeks; }