/*
* Project.cpp - TaskJuggler
*
- * Copyright (c) 2001 by Chris Schlaeger <cs@suse.de>
+ * Copyright (c) 2001, 2002 by Chris Schlaeger <cs@suse.de>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms version 2 of the GNU General Public License as
+ * it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* $Id$
*/
+
#include <config.h>
#include <stdio.h>
-
+#include <stdlib.h>
+#include <qdom.h>
#include <qdict.h>
+#include "debug.h"
#include "Project.h"
#include "Utility.h"
+#include "kotrus.h"
-#define COL_DEFAULT "#fffadd"
-#define COL_WEEKEND "#ffec80"
-#define COL_BOOKED "#ffc0a3"
-#define COL_HEADER "#a5c2ff"
-#define COL_MILESTONE "#ff2a2a"
+DebugController DebugCtrl;
Project::Project()
{
taskList.setAutoDelete(TRUE);
- priority = 50;
+ resourceList.setAutoDelete(TRUE);
+ accountList.setAutoDelete(TRUE);
+
+ vacationList.setAutoDelete(TRUE);
+
+ htmlTaskReports.setAutoDelete(TRUE);
+ htmlResourceReports.setAutoDelete(TRUE);
+ htmlAccountReports.setAutoDelete(TRUE);
+ htmlWeeklyCalendars.setAutoDelete(TRUE);
+ exportReports.setAutoDelete(TRUE);
+
+ scenarioNames.append("Plan");
+ scenarioNames.append("Actual");
+
+ xmlreport = 0;
+#ifdef HAVE_ICAL
+#ifdef HAVE_KDE
+ icalReport = 0;
+#endif
+#endif
+
+ priority = 500;
+ /* The following settings are country and culture dependent. Those
+ * defaults are probably true for many Western countries, but have to be
+ * changed in project files. */
+ dailyWorkingHours = 8.0;
+ yearlyWorkingDays = 252;
+ scheduleGranularity = ONEHOUR;
+ weekStartsMonday = TRUE;
+ timeFormat = "%Y-%m-%d %H:%M";
+ shortTimeFormat = "%H:%M";
+
start = 0;
end = 0;
+ now = time(0);
+
minEffort = 0.0;
maxEffort = 1.0;
rate = 0.0;
- /* The 'closed' flag may be used for container classes to hide all
- * sub tasks. */
- addAllowedFlag("closed");
- // The 'hidden' flag may be used to hide the task in all reports.
- addAllowedFlag("hidden");
- htmlTaskReport = "";
- htmlTaskReportStart = 0;
- htmlTaskReportEnd = 0;
- htmlResourceReport = "";
- htmlResourceReportStart = 0;
- htmlResourceReportEnd = 0;
+ currencyDigits = 3;
+ kotrus = 0;
+
+ /* Initialize working hours with default values that match the Monday -
+ * Friday 9 - 6 (with 1 hour lunch break) pattern used by many western
+ * countries. */
+ // Sunday
+ workingHours[0] = new QPtrList<Interval>();
+ workingHours[0]->setAutoDelete(TRUE);
+
+ for (int i = 1; i < 6; ++i)
+ {
+ workingHours[i] = new QPtrList<Interval>();
+ workingHours[i]->setAutoDelete(TRUE);
+ workingHours[i]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
+ workingHours[i]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
+ }
+
+ // Saturday
+ workingHours[6] = new QPtrList<Interval>();
+ workingHours[6]->setAutoDelete(TRUE);
+}
+
+Project::~Project()
+{
+ delete xmlreport;
+#ifdef HAVE_ICAL
+#ifdef HAVE_KDE
+ delete icalReport;
+#endif
+#endif
+}
+
+const QString&
+Project::getScenarioName(int sc)
+{
+ return scenarioNames[sc];
}
bool
-Project::addTask(Task* t)
+Project::addId(const QString& id)
{
- taskList.append(t);
+ if (projectIDs.findIndex(id) != -1)
+ return FALSE;
+ else
+ projectIDs.append(id);
return TRUE;
}
+QString
+Project::getIdIndex(const QString& i) const
+{
+ int idx;
+ if ((idx = projectIDs.findIndex(i)) == -1)
+ return QString("?");
+ QString idxStr;
+ do
+ {
+ idxStr = QChar('A' + idx % ('Z' - 'A')) + idxStr;
+ idx /= 'Z' - 'A';
+ } while (idx > 'Z' - 'A');
+
+ return idxStr;
+}
+
+int
+Project::calcWorkingDays(const Interval& iv)
+{
+ int workingDays = 0;
+
+ for (time_t s = midnight(iv.getStart()); s <= iv.getEnd();
+ s = sameTimeNextDay(s))
+ if (isWorkingDay(s))
+ workingDays++;
+
+ return workingDays;
+}
+
bool
Project::pass2()
{
QDict<Task> idHash;
bool error = FALSE;
+ // Generate sequence numbers for all lists.
+ taskList.createIndex(TRUE);
+ resourceList.createIndex(TRUE);
+ accountList.createIndex(TRUE);
+ shiftList.createIndex(TRUE);
+
+ // Initialize random generator.
+ srand((int) start);
+
// Create hash to map task IDs to pointers.
for (Task* t = taskList.first(); t != 0; t = taskList.next())
{
idHash.insert(t->getId(), t);
}
-
// Create cross links from dependency lists.
for (Task* t = taskList.first(); t != 0; t = taskList.next())
{
if (!t->xRef(idHash))
error = TRUE;
}
+ // Set dates according to implicit dependencies
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
+ t->implicitXRef();
- TaskList sortedTasks(taskList);
- sortedTasks.setSorting(TaskList::PrioDown);
- sortedTasks.sort();
+ // Find out what scenarios need to be scheduled.
+ // TODO: No multiple scenario support yet.
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
+ if (!hasExtraValues && t->hasExtraValues(Task::Actual))
+ hasExtraValues = TRUE;
+
+ /* Now we can copy the missing values from the plan scenario to the other
+ * scenarios. */
+ for (int sc = 1; sc < getMaxScenarios(); sc++)
+ overlayScenario(sc);
+
+ // Now check that all tasks have sufficient data to be scheduled.
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
+ if (!t->preScheduleOk())
+ error = TRUE;
+
+ // Check all tasks for dependency loops.
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
+ if (t->loopDetector())
+ return FALSE;
+
+ return !error;
+}
+
+bool
+Project::scheduleAllScenarios()
+{
+ bool error = FALSE;
- const time_t scheduleGranularity = ONEHOUR;
- for (int day = start; day < end; day += scheduleGranularity)
+ if (DEBUGPS(1))
+ qWarning("Scheduling plan scenario...");
+ prepareScenario(Task::Plan);
+ if (!schedule("Plan"))
{
- bool done;
- do
- {
- done = TRUE;
- for (Task* t = sortedTasks.first(); t != 0; t = sortedTasks.next())
- if (!t->schedule(day, scheduleGranularity))
- done = FALSE;
- } while (!done);
+ if (DEBUGPS(2))
+ qWarning("Scheduling errors in plan scenario.");
+ error = TRUE;
}
+ finishScenario(Task::Plan);
- if (unscheduledTasks() > 0)
+ if (hasExtraValues)
{
- fprintf(stderr, "Can't schedule some tasks. Giving up!\n");
+ if (DEBUGPS(1))
+ qWarning("Scheduling actual scenario...");
+ prepareScenario(Task::Actual);
+ if (!schedule("Actual"))
+ {
+ if (DEBUGPS(2))
+ qWarning("Scheduling errors in actual scenario.");
+ error = TRUE;
+ }
+ finishScenario(Task::Actual);
}
- else
- checkSchedule();
- return error;
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
+ t->computeBuffers();
+
+ /* Create indices for all lists according to their default sorting
+ * criteria. */
+ taskList.createIndex();
+ resourceList.createIndex();
+ accountList.createIndex();
+ shiftList.createIndex();
+
+ return !error;
}
-bool
-Project::reportHTMLHeader(FILE* f)
+void
+Project::overlayScenario(int sc)
{
- fprintf(f, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n"
- "<! Generated by TaskJuggler v"VERSION">"
- "<html>\n"
- "<head>\n"
- "<title>Task Report</title>\n"
- "</head>\n"
- "<body>\n");
- return TRUE;
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
+ t->overlayScenario(sc);
}
-bool
-Project::reportHTMLFooter(FILE* f)
+void
+Project::prepareScenario(int sc)
{
- fprintf(f, "<br><font size=\"-2\">%s - TaskJuggler v"
- VERSION"</font></body>\n"
- "</html>\n", version.latin1());
- return TRUE;
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
+ t->prepareScenario(sc);
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
+ t->propagateInitialValues();
+ for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
+ r->prepareScenario(sc);
}
-bool
-Project::reportHTMLTaskList()
+void
+Project::finishScenario(int sc)
{
- /* TODO: This is pretty much a quick hack. Using FILE is bad. latin1()
- * does not work for non ASCII characters! */
- if (htmlTaskReport == "")
- return TRUE;
-
- if (htmlTaskReportStart == 0)
- htmlTaskReportStart = start;
- if (htmlTaskReportEnd == 0)
- htmlTaskReportEnd = 0;
+ for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
+ r->finishScenario(sc);
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
+ t->finishScenario(sc);
+}
- FILE* f;
- if ((f = fopen(htmlTaskReport, "w")) == NULL)
- {
- fprintf(stderr, "Cannot open report file %s!\n",
- htmlTaskReport.latin1());
- return FALSE;
- }
- reportHTMLHeader(f);
- fprintf(f, "<table border=\"0\" cellpadding=\"1\">\n");
- fprintf(f, "<tr>");
- for (QStringList::Iterator it = htmlTaskReportColumns.begin();
- it != htmlTaskReportColumns.end(); ++it )
- {
- if (*it == "no")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>No.</h2></td>");
- else if (*it == "taskId")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>Task ID</h2></td>");
- else if (*it == "name")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>Task</h2></td>");
- else if (*it == "start")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>Start</h2></td>");
- else if (*it == "end")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>End</h2></td>");
- else if (*it == "minStart")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>Min. Start</h2></td>");
- else if (*it == "maxStart")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>Max. Start</h2></td>");
- else if (*it == "resources")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>Resources</h2></td>");
- else if (*it == "depends")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>Dependencies</h2></td>");
- else if (*it == "follows")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>Following Tasks</h2></td>");
- else if (*it == "note")
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\">"
- "<h2>Note</h2></td>");
- else if (*it == "daily")
- {
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" rowspan=\"2\"></td>");
- htmlMonthHeader(f, htmlTaskReportStart, htmlTaskReportEnd);
- }
- else
- {
- fprintf(stderr, "Unknown Column '%s' for HTML Task Report\n",
- (*it).latin1());
- return FALSE;
- }
- }
- fprintf(f, "</tr>");
- fprintf(f, "<tr>");
- for (QStringList::Iterator it = htmlTaskReportColumns.begin();
- it != htmlTaskReportColumns.end(); ++it )
- {
- if (*it == "daily")
- htmlDayHeader(f, htmlTaskReportStart, htmlTaskReportEnd);
- }
- fprintf(f, "</tr>\n");
+bool
+Project::schedule(const QString& scenario)
+{
+ bool error = FALSE;
- int i = 1;
- QDict<int> idxDict;
- idxDict.setAutoDelete(TRUE);
- for (Task* t = taskList.first(); t != 0; t = taskList.next(), ++i)
- idxDict.insert(t->getId(), new int(i));
+ TaskList sortedTasks(taskList);
+ sortedTasks.setSorting(CoreAttributesList::PrioDown, 0);
+ sortedTasks.setSorting(CoreAttributesList::SequenceUp, 1);
+ sortedTasks.sort();
- i = 1;
- for (Task* t = taskList.first(); t != 0; t = taskList.next(), ++i)
+ bool done;
+ do
{
- if (t->hasFlag("hidden"))
- continue;
-
- fprintf(f, "<tr valign=\"center\">");
- for (QStringList::Iterator it = htmlTaskReportColumns.begin();
- it != htmlTaskReportColumns.end(); ++it )
+ done = TRUE;
+ time_t slot = 0;
+ for (Task* t = sortedTasks.first(); t; t = sortedTasks.next())
{
- if (*it == "no")
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\">"
- "%d.</td>", i);
- else if (*it == "taskId")
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\""
- " nowrap>%s</td>", t->getId().latin1());
- else if (*it == "name")
+ if (slot == 0)
{
- int indent;
- Task* tp = t->getParent();
- QString spaces = "";
- for (indent = 0; tp != 0; ++indent)
+ slot = t->nextSlot(scheduleGranularity);
+ if (slot == 0)
+ continue;
+ if (DEBUGPS(5))
+ qWarning("Task %s requests slot %s", t->getId().latin1(),
+ time2ISO(slot).latin1());
+ if (slot < start || slot > end)
{
- spaces += " ";
- tp = tp->getParent();
- }
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\""
- " nowrap>%s<font size=\"%c%d\">%s</font></td>\n",
- spaces.latin1(), 2 - indent < 0 ? '-' : '+',
- 2 - indent < 0 ? -(2 - indent) : 2 - indent,
- t->getName().latin1());
- }
- else if (*it == "start")
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\" %s>"
- "%s</td>\n",
- t->isStartOk() ? "" : "bgcolor=\"red\"",
- t->getStartISO().latin1());
- else if (*it == "end")
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\" %s>"
- "%s</td>\n",
- t->isEndOk() ? "" : "bgcolor=\"red\"",
- t->getEndISO().latin1());
- else if (*it == "minStart")
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\">"
- "%s</td>\n",
- time2ISO(t->getMinStart()).latin1());
- else if (*it == "maxStart")
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\">"
- "%s</td>\n",
- time2ISO(t->getMaxStart()).latin1());
- else if (*it == "resources")
- {
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\">"
- "<font size=\"-2\">");
- bool first = TRUE;
- for (Resource* r = t->firstBookedResource(); r != 0;
- r = t->nextBookedResource())
- {
- fprintf(f, "%s%s", first ? "" : ", ",
- r->getName().latin1());
- first = FALSE;
- }
- fprintf(f, "</font></td>\n");
- }
- else if (*it == "depends")
- {
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\">"
- "<font size=\"-2\">");
- bool first = TRUE;
- for (Task* d = t->firstPrevious(); d != 0;
- d = t->nextPrevious())
- {
- fprintf(f, "%s%d", first ? "" : ", ",
- *(idxDict[d->getId()]));
- first = FALSE;
- }
- fprintf(f, "</font></td>\n");
- }
- else if (*it == "follows")
- {
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\">"
- "<font size=\"-2\">");
- bool first = TRUE;
- for (Task* d = t->firstFollower(); d != 0;
- d = t->nextFollower())
- {
- fprintf(f, "%s%d", first ? "" : ", ",
- *(idxDict[d->getId()]));
- first = FALSE;
- }
- fprintf(f, "</font></td>\n");
- }
- else if (*it == "note")
- {
- fprintf(f, "<td bgcolor=\""COL_DEFAULT"\" rowspan=\"2\">"
- "<font size=\"-2\">%s</font></td>\n",
- htmlFilter(t->getNote()).latin1());
- }
- else if (*it == "daily")
- {
- fprintf(f, "<td bgcolor=\""COL_HEADER"\"><font size=\"-4\">"
- "Plan</font></td>");
- const int oneDay = 60 * 60 * 24;
- for (time_t day = htmlTaskReportStart;
- day < htmlTaskReportEnd; day += oneDay)
- {
- double load = t->getLoadOnDay(day);
- QString bgCol = COL_DEFAULT;
- if (t->isMilestone() && t->isActiveToday(day))
- bgCol = COL_MILESTONE;
- else if (isWeekend(day))
- bgCol = COL_WEEKEND;
- else if (t->isActiveToday(day))
- bgCol = COL_BOOKED;
- if (load > 0.0)
- fprintf(f,
- "<td bgcolor=\"%s\"><font size=\"-3\">%3.1f</font></td>",
- bgCol.latin1(), load);
- else
- fprintf(f, "<td bgcolor=\"%s\"></td>",
- bgCol.latin1());
+ t->setRunaway();
+ if (DEBUGPS(5))
+ qDebug("Marking task %s as runaway",
+ t->getId().latin1());
+ error = TRUE;
}
}
+ t->schedule(slot, scheduleGranularity);
+ done = FALSE;
}
- fprintf(f, "</tr>");
-
- fprintf(f, "<tr>\n");
- for (QStringList::Iterator it = htmlTaskReportColumns.begin();
- it != htmlTaskReportColumns.end(); ++it )
- {
- if (*it == "daily")
- {
- fprintf(f, "<td bgcolor=\""COL_HEADER"\"><font size=\"-4\">"
- "Actual</font></td>");
- const int oneDay = 60 * 60 * 24;
- for (time_t day = htmlTaskReportStart;
- day < htmlTaskReportEnd; day += oneDay)
- fprintf(f, "<td bgcolor=\"%s\"></td>",
- isWeekend(day) ? COL_WEEKEND : COL_DEFAULT);
- }
- }
- fprintf(f, "</tr>\n");
- }
- fprintf(f, "</table>");
- reportHTMLFooter(f);
-
- fclose(f);
- return TRUE;
+ } while (!done);
+
+ if (error)
+ for (Task* t = sortedTasks.first(); t; t = sortedTasks.next())
+ if (t->isRunaway())
+ if (t->getScheduling() == Task::ASAP)
+ t->fatalError(QString(
+ "End of task %1 does not fit into the project time "
+ "frame.").arg(t->getId()));
+ else
+ t->fatalError(QString(
+ "Start of task %1 does not fit into the project time "
+ "frame.").arg(t->getId()));
+
+ if (!checkSchedule(scenario))
+ error = TRUE;
+
+ return !error;
}
bool
-Project::reportHTMLResourceList()
+Project::checkSchedule(const QString& scenario)
{
- /* TODO: This is pretty much a quick hack. Using FILE is bad. latin1()
- * does not work for non ASCII characters! */
- if (htmlResourceReport == "")
- return TRUE;
- FILE* f;
- if ((f = fopen(htmlResourceReport, "w")) == NULL)
- {
- fprintf(stderr, "Cannot open report file %s!\n",
- htmlResourceReport.latin1());
- return FALSE;
- }
- reportHTMLHeader(f);
- fprintf(f, "<table border=\"1\" cellpadding=\"3\">\n");
- fprintf(f, "<tr>");
- fprintf(f, "<td rowspan=\"2\"></td>");
- htmlMonthHeader(f, htmlResourceReportStart, htmlResourceReportEnd);
- fprintf(f, "</tr>\n");
-
- fprintf(f, "<tr>");
- htmlDayHeader(f, htmlResourceReportStart, htmlResourceReportEnd);
- fprintf(f, "</tr>\n");
-
- const int oneDay = 60 * 60 * 24;
- for (Resource* r = resourceList.first(); r != 0;
- r = resourceList.next())
+ int errors = 0;
+ for (Task* t = taskList.first(); t != 0; t = taskList.next())
{
- fprintf(f, "<tr>");
- fprintf(f, "<td nowrap><b>%s</b></td>", r->getName().latin1());
- for (time_t day = htmlResourceReportStart; day < htmlResourceReportEnd;
- day += oneDay)
+ /* Only check top-level tasks, since they recursively check their sub
+ * tasks. */
+ if (t->getParent() == 0)
+ t->scheduleOk(errors, scenario);
+ if (errors >= 10)
{
- double load = r->getLoadOnDay(day);
- QString s = "";
- if (load > 0.0)
- s.sprintf("%3.1f", load);
- fprintf(f, "<td %s>%s</td>",
- isWeekend(day) ? "bgcolor=\""COL_WEEKEND"\"" : "",
- s.latin1());
+ qWarning(QString("Too many errors in %1 scenario. Giving up.")
+ .arg(scenario.lower()));
+ break;
}
- fprintf(f, "</tr>\n");
-
- for (Task* t = taskList.first(); t != 0; t = taskList.next())
- if (r->isAssignedTo(t))
- {
- fprintf(f, "<tr>");
- fprintf(f, "<td nowrap> <font size=\"-1\">%s</font></td>",
- t->getName().latin1());
- for (time_t day = htmlResourceReportStart;
- day < htmlResourceReportEnd; day += oneDay)
- {
- double load = r->getLoadOnDay(day, t);
- QString s = "";
- if (load > 0.0)
- s.sprintf("%3.1f", load);
- fprintf(f, "<td %s><font size=\"-1\">%s</font></td>",
- isWeekend(day) ? "bgcolor=\""COL_WEEKEND"\"" : "",
- s.latin1());
- }
- fprintf(f, "</tr>\n");
- }
}
- fprintf(f, "</table>");
- reportHTMLFooter(f);
-
- fclose(f);
- return TRUE;
+ return errors == 0;
}
void
-Project::htmlDayHeader(FILE* f, time_t s, time_t e)
+Project::generateReports()
{
- const int oneDay = 60 * 60 * 24;
+ if (DEBUGPS(1))
+ qWarning("Generating reports...");
+
+ // Generate task reports
+ for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
+ h = htmlTaskReports.next())
+ h->generate();
+
+ // Generate resource reports
+ for (HTMLResourceReport* r = htmlResourceReports.first(); r != 0;
+ r = htmlResourceReports.next())
+ r->generate();
+
+ // Generate account reports
+ for (HTMLAccountReport* r = htmlAccountReports.first(); r != 0;
+ r = htmlAccountReports.next())
+ r->generate();
+
+ // Generate calendar reports
+ for (HTMLWeeklyCalendar* r = htmlWeeklyCalendars.first(); r != 0;
+ r = htmlWeeklyCalendars.next())
+ r->generate();
+
+ // Generate export files
+ for (ExportReport* e = exportReports.first(); e != 0;
+ e = exportReports.next())
+ e->generate();
+
+ if( xmlreport )
+ xmlreport->generate();
+#ifdef HAVE_ICAL
+#ifdef HAVE_KDE
+ if( icalReport )
+ icalReport->generate();
+#endif
+#endif
- for (time_t day = s; day < e; day += oneDay)
- {
- int dom = dayOfMonth(day);
- fprintf(f, "<td bgcolor=\"%s\"><font size=\"-2\">"
- " %s%d</font></td>",
- isWeekend(day) ? COL_WEEKEND : COL_HEADER,
- dom < 10 ? " " : "", dom);
- }
}
-void
-Project::htmlMonthHeader(FILE* f, time_t s, time_t e)
+bool
+Project::needsActualDataForReports()
{
- const int oneDay = 60 * 60 * 24;
-
- for (time_t day = s; day < e; day += oneDay * daysLeftInMonth(day))
- {
- int left = daysLeftInMonth(day);
- if (left > (e - day) / oneDay)
- left = (e - day) / oneDay;
- fprintf(f, "<td bgcolor=\""COL_HEADER"\" colspan=\"%d\" "
- "align=\"center\"><font size=\"+2\"><b>%s</b></font></td>",
- left, monthAndYear(day));
- }
+ bool needsActual = FALSE;
+
+ // Generate task reports
+ for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
+ h = htmlTaskReports.next())
+ if (h->getShowActual())
+ needsActual = TRUE;
+ // Generate resource reports
+ for (HTMLResourceReport* h = htmlResourceReports.first(); h != 0;
+ h = htmlResourceReports.next())
+ if (h->getShowActual())
+ needsActual = TRUE;
+
+ // Generate account reports
+ for (HTMLAccountReport* h = htmlAccountReports.first(); h != 0;
+ h = htmlAccountReports.next())
+ if (h->getShowActual())
+ needsActual = TRUE;
+
+ return needsActual;
}
void
-Project::printText()
+Project::setKotrus(Kotrus* k)
{
- printf("ID Task Name Start End\n");
- int i = 0;
- for (Task* t = taskList.first(); t != 0; t = taskList.next(), ++i)
- {
- printf("%2d. %-12s %10s %10s\n",
- i, t->getName().latin1(),
- t->getStartISO().latin1(), t->getEndISO().latin1());
- }
-
- printf("\n\n");
- resourceList.printText();
+ if (kotrus)
+ delete kotrus;
+ kotrus = k;
}
-int
-Project::unscheduledTasks()
+bool
+Project::readKotrus()
{
- int cntr = 0;
- for (Task* t = taskList.first(); t != 0; t = taskList.next())
- if (!t->isScheduled())
- cntr++;
+ if (!kotrus)
+ return TRUE;
+
+ for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
+ r->dbLoadBookings(r->getKotrusId(), 0);
- return cntr;
+ return TRUE;
}
bool
-Project::checkSchedule()
+Project::updateKotrus()
{
- for (Task* t = taskList.first(); t != 0; t = taskList.next())
- if (!t->scheduleOK())
- return FALSE;
-
return TRUE;
}
-QString
-Project::htmlFilter(const QString& s)
+bool
+Project::loadFromXML( const QString& inpFile )
+{
+ QDomDocument doc;
+ QFile file( inpFile );
+
+ doc.setContent( &file );
+ qDebug( "Loading XML " + inpFile );
+
+ QDomElement elemProject = doc.documentElement();
+
+ if( !elemProject.isNull())
+ {
+ parseDomElem( elemProject );
+ }
+ else
+ {
+ qDebug("Empty !" );
+ }
+ pass2();
+ scheduleAllScenarios();
+ return true;
+}
+
+
+void Project::parseDomElem( QDomElement& parentElem )
{
- // TODO: All special characters must be html-ized.
- return s;
+ QDomElement elem = parentElem.firstChild().toElement();
+
+ for( ; !elem.isNull(); elem = elem.nextSibling().toElement() )
+ {
+ QString tagName = elem.tagName();
+
+ qDebug( "|| elemType: " + tagName );
+
+ if( tagName == "Task" )
+ {
+ QString tId = elem.attribute("Id");
+ Task *t = new Task( this, tId, QString(), 0, QString(), 0 );
+
+ t->loadFromXML( elem, this );
+ addTask( t );
+ }
+ else if( tagName == "Name" )
+ {
+ setName( elem.text() );
+ }
+ else if( tagName == "Project" )
+ {
+ QString prjId = elem.attribute("Id");
+ addId( prjId ); // FIXME ! There can be more than one project ids!
+
+ prjId = elem.attribute("WeekStart");
+ setWeekStartsMonday( prjId == "Mon" );
+ }
+ else if( tagName == "Version" )
+ setVersion( elem.text() );
+ else if( tagName == "Priority" )
+ setPriority( elem.text().toInt());
+ else if( tagName == "start" )
+ setStart( elem.text().toLong());
+ else if( tagName == "end" )
+ setEnd( elem.text().toLong());
+
+ // parseDomElem( elem );
+ }
}