OSDN Git Service

- Added better status reporting functions.
[tjqt4port/tj2qt4.git] / taskjuggler / Project.cpp
index aa3c313..306ad72 100644 (file)
 /*
  * 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 += "&nbsp;&nbsp;&nbsp;&nbsp;";
-                                       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>&nbsp;&nbsp;&nbsp;&nbsp;<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\">"
-                               "&nbsp;%s%d</font></td>",
-                               isWeekend(day) ? COL_WEEKEND : COL_HEADER,
-                               dom < 10 ? "&nbsp;" : "", 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 );
+   }
 }