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);
33 activeAsap.setSorting(CoreAttributesList::PrioDown, 0);
34 activeAlap.setSorting(CoreAttributesList::PrioDown, 0);
36 /* The following settings are country and culture dependent. Those
37 * defaults are probably true for many Western countries, but have to be
38 * changed in project files. */
39 dailyWorkingHours = 8.0;
40 yearlyWorkingDays = 252;
41 scheduleGranularity = ONEHOUR;
45 weekStartsMonday = FALSE;
60 /* Initialize working hours with default values that match the Monday -
61 * Friday 9 - 6 (with 1 hour lunch break) pattern used by many western
64 workingHours[0] = new QPtrList<Interval>();
65 workingHours[0]->setAutoDelete(TRUE);
68 workingHours[1] = new QPtrList<Interval>();
69 workingHours[1]->setAutoDelete(TRUE);
70 workingHours[1]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
71 workingHours[1]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
73 workingHours[2] = new QPtrList<Interval>();
74 workingHours[2]->setAutoDelete(TRUE);
75 workingHours[2]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
76 workingHours[2]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
78 workingHours[3] = new QPtrList<Interval>();
79 workingHours[3]->setAutoDelete(TRUE);
80 workingHours[3]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
81 workingHours[3]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
83 workingHours[4] = new QPtrList<Interval>();
84 workingHours[4]->setAutoDelete(TRUE);
85 workingHours[4]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
86 workingHours[4]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
88 workingHours[5] = new QPtrList<Interval>();
89 workingHours[5]->setAutoDelete(TRUE);
90 workingHours[5]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
91 workingHours[5]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
94 workingHours[6] = new QPtrList<Interval>();
95 workingHours[6]->setAutoDelete(TRUE);
99 Project::addId(const QString& id)
101 if (projectIDs.findIndex(id) != -1)
104 projectIDs.append(id);
109 Project::getIdIndex(const QString& i) const
112 if ((idx = projectIDs.findIndex(i)) == -1)
117 idxStr = QChar('A' + idx % ('Z' - 'A')) + idxStr;
119 } while (idx > 'Z' - 'A');
125 Project::addTask(Task* t)
137 // Generate sequence numbers for all lists.
138 taskList.createIndex(TRUE);
139 resourceList.createIndex(TRUE);
140 accountList.createIndex(TRUE);
141 shiftList.createIndex(TRUE);
143 // Initialize random generator.
146 // Create hash to map task IDs to pointers.
147 for (Task* t = taskList.first(); t != 0; t = taskList.next())
149 idHash.insert(t->getId(), t);
151 // Create cross links from dependency lists.
152 for (Task* t = taskList.first(); t != 0; t = taskList.next())
154 if (!t->xRef(idHash))
157 // Set dates according to implicit dependencies
158 for (Task* t = taskList.first(); t != 0; t = taskList.next())
161 bool hasActualValues = FALSE;
162 for (Task* t = taskList.first(); t != 0; t = taskList.next())
164 if (!t->preScheduleOk())
166 if (!hasActualValues && t->hasActualValues())
167 hasActualValues = TRUE;
170 for (Task* t = taskList.first(); t != 0; t = taskList.next())
171 if (t->loopDetector())
178 qWarning("Scheduling plan scenario...");
187 qWarning("Scheduling actual scenario...");
194 for (Task* t = taskList.first(); t != 0; t = taskList.next())
197 /* Create indices for all lists according to their default sorting
199 taskList.createIndex();
200 resourceList.createIndex();
201 accountList.createIndex();
202 shiftList.createIndex();
208 Project::preparePlan()
210 for (Task* t = taskList.first(); t != 0; t = taskList.next())
212 for (Task* t = taskList.first(); t != 0; t = taskList.next())
213 t->propagateInitialValues();
214 for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
219 Project::finishPlan()
221 for (Task* t = taskList.first(); t != 0; t = taskList.next())
223 for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
228 Project::prepareActual()
230 for (Task* t = taskList.first(); t != 0; t = taskList.next())
232 for (Task* t = taskList.first(); t != 0; t = taskList.next())
233 t->propagateInitialValues();
234 for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
239 Project::finishActual()
241 for (Task* t = taskList.first(); t != 0; t = taskList.next())
243 for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
252 TaskList sortedTasks(taskList);
253 sortedTasks.setSorting(CoreAttributesList::PrioDown, 0);
256 time_t timeDelta = scheduleGranularity;
260 /* Find all tasks than have enough information to be scheduled and sort
261 * them into two lists; one list for all ASAP tasks and one list for all
263 updateActiveTaskList(sortedTasks);
264 for (day = start; !(activeAsap.isEmpty() && activeAlap.isEmpty()); )
267 qWarning("Scheduling slot %s", time2ISO(day).latin1());
268 timeDelta = scheduleGranularity;
272 qWarning("%d active ALAP tasks", activeAlap.count());
274 for (Task* t = activeAlap.first(); t != 0; t = activeAlap.next())
276 if (!t->schedule(day, scheduleGranularity))
279 break; // Start with top priority tasks again.
281 if (t->needsEarlierTimeSlot(day + scheduleGranularity))
284 qWarning("Scheduling backwards now");
285 timeDelta = -scheduleGranularity;
295 qWarning("%d active ASAP tasks", activeAsap.count());
297 for (Task* t = activeAsap.first(); t != 0;
298 t = activeAsap.next())
300 if (!t->schedule(day, scheduleGranularity))
303 break; // Start with top priority tasks again.
310 /* Runaway detection */
314 qWarning("Scheduler ran over start of project");
316 for (Task* t = activeAlap.first(); t != 0; t = activeAlap.next())
317 if (t->setRunaway(day - timeDelta, scheduleGranularity))
320 qDebug("Marking task %s as runaway",
321 t->getId().latin1());
329 qDebug("Scheduler ran over end of project");
331 for (Task* t = activeAsap.first(); t != 0; t = activeAsap.next())
332 if (t->setRunaway(day - timeDelta, scheduleGranularity))
335 qDebug("Marking task %s as runaway",
336 t->getId().latin1());
339 day = end - scheduleGranularity;
344 for (Task* t = sortedTasks.first(); t; t = sortedTasks.next())
346 if (t->getScheduling() == Task::ASAP)
347 t->fatalError(QString(
348 "End of task %1 does not fit into the project time "
349 "frame.").arg(t->getId()));
351 t->fatalError(QString(
352 "Start of task %1 does not fit into the project time "
353 "frame.").arg(t->getId()));
355 if (!checkSchedule())
362 Project::updateActiveTaskList(TaskList& sortedTasks)
364 for (Task* t = sortedTasks.first(); t != 0; t = sortedTasks.next())
370 Project::checkSchedule()
373 for (Task* t = taskList.first(); t != 0; t = taskList.next())
375 /* Only check top-level tasks, since they recursively check their sub
377 if (t->getParent() == 0)
378 t->scheduleOk(errors);
387 Project::setKotrus(Kotrus* k)
395 Project::generateReports()
398 qWarning("Generating reports...");
400 // Generate task reports
401 for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
402 h = htmlTaskReports.next())
405 // Generate resource reports
406 for (HTMLResourceReport* r = htmlResourceReports.first(); r != 0;
407 r = htmlResourceReports.next())
410 // Generate account reports
411 for (HTMLAccountReport* r = htmlAccountReports.first(); r != 0;
412 r = htmlAccountReports.next())
415 // Generate calendar reports
416 for (HTMLWeeklyCalendar* r = htmlWeeklyCalendars.first(); r != 0;
417 r = htmlWeeklyCalendars.next())
420 // Generate export files
421 for (ExportReport* e = exportReports.first(); e != 0;
422 e = exportReports.next())
426 xmlreport->generate();
430 icalReport->generate();
437 Project::needsActualDataForReports()
439 bool needsActual = FALSE;
441 // Generate task reports
442 for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
443 h = htmlTaskReports.next())
444 if (h->getShowActual())
446 // Generate resource reports
447 for (HTMLResourceReport* h = htmlResourceReports.first(); h != 0;
448 h = htmlResourceReports.next())
449 if (h->getShowActual())
452 // Generate account reports
453 for (HTMLAccountReport* h = htmlAccountReports.first(); h != 0;
454 h = htmlAccountReports.next())
455 if (h->getShowActual())
462 Project::removeActiveTask(Task* t)
467 qWarning("Deactivating %s", t->getId().latin1());
469 if (t->getScheduling() == Task::ASAP)
470 activeAsap.removeRef(t);
472 activeAlap.removeRef(t);
476 Project::addActiveTask(Task* t)
478 if (t->getScheduling() == Task::ASAP)
480 if (activeAsap.findRef(t) == -1)
483 qWarning("Activating %s", t->getId().latin1());
484 activeAsap.inSort(t);
489 if (activeAlap.findRef(t) == -1)
492 qWarning("Activating %s", t->getId().latin1());
493 activeAlap.inSort(t);
499 Project::readKotrus()
504 for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
505 r->dbLoadBookings(r->getKotrusId(), 0);
511 Project::updateKotrus()
518 Project::loadFromXML( const QString& inpFile )
521 QFile file( inpFile );
523 doc.setContent( &file );
524 qDebug( "Loading XML " + inpFile );
526 QDomElement elemProject = doc.documentElement();
528 if( !elemProject.isNull())
530 parseDomElem( elemProject );
541 void Project::parseDomElem( QDomElement& parentElem )
543 QDomElement elem = parentElem.firstChild().toElement();
545 for( ; !elem.isNull(); elem = elem.nextSibling().toElement() )
547 QString tagName = elem.tagName();
549 qDebug( "|| elemType: " + tagName );
551 if( tagName == "Task" )
553 QString tId = elem.attribute("Id");
554 Task *t = new Task( this, tId, QString(), 0, QString(), 0 );
556 t->loadFromXML( elem, this );
559 else if( tagName == "Name" )
561 setName( elem.text() );
563 else if( tagName == "Version" )
564 setVersion( elem.text() );
565 else if( tagName == "Priority" )
566 setPriority( elem.text().toInt());
567 else if( tagName == "start" )
568 setStart( elem.text().toLong());
569 else if( tagName == "end" )
570 setEnd( elem.text().toLong());
572 // parseDomElem( elem );