2 * Project.cpp - TaskJuggler
4 * Copyright (c) 2001, 2002 by Chris Schlaeger <cs@suse.de>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of version 2 of the GNU General Public License as
8 * published by the Free Software Foundation.
25 int Project::debugLevel = 0;
26 int Project::debugMode = -1;
30 taskList.setAutoDelete(TRUE);
31 resourceList.setAutoDelete(TRUE);
32 accountList.setAutoDelete(TRUE);
34 vacationList.setAutoDelete(TRUE);
36 htmlTaskReports.setAutoDelete(TRUE);
37 htmlResourceReports.setAutoDelete(TRUE);
38 htmlAccountReports.setAutoDelete(TRUE);
39 htmlWeeklyCalendars.setAutoDelete(TRUE);
40 exportReports.setAutoDelete(TRUE);
42 scenarioNames.append("Plan");
43 scenarioNames.append("Actual");
53 /* The following settings are country and culture dependent. Those
54 * defaults are probably true for many Western countries, but have to be
55 * changed in project files. */
56 dailyWorkingHours = 8.0;
57 yearlyWorkingDays = 252;
58 scheduleGranularity = ONEHOUR;
59 weekStartsMonday = TRUE;
60 timeFormat = "%Y-%m-%d %H:%M";
61 shortTimeFormat = "%H:%M";
73 /* Initialize working hours with default values that match the Monday -
74 * Friday 9 - 6 (with 1 hour lunch break) pattern used by many western
77 workingHours[0] = new QPtrList<Interval>();
78 workingHours[0]->setAutoDelete(TRUE);
80 for (int i = 1; i < 6; ++i)
82 workingHours[i] = new QPtrList<Interval>();
83 workingHours[i]->setAutoDelete(TRUE);
84 workingHours[i]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
85 workingHours[i]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
89 workingHours[6] = new QPtrList<Interval>();
90 workingHours[6]->setAutoDelete(TRUE);
104 Project::getScenarioName(int sc)
106 return scenarioNames[sc];
110 Project::addId(const QString& id)
112 if (projectIDs.findIndex(id) != -1)
115 projectIDs.append(id);
120 Project::getIdIndex(const QString& i) const
123 if ((idx = projectIDs.findIndex(i)) == -1)
128 idxStr = QChar('A' + idx % ('Z' - 'A')) + idxStr;
130 } while (idx > 'Z' - 'A');
136 Project::pass2(bool checkOnlySyntax)
141 // Generate sequence numbers for all lists.
142 taskList.createIndex(TRUE);
143 resourceList.createIndex(TRUE);
144 accountList.createIndex(TRUE);
145 shiftList.createIndex(TRUE);
147 // Initialize random generator.
150 // Create hash to map task IDs to pointers.
151 for (Task* t = taskList.first(); t != 0; t = taskList.next())
153 idHash.insert(t->getId(), t);
155 // Create cross links from dependency lists.
156 for (Task* t = taskList.first(); t != 0; t = taskList.next())
158 if (!t->xRef(idHash))
161 // Set dates according to implicit dependencies
162 for (Task* t = taskList.first(); t != 0; t = taskList.next())
165 // Find out what scenarios need to be scheduled.
166 // TODO: No multiple scenario support yet.
167 bool hasExtraValues = FALSE;
168 for (Task* t = taskList.first(); t != 0; t = taskList.next())
169 if (!hasExtraValues && t->hasExtraValues(Task::Actual))
170 hasExtraValues = TRUE;
172 /* Now we can copy the missing values from the plan scenario to the other
174 for (int sc = 1; sc < getMaxScenarios(); sc++)
177 // Now check that all tasks have sufficient data to be scheduled.
178 for (Task* t = taskList.first(); t != 0; t = taskList.next())
179 if (!t->preScheduleOk())
182 for (Task* t = taskList.first(); t != 0; t = taskList.next())
183 if (t->loopDetector())
193 qWarning("Scheduling plan scenario...");
194 prepareScenario(Task::Plan);
195 if (!schedule("Plan"))
198 qWarning("Scheduling errors in plan scenario.");
201 finishScenario(Task::Plan);
206 qWarning("Scheduling actual scenario...");
207 prepareScenario(Task::Actual);
208 if (!schedule("Actual"))
211 qWarning("Scheduling errors in actual scenario.");
214 finishScenario(Task::Actual);
217 for (Task* t = taskList.first(); t != 0; t = taskList.next())
220 /* Create indices for all lists according to their default sorting
222 taskList.createIndex();
223 resourceList.createIndex();
224 accountList.createIndex();
225 shiftList.createIndex();
231 Project::overlayScenario(int sc)
233 for (Task* t = taskList.first(); t != 0; t = taskList.next())
234 t->overlayScenario(sc);
238 Project::prepareScenario(int sc)
240 for (Task* t = taskList.first(); t != 0; t = taskList.next())
241 t->prepareScenario(sc);
242 for (Task* t = taskList.first(); t != 0; t = taskList.next())
243 t->propagateInitialValues();
244 for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
245 r->prepareScenario(sc);
249 Project::finishScenario(int sc)
251 for (Task* t = taskList.first(); t != 0; t = taskList.next())
252 t->finishScenario(sc);
253 for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
254 r->finishScenario(sc);
258 Project::schedule(const QString& scenario)
262 TaskList sortedTasks(taskList);
263 sortedTasks.setSorting(CoreAttributesList::PrioDown, 0);
264 sortedTasks.setSorting(CoreAttributesList::SequenceUp, 1);
272 for (Task* t = sortedTasks.first(); t; t = sortedTasks.next())
276 slot = t->nextSlot(scheduleGranularity);
280 qWarning("Task %s requests slot %s", t->getId().latin1(),
281 time2ISO(slot).latin1());
282 if (slot < start || slot > end)
286 qDebug("Marking task %s as runaway",
287 t->getId().latin1());
291 t->schedule(slot, scheduleGranularity);
297 for (Task* t = sortedTasks.first(); t; t = sortedTasks.next())
299 if (t->getScheduling() == Task::ASAP)
300 t->fatalError(QString(
301 "End of task %1 does not fit into the project time "
302 "frame.").arg(t->getId()));
304 t->fatalError(QString(
305 "Start of task %1 does not fit into the project time "
306 "frame.").arg(t->getId()));
308 if (!checkSchedule(scenario))
315 Project::checkSchedule(const QString& scenario)
318 for (Task* t = taskList.first(); t != 0; t = taskList.next())
320 /* Only check top-level tasks, since they recursively check their sub
322 if (t->getParent() == 0)
323 t->scheduleOk(errors, scenario);
326 qWarning(QString("Too many errors in %1 scenario. Giving up.")
327 .arg(scenario.lower()));
336 Project::generateReports()
339 qWarning("Generating reports...");
341 // Generate task reports
342 for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
343 h = htmlTaskReports.next())
346 // Generate resource reports
347 for (HTMLResourceReport* r = htmlResourceReports.first(); r != 0;
348 r = htmlResourceReports.next())
351 // Generate account reports
352 for (HTMLAccountReport* r = htmlAccountReports.first(); r != 0;
353 r = htmlAccountReports.next())
356 // Generate calendar reports
357 for (HTMLWeeklyCalendar* r = htmlWeeklyCalendars.first(); r != 0;
358 r = htmlWeeklyCalendars.next())
361 // Generate export files
362 for (ExportReport* e = exportReports.first(); e != 0;
363 e = exportReports.next())
367 xmlreport->generate();
371 icalReport->generate();
378 Project::needsActualDataForReports()
380 bool needsActual = FALSE;
382 // Generate task reports
383 for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
384 h = htmlTaskReports.next())
385 if (h->getShowActual())
387 // Generate resource reports
388 for (HTMLResourceReport* h = htmlResourceReports.first(); h != 0;
389 h = htmlResourceReports.next())
390 if (h->getShowActual())
393 // Generate account reports
394 for (HTMLAccountReport* h = htmlAccountReports.first(); h != 0;
395 h = htmlAccountReports.next())
396 if (h->getShowActual())
403 Project::setKotrus(Kotrus* k)
411 Project::readKotrus()
416 for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
417 r->dbLoadBookings(r->getKotrusId(), 0);
423 Project::updateKotrus()
429 Project::loadFromXML( const QString& inpFile )
432 QFile file( inpFile );
434 doc.setContent( &file );
435 qDebug( "Loading XML " + inpFile );
437 QDomElement elemProject = doc.documentElement();
439 if( !elemProject.isNull())
441 parseDomElem( elemProject );
452 void Project::parseDomElem( QDomElement& parentElem )
454 QDomElement elem = parentElem.firstChild().toElement();
456 for( ; !elem.isNull(); elem = elem.nextSibling().toElement() )
458 QString tagName = elem.tagName();
460 qDebug( "|| elemType: " + tagName );
462 if( tagName == "Task" )
464 QString tId = elem.attribute("Id");
465 Task *t = new Task( this, tId, QString(), 0, QString(), 0 );
467 t->loadFromXML( elem, this );
470 else if( tagName == "Name" )
472 setName( elem.text() );
474 else if( tagName == "Project" )
476 QString prjId = elem.attribute("Id");
477 addId( prjId ); // FIXME ! There can be more than one project ids!
479 prjId = elem.attribute("WeekStart");
480 setWeekStartsMonday( prjId == "Mon" );
482 else if( tagName == "Version" )
483 setVersion( elem.text() );
484 else if( tagName == "Priority" )
485 setPriority( elem.text().toInt());
486 else if( tagName == "start" )
487 setStart( elem.text().toLong());
488 else if( tagName == "end" )
489 setEnd( elem.text().toLong());
491 // parseDomElem( elem );