21#include "kptproject.h"
22#include "kptappointment.h"
24#include "kptprojectdialog.h"
25#include "kptdatetime.h"
28#include "kpteffortcostmap.h"
29#include "kptschedule.h"
33#include <tqdatetime.h>
38#include <kdatetimewidget.h>
45Project::Project(Node *parent)
50 m_constraint = Node::MustStartOn;
51 m_standardWorktime =
new StandardWorktime();
52 m_schedules.setAutoDelete(
true);
59 m_constraintStartTime = TQDateTime(TQDate::currentDate(), TQTime());
60 m_constraintEndTime = TQDateTime(m_constraintStartTime.addDays(1));
62 m_calendars.setAutoDelete(
true);
67 m_resourceGroups.setAutoDelete(
true);
68 m_resourceGroups.clear();
69 delete m_standardWorktime;
72int Project::type()
const {
return Node::Type_Project; }
76 kdError()<<k_funcinfo<<
"Schedule == 0, cannot calculate"<<endl;
79 m_currentSchedule = schedule;
83void Project::calculate(Effort::Use estType) {
84 m_currentSchedule = findSchedule((Schedule::Type)estType);
85 if (m_currentSchedule == 0) {
86 m_currentSchedule = createSchedule(i18n(
"Standard"), (Schedule::Type)estType);
91void Project::calculate() {
92 if (m_currentSchedule == 0) {
93 kdError()<<k_funcinfo<<
"No current schedule to calculate"<<endl;
96 Effort::Use estType = (Effort::Use)m_currentSchedule->type();
97 if (type() == Type_Project) {
98 initiateCalculation(*m_currentSchedule);
99 if (m_constraint == Node::MustStartOn) {
101 m_currentSchedule->startTime = m_constraintStartTime;
102 m_currentSchedule->earliestStart = m_constraintStartTime;
104 propagateEarliestStart(m_currentSchedule->earliestStart);
105 m_currentSchedule->latestFinish = calculateForward(estType);
106 propagateLatestFinish(m_currentSchedule->latestFinish);
107 calculateBackward(estType);
108 m_currentSchedule->endTime = scheduleForward(m_currentSchedule->startTime, estType);
109 calcCriticalPath(
false);
112 m_currentSchedule->endTime = m_constraintEndTime;
113 m_currentSchedule->latestFinish = m_constraintEndTime;
115 propagateLatestFinish(m_currentSchedule->latestFinish);
116 m_currentSchedule->earliestStart = calculateBackward(estType);
117 propagateEarliestStart(m_currentSchedule->earliestStart);
118 calculateForward(estType);
119 m_currentSchedule->startTime = scheduleBackward(m_currentSchedule->endTime, estType);
120 calcCriticalPath(
true);
123 calcResourceOverbooked();
124 m_currentSchedule->notScheduled =
false;
125 }
else if (type() == Type_Subproject) {
126 kdWarning()<<k_funcinfo<<
"Subprojects not implemented"<<endl;
128 kdError()<<k_funcinfo<<
"Illegal project type: "<<type()<<endl;
132bool Project::calcCriticalPath(
bool fromEnd) {
135 TQPtrListIterator<Node> startnodes = m_startNodes;
136 for (; startnodes.current(); ++startnodes) {
137 startnodes.current()->calcCriticalPath(fromEnd);
140 TQPtrListIterator<Node> endnodes = m_endNodes;
141 for (; endnodes.current(); ++endnodes) {
142 endnodes.current()->calcCriticalPath(fromEnd);
150 if (m_currentSchedule)
151 return m_currentSchedule->startTime;
153 return m_constraintStartTime;
158 if (m_currentSchedule)
159 return m_currentSchedule->endTime;
161 return m_constraintEndTime;
166 return new Duration(getLatestFinish() - getEarliestStart());
173DateTime Project::calculateForward(
int use) {
175 if (type() == Node::Type_Project) {
180 TQPtrListIterator<Node> endnodes = m_endNodes;
181 for (; endnodes.current(); ++endnodes) {
182 time = endnodes.current()->calculateForward(use);
183 if (!finish.isValid() || time > finish)
194DateTime Project::calculateBackward(
int use) {
196 if (type() == Node::Type_Project) {
201 TQPtrListIterator<Node> startnodes = m_startNodes;
202 for (; startnodes.current(); ++startnodes) {
203 time = startnodes.current()->calculateBackward(use);
204 if (!start.isValid() || time < start)
215DateTime Project::scheduleForward(
const DateTime &earliest,
int use) {
217 DateTime end = earliest;
219 TQPtrListIterator<Node> it(m_endNodes);
220 for (; it.current(); ++it) {
221 time = it.current()->scheduleForward(earliest, use);
230DateTime Project::scheduleBackward(
const DateTime &latest,
int use) {
232 DateTime start = latest;
234 TQPtrListIterator<Node> it(m_startNodes);
235 for (; it.current(); ++it) {
236 time = it.current()->scheduleBackward(latest, use);
245void Project::adjustSummarytask() {
246 TQPtrListIterator<Node> it(m_summarytasks);
247 for (; it.current(); ++it) {
248 it.current()->adjustSummarytask();
252void Project::initiateCalculation(Schedule &sch) {
255 m_visitedForward =
false;
256 m_visitedBackward =
false;
257 TQPtrListIterator<ResourceGroup> git(m_resourceGroups);
258 for ( ; git.current(); ++git ) {
259 git.current()->initiateCalculation(sch);
261 Node::initiateCalculation(sch);
262 m_startNodes.clear();
264 m_summarytasks.clear();
265 initiateCalculationLists(m_startNodes, m_endNodes, m_summarytasks);
268void Project::initiateCalculationLists(TQPtrList<Node> &startnodes, TQPtrList<Node> &endnodes, TQPtrList<Node> &summarytasks) {
270 if (type() == Node::Type_Project) {
271 TQPtrListIterator<Node> it = childNodeIterator();
272 for (; it.current(); ++it) {
273 it.current()->initiateCalculationLists(startnodes, endnodes, summarytasks);
280bool Project::load(TQDomElement &element) {
284 TQString
id = element.attribute(
"id");
286 kdWarning()<<k_funcinfo<<
"Id must be unique: "<<
id<<endl;
288 m_name = element.attribute(
"name");
289 m_leader = element.attribute(
"leader");
290 m_description = element.attribute(
"description");
295 s = element.attribute(
"scheduling",
"0");
296 m_constraint = (Node::ConstraintType)s.toInt(&ok);
299 if (m_constraint != Node::MustStartOn &&
300 m_constraint != Node::MustFinishOn) {
301 kdError()<<k_funcinfo<<
"Illegal constraint: "<<constraintToString()<<endl;
302 setConstraint(Node::MustStartOn);
304 s = element.attribute(
"start-time");
306 m_constraintStartTime = DateTime::fromString(s);
307 s = element.attribute(
"end-time");
309 m_constraintEndTime = DateTime::fromString(s);
313 TQDomNodeList list = element.childNodes();
314 for (
unsigned int i=0; i<list.count(); ++i) {
315 if (list.item(i).isElement()) {
316 TQDomElement e = list.item(i).toElement();
317 if (e.tagName() ==
"calendar") {
320 Calendar *child =
new Calendar();
321 child->setProject(
this);
322 if (child->load(e)) {
326 kdError()<<k_funcinfo<<
"Failed to load calendar"<<endl;
329 }
else if (e.tagName() ==
"standard-worktime") {
331 StandardWorktime *child =
new StandardWorktime();
332 if (child->load(e)) {
333 setStandardWorktime(child);
335 kdError()<<k_funcinfo<<
"Failed to load standard worktime"<<endl;
341 for (
unsigned int i=0; i<list.count(); ++i) {
342 if (list.item(i).isElement()) {
343 TQDomElement e = list.item(i).toElement();
345 if (e.tagName() ==
"resource-group") {
348 ResourceGroup *child =
new ResourceGroup(
this);
349 if (child->load(e)) {
350 addResourceGroup(child);
358 for (
unsigned int i=0; i<list.count(); ++i) {
359 if (list.item(i).isElement()) {
360 TQDomElement e = list.item(i).toElement();
362 if (e.tagName() ==
"project") {
365 Project *child =
new Project(
this);
366 if (child->load(e)) {
367 if (!addTask(child,
this)) {
374 }
else if (e.tagName() ==
"task") {
378 Task *child =
new Task(
this);
379 if (child->load(e, *
this)) {
380 if (!addTask(child,
this)) {
391 for (
unsigned int i=0; i<list.count(); ++i) {
392 if (list.item(i).isElement()) {
393 TQDomElement e = list.item(i).toElement();
394 if (e.tagName() ==
"accounts") {
398 if (!m_accounts.load(e, *
this)) {
399 kdError()<<k_funcinfo<<
"Failed to load accounts"<<endl;
401 }
else if (e.tagName() ==
"relation") {
405 Relation *child =
new Relation();
406 if (!child->load(e, *
this)) {
408 kdError()<<k_funcinfo<<
"Failed to load relation"<<endl;
412 }
else if (e.tagName() ==
"schedules") {
416 TQDomNodeList lst = e.childNodes();
417 for (
unsigned int i=0; i<lst.count(); ++i) {
418 if (lst.item(i).isElement()) {
419 TQDomElement el = lst.item(i).toElement();
420 if (el.tagName() ==
"schedule") {
421 MainSchedule *sch =
new MainSchedule();
422 if (sch->loadXML(el, *
this)) {
425 setParentSchedule(sch);
427 sch->setScheduled(
true);
429 kdError()<<k_funcinfo<<
"Failed to load schedule"<<endl;
441 TQPtrListIterator<Calendar> calit(m_calendars);
442 for (; calit.current(); ++calit) {
443 if (calit.current()->id() == calit.current()->parentId()) {
444 kdError()<<k_funcinfo<<
"Calendar want itself as parent"<<endl;
447 calit.current()->setParent(calendar(calit.current()->parentId()));
450 TQIntDictIterator<Schedule> it = m_schedules;
452 if (m_constraint == Node::MustFinishOn)
453 m_constraintEndTime = it.current()->endTime;
455 m_constraintStartTime = it.current()->startTime;
462void Project::save(TQDomElement &element)
const {
463 TQDomElement me = element.ownerDocument().createElement(
"project");
464 element.appendChild(me);
466 me.setAttribute(
"name", m_name);
467 me.setAttribute(
"leader", m_leader);
468 me.setAttribute(
"id", m_id);
469 me.setAttribute(
"description", m_description);
473 me.setAttribute(
"scheduling",constraintToString());
474 me.setAttribute(
"start-time", m_constraintStartTime.toString(TQt::ISODate));
475 me.setAttribute(
"end-time", m_constraintEndTime.toString(TQt::ISODate));
480 TQPtrListIterator<Calendar> calit(m_calendars);
481 for (; calit.current(); ++calit) {
482 calit.current()->save(me);
485 if (m_standardWorktime)
486 m_standardWorktime->save(me);
489 TQPtrListIterator<ResourceGroup> git(m_resourceGroups);
490 for ( ; git.current(); ++git ) {
491 git.current()->save(me);
495 TQPtrListIterator<Relation> it(m_dependParentNodes);
496 for ( ; it.current(); ++it ) {
497 it.current()->save(me);
500 for (
int i=0; i<numChildren(); i++)
502 getChildNode(i)->save(me);
505 TQPtrListIterator<Node> nodes(m_nodes);
506 for ( ; nodes.current(); ++nodes ) {
507 nodes.current()->saveRelations(me);
510 if (!m_schedules.isEmpty()) {
511 TQDomElement el = me.ownerDocument().createElement(
"schedules");
513 TQIntDictIterator<Schedule> it = m_schedules;
514 for (; it.current(); ++it) {
515 if (!it.current()->isDeleted() && it.current()->isScheduled()) {
516 TQDomElement schs = el.ownerDocument().createElement(
"schedule");
517 el.appendChild(schs);
518 it.current()->saveXML(schs);
520 Node::saveAppointments(schs, it.current()->id());
527 TQPtrListIterator<Node> it = m_nodes;
528 for (; it.current(); ++it) {
529 it.current()->setParentSchedule(sch);
534 m_resourceGroups.append(group);
538void Project::removeResourceGroup(ResourceGroup * group){
539 m_resourceGroups.remove(group);
543void Project::removeResourceGroup(
int ){
548void Project::insertResourceGroup(
unsigned int ,
552TQPtrList<ResourceGroup> &Project::resourceGroups() {
553 return m_resourceGroups;
556bool Project::addTask( Node* task, Node* position )
560 if ( 0 == position ) {
561 kdError()<<k_funcinfo<<
"position=0, could not add task: "<<task->name()<<endl;
567 if ( Node::Type_Project == position->type() ) {
568 return addSubTask(task, position);
572 Node* parentNode = position->getParent();
574 kdDebug()<<k_funcinfo<<
"parent node not found???"<<endl;
577 int index = parentNode->findChildNode( position );
580 kdDebug()<<k_funcinfo<<
"Task not found???"<<endl;
583 return addSubTask(task, index+1, parentNode);
586bool Project::addSubTask( Node* task, Node* position )
590 if ( 0 == position ) {
591 kdError()<<k_funcinfo<<
"No parent, can not add subtask: "<<task->name()<<endl;
594 if (!registerNodeId(task)) {
595 kdError()<<k_funcinfo<<
"Failed to register node id, can not add subtask: "<<task->name()<<endl;
598 position->addChildNode(task);
602bool Project::addSubTask( Node* task,
int index, Node* parent )
606 kdError()<<k_funcinfo<<
"No parent, can not add subtask: "<<task->name()<<endl;
609 if (!registerNodeId(task)) {
610 kdError()<<k_funcinfo<<
"Failed to register node id, can not add subtask: "<<task->name()<<endl;
613 parent->insertChildNode(index, task);
617void Project::delTask(Node *node)
619 Node *parent = node->getParent();
621 kdDebug()<<k_funcinfo<<
"Node must have a parent!"<<endl;
624 removeId(node->id());
625 parent->delChildNode(node,
false);
628bool Project::canIndentTask(Node* node)
635 if (node->type() == Node::Type_Project) {
640 Node* parentNode = node->getParent();
644 if (parentNode->findChildNode(node) == -1) {
645 kdError()<<k_funcinfo<<
"Tasknot found???"<<endl;
648 Node *sib = node->siblingBefore();
653 if (node->findParentRelation(sib) || node->findChildRelation(sib)) {
660bool Project::indentTask( Node* node )
662 if (canIndentTask(node)) {
663 Node *newParent = node->siblingBefore();
664 node->getParent()->delChildNode(node,
false);
665 newParent->addChildNode(node);
671bool Project::canUnindentTask( Node* node )
678 if ( Node::Type_Project == node->type() ) {
684 Node* parentNode = node->getParent();
688 Node* grandParentNode = parentNode->getParent();
689 if ( !grandParentNode ) {
693 int index = parentNode->findChildNode( node );
695 kdError()<<k_funcinfo<<
"Tasknot found???"<<endl;
701bool Project::unindentTask( Node* node )
703 if (canUnindentTask(node)) {
704 Node *parentNode = node->getParent();
705 Node *grandParentNode = parentNode->getParent();
706 parentNode->delChildNode(node,
false);
707 grandParentNode->addChildNode(node,parentNode);
713bool Project::canMoveTaskUp( Node* node )
718 Node* parentNode = node->getParent();
723 if (parentNode->findChildNode(node) == -1) {
724 kdError()<<k_funcinfo<<
"Tasknot found???"<<endl;
727 if (node->siblingBefore()) {
733bool Project::moveTaskUp( Node* node )
735 if (canMoveTaskUp(node)) {
736 return node->getParent()->moveChildUp(node);
741bool Project::canMoveTaskDown( Node* node )
746 Node* parentNode = node->getParent();
750 if (parentNode->findChildNode(node) == -1) {
751 kdError()<<k_funcinfo<<
"Tasknot found???"<<endl;
754 if (node->siblingAfter()) {
760bool Project::moveTaskDown( Node* node )
762 if (canMoveTaskDown(node)) {
763 return node->getParent()->moveChildDown(node);
768Task *Project::createTask(Node* parent) {
769 Task* node =
new Task(parent);
770 node->setId(uniqueNodeId());
774Task *Project::createTask(Task &def, Node* parent) {
775 Task* node =
new Task(def, parent);
776 node->setId(uniqueNodeId());
780TQString Project::uniqueNodeId(
int seed) {
782 while (findNode(TQString(
"%1").arg(i))) {
785 return TQString(
"%1").arg(i);
788bool Project::removeId(
const TQString &
id) {
789 kdDebug()<<k_funcinfo<<
"id="<<
id<<endl;
790 return (m_parent ? m_parent->removeId(
id) : nodeIdDict.remove(
id));
793void Project::insertId(
const TQString &
id,
const Node *node) {
794 kdDebug()<<k_funcinfo<<
"id="<<
id<<
" "<<node->name()<<endl;
795 m_parent ? m_parent->insertId(
id, node) : nodeIdDict.insert(
id, node);
798bool Project::registerNodeId(
Node *node) {
799 if (node->id().isEmpty()) {
800 kdError()<<k_funcinfo<<
"Id is empty."<<endl;
803 Node *rn = findNode(node->id());
805 insertId(node->id(), node);
809 kdError()<<k_funcinfo<<
"Id allready exists for different task: "<<node->id()<<endl;
817 return findResourceGroup(
id);
821 return findResource(
id);
825EffortCostMap Project::plannedEffortCostPrDay(
const TQDate &,
const TQDate &)
const {
836 TQPtrListIterator<Node> it(childNodeIterator());
837 for (; it.current(); ++it) {
838 eff += it.current()->plannedEffort();
844Duration Project::plannedEffort(
const TQDate &date) {
847 TQPtrListIterator<Node> it(childNodeIterator());
848 for (; it.current(); ++it) {
849 eff += it.current()->plannedEffort(date);
855Duration Project::plannedEffortTo(
const TQDate &date) {
858 TQPtrListIterator<Node> it(childNodeIterator());
859 for (; it.current(); ++it) {
860 eff += it.current()->plannedEffortTo(date);
869 TQPtrListIterator<Node> it(childNodeIterator());
870 for (; it.current(); ++it) {
871 eff += it.current()->actualEffort();
877Duration Project::actualEffort(
const TQDate &date) {
880 TQPtrListIterator<Node> it(childNodeIterator());
881 for (; it.current(); ++it) {
882 eff += it.current()->actualEffort(date);
888Duration Project::actualEffortTo(
const TQDate &date) {
891 TQPtrListIterator<Node> it(childNodeIterator());
892 for (; it.current(); ++it) {
893 eff += it.current()->actualEffortTo(date);
898double Project::plannedCost() {
901 TQPtrListIterator<Node> it(childNodeIterator());
902 for (; it.current(); ++it) {
903 c += it.current()->plannedCost();
909double Project::plannedCost(
const TQDate &date) {
912 TQPtrListIterator<Node> it(childNodeIterator());
913 for (; it.current(); ++it) {
914 c += it.current()->plannedCost(date);
920double Project::plannedCostTo(
const TQDate &date) {
923 TQPtrListIterator<Node> it(childNodeIterator());
924 for (; it.current(); ++it) {
925 c += it.current()->plannedCostTo(date);
930double Project::actualCost() {
933 TQPtrListIterator<Node> it(childNodeIterator());
934 for (; it.current(); ++it) {
935 c += it.current()->actualCost();
941double Project::actualCost(
const TQDate &date) {
944 TQPtrListIterator<Node> it(childNodeIterator());
945 for (; it.current(); ++it) {
946 c += it.current()->actualCost(date);
952double Project::actualCostTo(
const TQDate &date) {
955 TQPtrListIterator<Node> it(childNodeIterator());
956 for (; it.current(); ++it) {
957 c += it.current()->actualCostTo(date);
962void Project::addCalendar(
Calendar *calendar) {
964 m_calendars.append(calendar);
967Calendar *Project::calendar(
const TQString
id)
const {
968 return findCalendar(
id);
971TQPtrList<Calendar> Project::calendars() {
972 TQPtrList<Calendar> list;
973 TQPtrListIterator<Calendar> it = m_calendars;
974 for (; it.current(); ++it) {
975 if (!it.current()->isDeleted()) {
976 list.append(it.current());
982void Project::setStandardWorktime(StandardWorktime * worktime) {
983 if (m_standardWorktime != worktime) {
984 delete m_standardWorktime;
985 m_standardWorktime = worktime;
992 if (!child || par->isDependChildOf(child)) {
997 if (par->isParentOf(child) || child->isParentOf(par)) {
1001 legal = legalChildren(par, child);
1003 legal = legalParents(par, child);
1008bool Project::legalParents(
Node *par,
Node *child) {
1011 for (
int i=0; i < par->numDependParentNodes() && legal; ++i) {
1012 Node *pNode = par->getDependParentNode(i)->
parent();
1013 if (child->isParentOf(pNode) || pNode->isParentOf(child)) {
1017 legal = legalChildren(pNode, child);
1020 legal = legalParents(pNode, child);
1025bool Project::legalChildren(Node *par, Node *child) {
1028 for (
int j=0; j < child->numDependChildNodes() && legal; ++j) {
1029 Node *cNode = child->getDependChildNode(j)->child();
1030 if (par->isParentOf(cNode) || cNode->isParentOf(par)) {
1034 legal = legalChildren(par, cNode);
1040void Project::generateWBS(
int count, WBSDefinition &def, TQString wbs) {
1041 if (type() == Type_Subproject || def.level0Enabled()) {
1042 Node::generateWBS(count, def, wbs);
1044 TQPtrListIterator<Node> it = m_nodes;
1045 for (
int i=0; it.current(); ++it) {
1046 it.current()->generateWBS(++i, def, m_wbs);
1051void Project::setCurrentSchedule(
long id) {
1052 setCurrentSchedulePtr(findSchedule(
id));
1053 Node::setCurrentSchedule(
id);
1054 TQDictIterator<Resource> it = resourceIdDict;
1055 for (; it.current(); ++it) {
1056 it.current()->setCurrentSchedule(
id);
1063 while (m_schedules.find(i)) {
1071bool Project::removeCalendarId(
const TQString &
id) {
1072 kdDebug()<<k_funcinfo<<
"id="<<
id<<endl;
1073 return calendarIdDict.remove(
id);
1076void Project::insertCalendarId(
const TQString &
id,
const Calendar *calendar) {
1077 kdDebug()<<k_funcinfo<<
"id="<<
id<<
": "<<calendar->name()<<endl;
1078 calendarIdDict.insert(
id, calendar);
1082void Project::printDebug(
bool children, TQCString indent) {
1084 kdDebug()<<indent<<
"+ Project node: "<<name()<<endl;
1086 TQPtrListIterator<ResourceGroup> it(resourceGroups());
1087 for ( ; it.current(); ++it)
1088 it.current()->printDebug(indent);
1090 Node::printDebug(children, indent);
1092void Project::printCalendarDebug(TQCString indent) {
1093 kdDebug()<<indent<<
"-------- Calendars debug printout --------"<<endl;
1094 TQPtrListIterator<Calendar> it = m_calendars;
1095 for (; it.current(); ++it) {
1096 it.current()->printDebug(indent +
"--");
1099 if (m_standardWorktime)
1100 m_standardWorktime->printDebug();
Calendar defines the working and nonworking days and hours.
DateTime is a TQDateTime which knows about Duration.
The duration class can be used to store a timespan in a convenient format.
MainSchedule holds scheduling information for the main project node.
This class represents any node in the project, a node can be a project or a subproject or any task.
This class represents a group of similar resources to be assigned to a task e.g.
Any resource that is used by a task.
The Schedule class holds data calculated during project calculation and scheduling,...