1 /* This file is part of the KDE project
2 Copyright (C) 2001 Thomas zander <zander@kde.org>
3 Copyright (C) 2002 - 2010, 2012 Dag Andersen <danders@get2net.dk>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20 // clazy:excludeall=qstring-arg
21 #include "kptnode.h"
22
23 #include "kptappointment.h"
24 #include "kptaccount.h"
25 #include "kptwbsdefinition.h"
26 #include "kptresource.h"
27 #include "kptschedule.h"
28 #include "kptxmlloaderobject.h"
29 #include "XmlSaveContext.h"
30 #include "kptdebug.h"
31
32 #include <KoXmlReader.h>
33
34 #include <KLocalizedString>
35
36 #include <QListIterator>
37
38
39 namespace KPlato
40 {
41
Node(Node * parent)42 Node::Node(Node *parent)
43 : QObject(0), // We don't use qobjects parent
44 m_nodes(), m_dependChildNodes(), m_dependParentNodes(),
45 m_estimate(0),
46 m_blockChanged(false)
47 {
48 //debugPlan<<"("<<this<<")";
49 m_parent = parent;
50 init();
51 m_id.clear(); // Not mapped
52 }
53
Node(const Node & node,Node * parent)54 Node::Node(const Node &node, Node *parent)
55 : QObject(0), // Don't set parent, we handle parent/child ourselves
56 m_nodes(),
57 m_dependChildNodes(),
58 m_dependParentNodes(),
59 m_estimate(0),
60 m_blockChanged(false)
61 {
62 //debugPlan<<"("<<this<<")";
63 m_parent = parent;
64 init();
65 m_name = node.name();
66 m_leader = node.leader();
67 m_description = node.description();
68 m_constraint = (ConstraintType) node.constraint();
69 m_constraintStartTime = node.constraintStartTime();
70 m_constraintEndTime = node.constraintEndTime();
71
72 m_runningAccount = node.runningAccount();
73 m_startupAccount = node.startupAccount();
74 m_shutdownAccount = node.shutdownAccount();
75
76 m_startupCost = node.startupCost();
77 m_shutdownCost = node.shutdownCost();
78
79 setObjectName(node.objectName());
80 }
81
~Node()82 Node::~Node() {
83 //debugPlan<<"("<<this<<")"<<m_name;
84 delete m_estimate;
85 while (!m_nodes.isEmpty())
86 delete m_nodes.takeFirst();
87
88 if (findNode() == this) {
89 removeId(); // only remove myself (I may be just a working copy)
90 }
91 while (!m_dependParentNodes.isEmpty()) {
92 delete m_dependParentNodes.value(0);
93 }
94 while (!m_dependChildNodes.isEmpty()) {
95 delete m_dependChildNodes.value(0);
96 }
97 if (m_runningAccount)
98 m_runningAccount->removeRunning(*this);
99 if (m_startupAccount)
100 m_startupAccount->removeStartup(*this);
101 if (m_shutdownAccount)
102 m_shutdownAccount->removeShutdown(*this);
103
104 foreach (Schedule *s, m_schedules) {
105 delete s;
106 }
107 m_schedules.clear();
108 m_parent = 0; //safety
109 }
110
init()111 void Node::init() {
112 m_priority = 0;
113 m_documents.node = this;
114 m_currentSchedule = 0;
115 m_name="";
116 m_constraint = Node::ASAP;
117 m_estimate = 0;
118 m_visitedForward = false;
119 m_visitedBackward = false;
120
121 m_runningAccount = 0;
122 m_startupAccount = 0;
123 m_shutdownAccount = 0;
124 m_startupCost = 0.0;
125 m_shutdownCost = 0.0;
126 }
127
setPriority(int priority)128 void Node::setPriority(int priority)
129 {
130 m_priority = priority;
131 changed(PriorityProperty);
132 }
133
priority() const134 int Node::priority() const
135 {
136 return m_priority;
137 }
138
typeToString(bool trans) const139 QString Node::typeToString(bool trans) const
140 {
141 return typeToString((Node::NodeTypes)type(), trans);
142 }
143
144 // static
typeToString(Node::NodeTypes typ,bool trans)145 QString Node::typeToString(Node::NodeTypes typ, bool trans)
146 {
147 return typeToStringList(trans).at(typ);
148 }
149
150 // static
typeToStringList(bool trans)151 QStringList Node::typeToStringList(bool trans)
152 {
153 return QStringList()
154 << (trans ? i18n("None") : QString("None"))
155 << (trans ? i18n("Project") : QString("Project"))
156 << (trans ? i18n("Sub-Project") : QString("Sub-Project"))
157 << (trans ? i18n("Task") : QString("Task"))
158 << (trans ? i18n("Milestone") : QString("Milestone"))
159 << (trans ? i18n("Periodic") : QString("Periodic"))
160 << (trans ? i18n("Summary") : QString("Summary-Task"));
161 }
162
setName(const QString & n)163 void Node::setName(const QString &n)
164 {
165 #ifndef NDEBUG
166 setObjectName(n);
167 #endif
168 m_name = n;
169 changed(this, NameProperty);
170 }
171
setLeader(const QString & l)172 void Node::setLeader(const QString &l)
173 {
174 m_leader = l;
175 changed(this, LeaderProperty);
176 }
177
setDescription(const QString & d)178 void Node::setDescription(const QString &d)
179 {
180 m_description = d;
181 changed(this, DescriptionProperty);
182 }
183
projectNode()184 Node *Node::projectNode() {
185 if ((type() == Type_Project) || (type() == Type_Subproject)) {
186 return this;
187 }
188 if (m_parent)
189 return m_parent->projectNode();
190
191 // This happens for default tasks
192 return 0;
193 }
194
projectNode() const195 const Node *Node::projectNode() const {
196 if ((type() == Type_Project) || (type() == Type_Subproject)) {
197 return this;
198 }
199 if (m_parent)
200 return m_parent->projectNode();
201
202 // This happens for default tasks
203 return 0;
204 }
205
takeChildNode(Node * node)206 void Node::takeChildNode(Node *node) {
207 //debugPlan<<"find="<<m_nodes.indexOf(node);
208 int t = type();
209 int i = m_nodes.indexOf(node);
210 if (i != -1) {
211 m_nodes.removeAt(i);
212 }
213 node->setParentNode(0);
214 if (t != type()) {
215 changed(TypeProperty);
216 }
217 }
218
takeChildNode(int number)219 void Node::takeChildNode(int number) {
220 int t = type();
221 if (number >= 0 && number < m_nodes.size()) {
222 Node *n = m_nodes.takeAt(number);
223 //debugPlan<<(n?n->id():"null")<<" :"<<(n?n->name():"");
224 if (n) {
225 n->setParentNode(0);
226 }
227 }
228 if (t != type()) {
229 changed(TypeProperty);
230 }
231 }
232
insertChildNode(int index,Node * node)233 void Node::insertChildNode(int index, Node *node) {
234 int t = type();
235 if (index == -1)
236 m_nodes.append(node);
237 else
238 m_nodes.insert(index,node);
239 node->setParentNode(this);
240 if (t != type()) {
241 changed(TypeProperty);
242 }
243 }
244
addChildNode(Node * node,Node * after)245 void Node::addChildNode(Node *node, Node *after) {
246 int t = type();
247 int index = m_nodes.indexOf(after);
248 if (index == -1) {
249 m_nodes.append(node);
250 node->setParentNode(this);
251 if (t != type()) {
252 changed(TypeProperty);
253 }
254 return;
255 }
256 m_nodes.insert(index+1, node);
257 node->setParentNode(this);
258 if (t != type()) {
259 changed(TypeProperty);
260 }
261 }
262
findChildNode(const Node * node) const263 int Node::findChildNode(const Node* node) const
264 {
265 return m_nodes.indexOf(const_cast<Node*>(node));
266 }
267
isChildOf(const Node * node) const268 bool Node::isChildOf(const Node* node) const
269 {
270 if (node == 0 || m_parent == 0) {
271 return false;
272 }
273 if (node == m_parent) {
274 return true;
275 }
276 return m_parent->isChildOf(node);
277 }
278
279
childNode(int number)280 Node* Node::childNode(int number)
281 {
282 //debugPlan<<number;
283 return m_nodes.value(number);
284 }
285
childNode(int number) const286 const Node* Node::childNode(int number) const
287 {
288 if (number < 0 || number >= m_nodes.count()) {
289 return 0;
290 }
291 return m_nodes.at(number);
292 }
293
indexOf(const Node * node) const294 int Node::indexOf(const Node *node) const
295 {
296 return m_nodes.indexOf(const_cast<Node*>(node));
297 }
298
299
getDelay()300 Duration *Node::getDelay() {
301 /* TODO
302 Calculate the delay of this node. Use the calculated startTime and the set startTime.
303 */
304 return 0L;
305 }
306
addDependChildNode(Node * node,Relation::Type p)307 void Node::addDependChildNode(Node *node, Relation::Type p) {
308 addDependChildNode(node,p,Duration());
309 }
310
addDependChildNode(Node * node,Relation::Type p,Duration lag)311 void Node::addDependChildNode(Node *node, Relation::Type p, Duration lag) {
312 Relation *relation = new Relation(this, node, p, lag);
313 if (node->addDependParentNode(relation))
314 m_dependChildNodes.append(relation);
315 else
316 delete relation;
317 }
318
insertDependChildNode(unsigned int index,Node * node,Relation::Type p)319 void Node::insertDependChildNode(unsigned int index, Node *node, Relation::Type p) {
320 Relation *relation = new Relation(this, node, p, Duration());
321 if (node->addDependParentNode(relation))
322 m_dependChildNodes.insert(index, relation);
323 else
324 delete relation;
325 }
326
addDependChildNode(Relation * relation)327 bool Node::addDependChildNode(Relation *relation) {
328 if(m_dependChildNodes.indexOf(relation) != -1)
329 return false;
330 m_dependChildNodes.append(relation);
331 return true;
332 }
333
takeDependChildNode(Relation * rel)334 void Node::takeDependChildNode(Relation *rel) {
335 int i = m_dependChildNodes.indexOf(rel);
336 if (i != -1) {
337 //debugPlan<<m_name<<": ("<<rel<<")";
338 m_dependChildNodes.removeAt(i);
339 }
340 }
341
addDependParentNode(Node * node,Relation::Type p)342 void Node::addDependParentNode(Node *node, Relation::Type p) {
343 addDependParentNode(node,p,Duration());
344 }
345
addDependParentNode(Node * node,Relation::Type p,Duration lag)346 void Node::addDependParentNode(Node *node, Relation::Type p, Duration lag) {
347 Relation *relation = new Relation(node, this, p, lag);
348 if (node->addDependChildNode(relation))
349 m_dependParentNodes.append(relation);
350 else
351 delete relation;
352 }
353
insertDependParentNode(unsigned int index,Node * node,Relation::Type p)354 void Node::insertDependParentNode(unsigned int index, Node *node, Relation::Type p) {
355 Relation *relation = new Relation(this, node, p, Duration());
356 if (node->addDependChildNode(relation))
357 m_dependParentNodes.insert(index,relation);
358 else
359 delete relation;
360 }
361
addDependParentNode(Relation * relation)362 bool Node::addDependParentNode(Relation *relation) {
363 if(m_dependParentNodes.indexOf(relation) != -1)
364 return false;
365 m_dependParentNodes.append(relation);
366 return true;
367 }
368
takeDependParentNode(Relation * rel)369 void Node::takeDependParentNode(Relation *rel) {
370 int i = m_dependParentNodes.indexOf(rel);
371 if (i != -1) {
372 //debugPlan<<m_name<<": ("<<rel<<")";
373 m_dependParentNodes.removeAt(i);
374 }
375 }
376
isParentOf(const Node * node) const377 bool Node::isParentOf(const Node *node) const
378 {
379 if (m_nodes.indexOf(const_cast<Node*>(node)) != -1)
380 return true;
381
382 QListIterator<Node*> nit(childNodeIterator());
383 while (nit.hasNext()) {
384 if (nit.next()->isParentOf(node))
385 return true;
386 }
387 return false;
388 }
389
findParentRelation(const Node * node) const390 Relation *Node::findParentRelation(const Node *node) const
391 {
392 for (int i=0; i<numDependParentNodes(); i++) {
393 Relation *rel = getDependParentNode(i);
394 if (rel->parent() == node)
395 return rel;
396 }
397 return (Relation *)0;
398 }
399
findChildRelation(const Node * node) const400 Relation *Node::findChildRelation(const Node *node) const
401 {
402 for (int i=0; i<numDependChildNodes(); i++) {
403 Relation *rel = getDependChildNode(i);
404 if (rel->child() == node)
405 return rel;
406 }
407 return (Relation *)0;
408 }
409
findRelation(const Node * node) const410 Relation *Node::findRelation(const Node *node) const
411 {
412 Relation *rel = findParentRelation(node);
413 if (!rel)
414 rel = findChildRelation(node);
415 return rel;
416 }
417
isDependChildOf(const Node * node) const418 bool Node::isDependChildOf(const Node *node) const
419 {
420 //debugPlan<<" '"<<m_name<<"' checking against '"<<node->name()<<"'";
421 for (int i=0; i<numDependParentNodes(); i++) {
422 Relation *rel = getDependParentNode(i);
423 if (rel->parent() == node)
424 return true;
425 if (rel->parent()->isDependChildOf(node))
426 return true;
427 }
428 return false;
429 }
430
getParentNodes()431 QList<Node*> Node::getParentNodes()
432 {
433 this->m_parentNodes.clear();
434 foreach(Relation * currentRelation, this->dependParentNodes())
435 {
436 if (!this->m_parentNodes.contains(currentRelation->parent()))
437 {
438 this->m_parentNodes.append(currentRelation->parent());
439 }
440 }
441 return this->m_parentNodes;
442 }
443
canMoveTo(const Node * newParent) const444 bool Node::canMoveTo(const Node *newParent) const
445 {
446 if (m_parent == newParent) {
447 return true;
448 }
449 if (newParent->isChildOf(this)) {
450 return false;
451 }
452 if (isDependChildOf(newParent) || newParent->isDependChildOf(this)) {
453 debugPlan<<"Can't move, node is dependent on new parent";
454 return false;
455 }
456 foreach (Node *n, m_nodes) {
457 if (!n->canMoveTo(newParent)) {
458 return false;
459 }
460 }
461 return true;
462 }
463
makeAppointments()464 void Node::makeAppointments() {
465 QListIterator<Node*> nit(m_nodes);
466 while (nit.hasNext()) {
467 nit.next()->makeAppointments();
468 }
469 }
470
calcResourceOverbooked()471 void Node::calcResourceOverbooked() {
472 QListIterator<Node*> nit(m_nodes);
473 while (nit.hasNext()) {
474 nit.next()->calcResourceOverbooked();
475 }
476 }
477
478 // Returns the (previously) calculated duration
duration(long id) const479 Duration Node::duration(long id) const
480 {
481 Schedule *s = schedule(id);
482 return s ? s->duration : Duration::zeroDuration;
483 }
484
variance(long id,Duration::Unit unit) const485 double Node::variance(long id, Duration::Unit unit) const
486 {
487 double d = deviation(id, unit);
488 return d * d;
489 }
490
deviation(long id,Duration::Unit unit) const491 double Node::deviation(long id, Duration::Unit unit) const
492 {
493 Schedule *s = schedule(id);
494 double d = 0.0;
495 if (s && m_estimate) {
496 d = s->duration.toDouble(unit);
497 double o = (d * (100 + m_estimate->optimisticRatio())) / 100;
498 double p = (d * (100 + m_estimate->pessimisticRatio())) / 100;
499 d = (p - o) / 6;
500 }
501 return d;
502 }
503
startTime(long id) const504 DateTime Node::startTime(long id) const
505 {
506 Schedule *s = schedule(id);
507 return s ? s->startTime : DateTime();
508 }
endTime(long id) const509 DateTime Node::endTime(long id) const
510 {
511 Schedule *s = schedule(id);
512 return s ? s->endTime : DateTime();
513 }
514
appointmentStartTime(long id) const515 DateTime Node::appointmentStartTime(long id) const
516 {
517 Schedule *s = schedule(id);
518 return s ? s->appointmentStartTime() : DateTime();
519 }
appointmentEndTime(long id) const520 DateTime Node::appointmentEndTime(long id) const
521 {
522 Schedule *s = schedule(id);
523 return s ? s->appointmentEndTime() : DateTime();
524 }
525
setDuration(const Duration & duration,long id)526 void Node::setDuration(const Duration &duration, long id)
527 {
528 Schedule *s = schedule(id);
529 if (s) {
530 s->duration = duration;
531 }
532 }
533
setEarlyStart(const DateTime & dt,long id)534 void Node::setEarlyStart(const DateTime &dt, long id)
535 {
536 Schedule *s = schedule(id);
537 if (s) s->earlyStart = dt;
538 }
539
earlyStart(long id) const540 DateTime Node::earlyStart(long id) const
541 {
542 Schedule *s = schedule(id);
543 return s ? s->earlyStart : DateTime();
544 }
545
setLateStart(const DateTime & dt,long id)546 void Node::setLateStart(const DateTime &dt, long id)
547 {
548 Schedule *s = schedule(id);
549 if (s) s->lateStart = dt;
550 }
551
lateStart(long id) const552 DateTime Node::lateStart(long id) const
553 {
554 Schedule *s = schedule(id);
555 return s ? s->lateStart : DateTime();
556 }
557
setEarlyFinish(const DateTime & dt,long id)558 void Node::setEarlyFinish(const DateTime &dt, long id)
559 {
560 Schedule *s = schedule(id);
561 if (s) s->earlyFinish = dt;
562 }
563
earlyFinish(long id) const564 DateTime Node::earlyFinish(long id) const
565 {
566 Schedule *s = schedule(id);
567 return s ? s->earlyFinish : DateTime();
568 }
569
setLateFinish(const DateTime & dt,long id)570 void Node::setLateFinish(const DateTime &dt, long id)
571 {
572 Schedule *s = schedule(id);
573 if (s) s->lateFinish = dt;
574 }
575
lateFinish(long id) const576 DateTime Node::lateFinish(long id) const
577 {
578 Schedule *s = schedule(id);
579 return s ? s->lateFinish : DateTime();
580 }
581
workStartTime(long id) const582 DateTime Node::workStartTime(long id) const
583 {
584 Schedule *s = schedule(id);
585 return s ? s->workStartTime : DateTime();
586 }
587
setWorkStartTime(const DateTime & dt,long id)588 void Node::setWorkStartTime(const DateTime &dt, long id)
589 {
590 Schedule *s = schedule(id);
591 if (s) s->workStartTime = dt;
592 }
593
workEndTime(long id) const594 DateTime Node::workEndTime(long id) const
595 {
596 Schedule *s = schedule(id);
597 return s ? s->workEndTime : DateTime();
598 }
599
setWorkEndTime(const DateTime & dt,long id)600 void Node::setWorkEndTime(const DateTime &dt, long id)
601 {
602 Schedule *s = schedule(id);
603 if (s) s->workEndTime = dt;
604 }
605
inCriticalPath(long id) const606 bool Node::inCriticalPath(long id) const
607 {
608 Schedule *s = schedule(id);
609 return s ? s->inCriticalPath : false;
610 }
611
schedulingStatus(long id,bool trans) const612 QStringList Node::schedulingStatus(long id, bool trans) const
613 {
614 Schedule *s = schedule(id);
615 QStringList lst;
616 if (s) {
617 lst = s->state();
618 }
619 if (lst.isEmpty()) {
620 lst.append(trans ? i18n("Not scheduled") : QString("Not scheduled"));
621 }
622 return lst;
623 }
624
resourceError(long id) const625 bool Node::resourceError(long id) const
626 {
627 Schedule *s = schedule(id);
628 return s ? s->resourceError : false;
629 }
630
resourceOverbooked(long id) const631 bool Node::resourceOverbooked(long id) const
632 {
633 Schedule *s = schedule(id);
634 return s ? s->resourceOverbooked : false;
635 }
636
resourceNotAvailable(long id) const637 bool Node::resourceNotAvailable(long id) const
638 {
639 Schedule *s = schedule(id);
640 return s ? s->resourceNotAvailable : false;
641 }
642
constraintError(long id) const643 bool Node::constraintError(long id) const
644 {
645 Schedule *s = schedule(id);
646 return s ? s->constraintError : false;
647 }
648
schedulingError(long id) const649 bool Node::schedulingError(long id) const
650 {
651 Schedule *s = schedule(id);
652 return s ? s->schedulingError : false;
653 }
654
notScheduled(long id) const655 bool Node::notScheduled(long id) const
656 {
657 if (type() == Type_Summarytask) {
658 // i am scheduled if al least on child is scheduled
659 foreach (Node *n, m_nodes) {
660 if (! n->notScheduled(id)) {
661 return false;
662 }
663 }
664 return true;
665 }
666 Schedule *s = schedule(id);
667 return s == 0 || s->isDeleted() || s->notScheduled;
668 }
669
overbookedResources(long id) const670 QStringList Node::overbookedResources(long id) const
671 {
672 Schedule *s = schedule(id);
673 return s ? s->overbookedResources() : QStringList();
674 }
675
saveWorkPackageXML(QDomElement &,long) const676 void Node::saveWorkPackageXML(QDomElement &, long) const
677 {
678 return;
679 }
680
saveRelations(QDomElement & element,const XmlSaveContext & context) const681 void Node::saveRelations(QDomElement &element, const XmlSaveContext &context) const
682 {
683 QListIterator<Relation*> it(m_dependChildNodes);
684 while (it.hasNext()) {
685 Relation *r = it.next();
686 if (context.saveNode(r->child())) {
687 r->save(element, context);
688 }
689 }
690 QListIterator<Node*> nodes(m_nodes);
691 while (nodes.hasNext()) {
692 nodes.next()->saveRelations(element, context);
693 }
694 }
695
setConstraint(Node::ConstraintType type)696 void Node::setConstraint(Node::ConstraintType type)
697 {
698 m_constraint = type;
699 changed(this, ConstraintTypeProperty);
700 }
701
setConstraint(const QString & type)702 void Node::setConstraint(const QString &type) {
703 // Do not i18n these, they are used in load()
704 if (type == "ASAP")
705 setConstraint(ASAP);
706 else if (type == "ALAP")
707 setConstraint(ALAP);
708 else if (type == "MustStartOn")
709 setConstraint(MustStartOn);
710 else if (type == "MustFinishOn")
711 setConstraint(MustFinishOn);
712 else if (type == "StartNotEarlier")
713 setConstraint(StartNotEarlier);
714 else if (type == "FinishNotLater")
715 setConstraint(FinishNotLater);
716 else if (type == "FixedInterval")
717 setConstraint(FixedInterval);
718 else
719 setConstraint(ASAP); // default
720 }
721
constraintToString(bool trans) const722 QString Node::constraintToString(bool trans) const {
723 return constraintList(trans).at(m_constraint);
724 }
725
constraintList(bool trans)726 QStringList Node::constraintList(bool trans) {
727 // keep theses in the same order as the enum!
728 return QStringList()
729 << (trans ? i18n("As Soon As Possible") : QString("ASAP"))
730 << (trans ? i18n("As Late As Possible") : QString("ALAP"))
731 << (trans ? i18n("Must Start On") : QString("MustStartOn"))
732 << (trans ? i18n("Must Finish On") : QString("MustFinishOn"))
733 << (trans ? i18n("Start Not Earlier") : QString("StartNotEarlier"))
734 << (trans ? i18n("Finish Not Later") : QString("FinishNotLater"))
735 << (trans ? i18n("Fixed Interval") : QString("FixedInterval"));
736 }
737
propagateEarliestStart(DateTime & time)738 void Node::propagateEarliestStart(DateTime &time) {
739 if (m_currentSchedule == 0) {
740 return;
741 }
742 if (type() != Type_Project) {
743 m_currentSchedule->earlyStart = time;
744 if (m_currentSchedule->lateStart.isValid() && m_currentSchedule->lateStart < time) {
745 m_currentSchedule->lateStart = time;
746 }
747 //m_currentSchedule->logDebug("propagateEarliestStart: " + time.toString());
748 switch (m_constraint) {
749 case FinishNotLater:
750 case MustFinishOn:
751 if (m_constraintEndTime < time) {
752 m_currentSchedule->logWarning("Task constraint outside project constraint");
753 #ifndef PLAN_NLOGDEBUG
754 m_currentSchedule->logDebug(QString("%1: end constraint %2 < %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString()));
755 #endif
756 }
757 break;
758 case MustStartOn:
759 case FixedInterval:
760 if (m_constraintStartTime < time) {
761 m_currentSchedule->logWarning("Task constraint outside project constraint");
762 #ifndef PLAN_NLOGDEBUG
763 m_currentSchedule->logDebug(QString("%1: start constraint %2 < %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString()));
764 #endif
765 }
766 break;
767 default:
768 break;
769 }
770 }
771 //debugPlan<<m_name<<":"<<m_currentSchedule->earlyStart;
772 QListIterator<Node*> it = m_nodes;
773 while (it.hasNext()) {
774 it.next()->propagateEarliestStart(time);
775 }
776 }
777
propagateLatestFinish(DateTime & time)778 void Node::propagateLatestFinish(DateTime &time) {
779 if (m_currentSchedule == 0) {
780 return;
781 }
782 if (type() != Type_Project) {
783 m_currentSchedule->lateFinish = time;
784 if (m_currentSchedule->earlyFinish.isValid() && m_currentSchedule->earlyFinish > time) {
785 m_currentSchedule->earlyFinish = time;
786 }
787 switch (m_constraint) {
788 case StartNotEarlier:
789 case MustStartOn:
790 if (m_constraintStartTime > time) {
791 m_currentSchedule->logWarning("Task constraint outside project constraint");
792 #ifndef PLAN_NLOGDEBUG
793 m_currentSchedule->logDebug(QString("%1: start constraint %2 < %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString()));
794 #endif
795 }
796 break;
797 case MustFinishOn:
798 case FixedInterval:
799 if (m_constraintEndTime > time) {
800 m_currentSchedule->logWarning("Task constraint outside project constraint");
801 #ifndef PLAN_NLOGDEBUG
802 m_currentSchedule->logDebug(QString("%1: end constraint %2 > %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString()));
803 #endif
804 }
805 break;
806 default:
807 break;
808 }
809 }
810 //debugPlan<<m_name<<":"<<m_currentSchedule->lateFinish;
811 QListIterator<Node*> it = m_nodes;
812 while (it.hasNext()) {
813 it.next()->propagateLatestFinish(time);
814 }
815 }
816
moveEarliestStart(DateTime & time)817 void Node::moveEarliestStart(DateTime &time) {
818 if (m_currentSchedule == 0)
819 return;
820 if (m_currentSchedule->earlyStart < time) {
821 //m_currentSchedule->logDebug("moveEarliestStart: " + m_currentSchedule->earlyStart.toString() + " -> " + time.toString());
822 m_currentSchedule->earlyStart = time;
823 }
824 QListIterator<Node*> it = m_nodes;
825 while (it.hasNext()) {
826 it.next()->moveEarliestStart(time);
827 }
828 }
829
moveLatestFinish(DateTime & time)830 void Node::moveLatestFinish(DateTime &time) {
831 if (m_currentSchedule == 0)
832 return;
833 if (m_currentSchedule->lateFinish > time)
834 m_currentSchedule->lateFinish = time;
835 QListIterator<Node*> it = m_nodes;
836 while (it.hasNext()) {
837 it.next()->moveLatestFinish(time);
838 }
839 }
840
initiateCalculation(MainSchedule & sch)841 void Node::initiateCalculation(MainSchedule &sch) {
842 m_visitedForward = false;
843 m_visitedBackward = false;
844 m_durationForward = Duration::zeroDuration;
845 m_durationBackward = Duration::zeroDuration;
846 m_earlyStart = DateTime();
847 m_earlyFinish = DateTime();
848 m_lateFinish = DateTime();
849
850 QListIterator<Node*> it = m_nodes;
851 while (it.hasNext()) {
852 it.next()->initiateCalculation(sch);
853 }
854 }
855
resetVisited()856 void Node::resetVisited() {
857 m_visitedForward = false;
858 m_visitedBackward = false;
859 QListIterator<Node*> it = m_nodes;
860 while (it.hasNext()) {
861 it.next()->resetVisited();
862 }
863 }
864
siblingBefore()865 Node *Node::siblingBefore() {
866 //debugPlan;
867 if (parentNode())
868 return parentNode()->childBefore(this);
869 return 0;
870 }
871
childBefore(Node * node)872 Node *Node::childBefore(Node *node) {
873 //debugPlan;
874 int index = m_nodes.indexOf(node);
875 if (index > 0){
876 return m_nodes.at(index-1);
877 }
878 return 0;
879 }
880
siblingAfter()881 Node *Node::siblingAfter() {
882 //debugPlan;
883 if (parentNode())
884 return parentNode()->childAfter(this);
885 return 0;
886 }
887
childAfter(Node * node)888 Node *Node::childAfter(Node *node)
889 {
890 //debugPlan;
891 Q_ASSERT(m_nodes.contains(node));
892 int index = m_nodes.indexOf(node);
893 if (index < m_nodes.count()-1) {
894 return m_nodes.at(index+1);
895 }
896 return 0;
897 }
898
moveChildUp(Node * node)899 bool Node::moveChildUp(Node* node)
900 {
901 if (findChildNode(node) == -1)
902 return false; // not my node!
903 Node *sib = node->siblingBefore();
904 if (!sib)
905 return false;
906 sib = sib->siblingBefore();
907 takeChildNode(node);
908 if (sib) {
909 addChildNode(node, sib);
910 } else {
911 insertChildNode(0, node);
912 }
913 return true;
914 }
915
moveChildDown(Node * node)916 bool Node::moveChildDown(Node* node)
917 {
918 if (findChildNode(node) == -1)
919 return false; // not my node!
920 Node *sib = node->siblingAfter();
921 if (!sib)
922 return false;
923 takeChildNode(node);
924 addChildNode(node, sib);
925 return true;
926 }
927
legalToLink(const Node * node) const928 bool Node::legalToLink(const Node *node) const
929 {
930 Node *p = const_cast<Node*>(this)->projectNode();
931 if (p)
932 return p->legalToLink(this, node);
933 return false;
934 }
935
isEndNode() const936 bool Node::isEndNode() const {
937 return m_dependChildNodes.isEmpty();
938 }
isStartNode() const939 bool Node::isStartNode() const {
940 return m_dependParentNodes.isEmpty();
941 }
942
setId(const QString & id)943 void Node::setId(const QString& id) {
944 //debugPlan<<id;
945 m_id = id;
946 }
947
setStartTime(const DateTime & startTime,long id)948 void Node::setStartTime(const DateTime &startTime, long id)
949 {
950 Schedule *s = schedule(id);
951 if (s)
952 s->startTime = startTime;
953 }
954
setEndTime(const DateTime & endTime,long id)955 void Node::setEndTime(const DateTime &endTime, long id)
956 {
957 Schedule *s = schedule(id);
958 if (s)
959 s->endTime = endTime;
960 }
961
saveAppointments(QDomElement & element,long id) const962 void Node::saveAppointments(QDomElement &element, long id) const {
963 //debugPlan<<m_name<<" id="<<id;
964 QListIterator<Node*> it(m_nodes);
965 while (it.hasNext()) {
966 it.next()->saveAppointments(element, id);
967 }
968 }
969
appointments(long id)970 QList<Appointment*> Node::appointments(long id)
971 {
972 Schedule *s = schedule(id);
973 QList<Appointment*> lst;
974 if (s)
975 lst = s->appointments();
976 return lst;
977 }
978
assignedResources(long id) const979 QList<Resource*> Node::assignedResources(long id) const {
980 Schedule *s = schedule(id);
981 QList<Resource*> res;
982 if (s) {
983 foreach (Appointment *a, s->appointments()) {
984 res << a->resource()->resource();
985 }
986 }
987 return res;
988 }
989
990
991 // Appointment *Node::findAppointment(Resource *resource) {
992 // if (m_currentSchedule)
993 // return m_currentSchedule->findAppointment(resource);
994 // return 0;
995 // }
996 // bool Node::addAppointment(Appointment *appointment) {
997 // if (m_currentSchedule)
998 // return m_currentSchedule->add(appointment);
999 // return false;
1000 // }
1001 //
1002 // called from resource side when resource adds appointment
addAppointment(Appointment * appointment,Schedule & main)1003 bool Node::addAppointment(Appointment *appointment, Schedule &main) {
1004 Schedule *s = findSchedule(main.id());
1005 if (s == 0) {
1006 s = createSchedule(&main);
1007 }
1008 appointment->setNode(s);
1009 //debugPlan<<this<<":"<<appointment<<","<<s<<","<<s->id()<<","<<main.id();
1010 return s->add(appointment);
1011 }
1012
addAppointment(ResourceSchedule * resource,const DateTime & start,const DateTime & end,double load)1013 void Node::addAppointment(ResourceSchedule *resource, const DateTime &start, const DateTime &end, double load) {
1014 Schedule *node = findSchedule(resource->id());
1015 if (node == 0) {
1016 node = createSchedule(resource->parent());
1017 }
1018 node->setCalculationMode(resource->calculationMode());
1019 node->addAppointment(resource, start, end, load);
1020 }
1021
isBaselined(long id) const1022 bool Node::isBaselined(long id) const
1023 {
1024 Schedule *s = schedule(id);
1025 return s ? s->isBaselined() : false;
1026 }
1027
takeSchedule(const Schedule * schedule)1028 void Node::takeSchedule(const Schedule *schedule) {
1029 if (schedule == 0)
1030 return;
1031 if (m_currentSchedule == schedule)
1032 m_currentSchedule = 0;
1033 m_schedules.take(schedule->id());
1034 }
1035
addSchedule(Schedule * schedule)1036 void Node::addSchedule(Schedule *schedule) {
1037 if (schedule == 0)
1038 return;
1039 m_schedules.insert(schedule->id(), schedule);
1040 }
1041
createSchedule(const QString & name,Schedule::Type type,long id)1042 Schedule *Node::createSchedule(const QString& name, Schedule::Type type, long id) {
1043 //debugPlan<<name<<" type="<<type<<" id="<<(int)id;
1044 NodeSchedule *sch = new NodeSchedule(this, name, type, id);
1045 addSchedule(sch);
1046 return sch;
1047 }
1048
createSchedule(Schedule * parent)1049 Schedule *Node::createSchedule(Schedule *parent) {
1050 //debugPlan<<name<<" type="<<type<<" id="<<(int)id;
1051 NodeSchedule *sch = new NodeSchedule(parent, this);
1052 addSchedule(sch);
1053 return sch;
1054 }
1055
schedule(long id) const1056 Schedule *Node::schedule(long id) const
1057 {
1058 switch (id) {
1059 case ANYSCHEDULED: {
1060 foreach (Schedule *s, m_schedules) {
1061 if (s->isScheduled()) {
1062 return s;
1063 }
1064 }
1065 return 0;
1066 }
1067 case CURRENTSCHEDULE:
1068 return m_currentSchedule;
1069 case NOTSCHEDULED:
1070 return 0;
1071 case BASELINESCHEDULE: {
1072 foreach (Schedule *s, m_schedules) {
1073 if (s->isBaselined()) {
1074 return s;
1075 }
1076 }
1077 return 0;
1078 }
1079 default:
1080 break;
1081 }
1082 return findSchedule(id);
1083 }
1084
findSchedule(long id) const1085 Schedule *Node::findSchedule(long id) const
1086 {
1087 return m_schedules.value(id);
1088 }
1089
findSchedule(const QString & name,const Schedule::Type type)1090 Schedule *Node::findSchedule(const QString &name, const Schedule::Type type) {
1091 QHash<long, Schedule*> it;
1092 foreach (Schedule *sch, it) {
1093 if (!sch->isDeleted() &&
1094 sch->name() == name && sch->type() == type)
1095 return sch;
1096 }
1097 return 0;
1098 }
1099
findSchedule(const QString & name)1100 Schedule *Node::findSchedule(const QString &name) {
1101 foreach (Schedule *sch, m_schedules) {
1102 if (!sch->isDeleted() && sch->name() == name)
1103 return sch;
1104 }
1105 return 0;
1106 }
1107
1108
findSchedule(const Schedule::Type type)1109 Schedule *Node::findSchedule(const Schedule::Type type) {
1110 //debugPlan<<m_name<<" find type="<<type<<" nr="<<m_schedules.count();
1111 QHash<long, Schedule*> hash;
1112 foreach (Schedule *sch, hash) {
1113 if (!sch->isDeleted() && sch->type() == type) {
1114 return sch;
1115 }
1116 }
1117 return 0;
1118 }
1119
setScheduleDeleted(long id,bool on)1120 void Node::setScheduleDeleted(long id, bool on) {
1121 Schedule *ns = findSchedule(id);
1122 if (ns == 0) {
1123 errorPlan<<m_name<<" Could not find schedule with id="<<id;
1124 } else {
1125 ns->setDeleted(on);
1126 }
1127 }
1128
setParentSchedule(Schedule * sch)1129 void Node::setParentSchedule(Schedule *sch) {
1130 Schedule *s = findSchedule(sch->id());
1131 if (s) {
1132 s->setParent(sch);
1133 }
1134 QListIterator<Node*> it = m_nodes;
1135 while (it.hasNext()) {
1136 it.next()->setParentSchedule(sch);
1137 }
1138 }
1139
calcCriticalPath(bool fromEnd)1140 bool Node::calcCriticalPath(bool fromEnd) {
1141 if (m_currentSchedule == 0)
1142 return false;
1143 //debugPlan<<m_name;
1144 if (!isCritical()) {
1145 return false;
1146 }
1147 if (!fromEnd && isStartNode()) {
1148 m_currentSchedule->inCriticalPath = true;
1149 return true;
1150 }
1151 if (fromEnd && isEndNode()) {
1152 m_currentSchedule->inCriticalPath = true;
1153 return true;
1154 }
1155 QListIterator<Relation*> pit(m_dependParentNodes);
1156 while (pit.hasNext()) {
1157 if (pit.next()->parent()->calcCriticalPath(fromEnd)) {
1158 m_currentSchedule->inCriticalPath = true;
1159 }
1160 }
1161 return m_currentSchedule->inCriticalPath;
1162 }
1163
calcFreeFloat()1164 void Node::calcFreeFloat() {
1165 foreach (Node *n, m_nodes) {
1166 n->calcFreeFloat();
1167 }
1168 return;
1169 }
1170
level() const1171 int Node::level() const {
1172 const Node *n = parentNode();
1173 return n ? n->level() + 1 : 0;
1174 }
1175
generateWBSCode(QList<int> & indexes,bool sortable) const1176 QString Node::generateWBSCode(QList<int> &indexes, bool sortable) const {
1177 //debugPlan<<m_name<<indexes;
1178 if (m_parent == 0) {
1179 return QString();
1180 }
1181 indexes.insert(0, m_parent->indexOf(this));
1182 return m_parent->generateWBSCode(indexes, sortable);
1183 }
1184
wbsCode(bool sortable) const1185 QString Node::wbsCode(bool sortable) const {
1186 //debugPlan<<m_name;
1187 QList<int> indexes;
1188 return generateWBSCode(indexes, sortable);
1189 }
1190
isScheduled(long id) const1191 bool Node::isScheduled(long id) const
1192 {
1193 Schedule *s = schedule(id);
1194 return s != 0 && s->isScheduled();
1195 }
1196
setCurrentSchedule(long id)1197 void Node::setCurrentSchedule(long id) {
1198 QListIterator<Node*> it = m_nodes;
1199 while (it.hasNext()) {
1200 it.next()->setCurrentSchedule(id);
1201 }
1202 //debugPlan<<m_name<<" id:"<<id<<"="<<m_currentSchedule;
1203 }
1204
setStartupCost(double cost)1205 void Node::setStartupCost(double cost)
1206 {
1207 m_startupCost = cost;
1208 changed(StartupCostProperty);
1209 }
1210
setStartupAccount(Account * acc)1211 void Node::setStartupAccount(Account *acc)
1212 {
1213 //debugPlan<<m_name<<"="<<acc;
1214 if (m_startupAccount) {
1215 m_startupAccount->removeStartup(*this);
1216 }
1217 m_startupAccount = acc;
1218 changed(StartupAccountProperty);
1219 }
1220
setShutdownCost(double cost)1221 void Node::setShutdownCost(double cost)
1222 {
1223 m_shutdownCost = cost;
1224 changed(ShutdownCostProperty);
1225 }
1226
setShutdownAccount(Account * acc)1227 void Node::setShutdownAccount(Account *acc)
1228 {
1229 //debugPlan<<m_name<<"="<<acc;
1230 if (m_shutdownAccount) {
1231 m_shutdownAccount->removeShutdown(*this);
1232 }
1233 m_shutdownAccount = acc;
1234 changed(ShutdownAccountProperty);
1235 }
1236
setRunningAccount(Account * acc)1237 void Node::setRunningAccount(Account *acc)
1238 {
1239 //debugPlan<<m_name<<"="<<acc;
1240 if (m_runningAccount) {
1241 m_runningAccount->removeRunning(*this);
1242 }
1243 m_runningAccount = acc;
1244 changed(RunningAccountProperty);
1245 }
1246
blockChanged(bool on)1247 void Node::blockChanged(bool on)
1248 {
1249 m_blockChanged = on;
1250 }
1251
changed(Node * node,int property)1252 void Node::changed(Node *node, int property) {
1253 if (m_blockChanged) {
1254 return;
1255 }
1256 switch (property) {
1257 case TypeProperty:
1258 case StartupCostProperty:
1259 case ShutdownCostProperty:
1260 case CompletionEntryProperty:
1261 case CompletionStartedProperty:
1262 case CompletionFinishedProperty:
1263 case CompletionStartTimeProperty:
1264 case CompletionFinishTimeProperty:
1265 case CompletionPercentageProperty:
1266 case CompletionRemainingEffortProperty:
1267 case CompletionActualEffortProperty:
1268 case CompletionUsedEffortProperty:
1269 foreach (Schedule *s, m_schedules) {
1270 s->clearPerformanceCache();
1271 }
1272 break;
1273 default: break;
1274 }
1275 if (m_parent) {
1276 m_parent->changed(node, property);
1277 }
1278 }
1279
plannedEffort(const Resource * resource,long id,EffortCostCalculationType type) const1280 Duration Node::plannedEffort(const Resource *resource, long id, EffortCostCalculationType type) const
1281 {
1282 Duration e;
1283 foreach (Node *n, m_nodes) {
1284 e += n->plannedEffort(resource, id, type);
1285 }
1286 return e;
1287 }
1288
plannedEffort(const Resource * resource,QDate date,long id,EffortCostCalculationType type) const1289 Duration Node::plannedEffort(const Resource *resource, QDate date, long id, EffortCostCalculationType type) const
1290 {
1291 Duration e;
1292 foreach (Node *n, m_nodes) {
1293 e += n->plannedEffort(resource, date, id, type);
1294 }
1295 return e;
1296 }
1297
plannedEffortTo(const Resource * resource,QDate date,long id,EffortCostCalculationType type) const1298 Duration Node::plannedEffortTo(const Resource *resource, QDate date, long id, EffortCostCalculationType type) const
1299 {
1300 Duration e;
1301 foreach (Node *n, m_nodes) {
1302 e += n->plannedEffortTo(resource, date, id, type);
1303 }
1304 return e;
1305 }
1306
plannedCost(long id,EffortCostCalculationType type) const1307 EffortCost Node::plannedCost(long id, EffortCostCalculationType type) const
1308 {
1309 EffortCost ec;
1310 foreach (Node *n, m_nodes) {
1311 ec += n->plannedCost(id, type);
1312 }
1313 return ec;
1314 }
1315
bcwsPrDay(long int id,EffortCostCalculationType type) const1316 EffortCostMap Node::bcwsPrDay(long int id, EffortCostCalculationType type) const
1317 {
1318 return const_cast<Node*>(this)->bcwsPrDay(id, type);
1319 }
1320
bcwsPrDay(long int id,EffortCostCalculationType type)1321 EffortCostMap Node::bcwsPrDay(long int id, EffortCostCalculationType type)
1322 {
1323 Schedule *s = schedule(id);
1324 if (s == 0) {
1325 return EffortCostMap();
1326 }
1327 EffortCostCache &ec = s->bcwsPrDayCache(type);
1328 if (! ec.cached) {
1329 ec.effortcostmap = EffortCostMap();
1330 foreach (Node *n, m_nodes) {
1331 ec.effortcostmap += n->bcwsPrDay(id, type);
1332 }
1333 ec.cached = true;
1334 }
1335 return ec.effortcostmap;
1336 }
1337
bcwpPrDay(long int id,EffortCostCalculationType type) const1338 EffortCostMap Node::bcwpPrDay(long int id, EffortCostCalculationType type) const
1339 {
1340 return const_cast<Node*>(this)->bcwpPrDay(id, type);
1341 }
1342
bcwpPrDay(long int id,EffortCostCalculationType type)1343 EffortCostMap Node::bcwpPrDay(long int id, EffortCostCalculationType type)
1344 {
1345 Schedule *s = schedule(id);
1346 if (s == 0) {
1347 return EffortCostMap();
1348 }
1349 EffortCostCache &ec = s->bcwpPrDayCache(type);
1350 if (! ec.cached) {
1351 ec.effortcostmap = EffortCostMap();
1352 foreach (Node *n, m_nodes) {
1353 ec.effortcostmap += n->bcwpPrDay(id, type);
1354 }
1355 ec.cached = true;
1356 }
1357 return ec.effortcostmap;
1358 }
1359
acwp(long id,EffortCostCalculationType type) const1360 EffortCostMap Node::acwp(long id, EffortCostCalculationType type) const
1361 {
1362 return const_cast<Node*>(this)->acwp(id, type);
1363 }
1364
acwp(long id,EffortCostCalculationType type)1365 EffortCostMap Node::acwp(long id, EffortCostCalculationType type)
1366 {
1367 Schedule *s = schedule(id);
1368 if (s == 0) {
1369 return EffortCostMap();
1370 }
1371 EffortCostCache &ec = s->acwpCache(type);
1372 if (! ec.cached) {
1373 ec.effortcostmap = EffortCostMap();
1374 foreach (Node *n, m_nodes) {
1375 ec.effortcostmap += n->acwp(id, type);
1376 }
1377 ec.cached = true;
1378 }
1379 return ec.effortcostmap;
1380 }
1381
acwp(QDate date,long id) const1382 EffortCost Node::acwp(QDate date, long id) const
1383 {
1384 EffortCost ec;
1385 foreach (Node *n, m_nodes) {
1386 ec += n->acwp(date, id);
1387 }
1388 return ec;
1389 }
1390
slotStandardWorktimeChanged(KPlato::StandardWorktime *)1391 void Node::slotStandardWorktimeChanged(KPlato::StandardWorktime*)
1392 {
1393 //debugPlan<<m_estimate;
1394 if (m_estimate) {
1395 m_estimate->m_expectedCached = false;
1396 m_estimate->m_optimisticCached = false;
1397 m_estimate->m_pessimisticCached = false;
1398 }
1399 }
1400
emitDocumentAdded(Node * node,Document * doc,int idx)1401 void Node::emitDocumentAdded(Node *node, Document *doc, int idx)
1402 {
1403 if (m_parent) {
1404 m_parent->emitDocumentAdded(node, doc, idx);
1405 }
1406 }
1407
emitDocumentRemoved(Node * node,Document * doc,int idx)1408 void Node::emitDocumentRemoved(Node *node, Document *doc, int idx)
1409 {
1410 if (m_parent) {
1411 m_parent->emitDocumentRemoved(node, doc, idx);
1412 }
1413 }
1414
emitDocumentChanged(Node * node,Document * doc,int idx)1415 void Node::emitDocumentChanged(Node *node, Document *doc, int idx)
1416 {
1417 if (m_parent) {
1418 m_parent->emitDocumentChanged(node, doc, idx);
1419 }
1420 }
1421
1422 ////////////////////////// Estimate /////////////////////////////////
1423
Estimate(Node * parent)1424 Estimate::Estimate(Node *parent)
1425 : m_parent(parent)
1426 {
1427 m_pertCached = false;
1428
1429 setUnit(Duration::Unit_h);
1430 setExpectedEstimate(8.0);
1431 setPessimisticEstimate(8.0);
1432 setOptimisticEstimate(8.0);
1433
1434 m_type = Type_Effort;
1435 m_calendar = 0;
1436 m_risktype = Risk_None;
1437 }
1438
Estimate(const Estimate & estimate,Node * parent)1439 Estimate::Estimate(const Estimate &estimate, Node *parent)
1440 : m_parent(parent)
1441 {
1442 copy(estimate);
1443 }
1444
~Estimate()1445 Estimate::~Estimate()
1446 {
1447 }
1448
clear()1449 void Estimate::clear()
1450 {
1451 m_pertCached = false;
1452
1453 setExpectedEstimate(0.0);
1454 setPessimisticEstimate(0.0);
1455 setOptimisticEstimate(0.0);
1456
1457 m_type = Type_Effort;
1458 m_calendar = 0;
1459 m_risktype = Risk_None;
1460 m_unit = Duration::Unit_h;
1461 changed(Node::EstimateProperty);
1462 }
1463
operator =(const Estimate & estimate)1464 Estimate &Estimate::operator=(const Estimate &estimate)
1465 {
1466 copy(estimate);
1467 return *this;
1468 }
1469
copy(const Estimate & estimate)1470 void Estimate::copy(const Estimate &estimate)
1471 {
1472 //m_parent = 0; // don't touch
1473 m_expectedEstimate = estimate.m_expectedEstimate;
1474 m_optimisticEstimate = estimate.m_optimisticEstimate;
1475 m_pessimisticEstimate = estimate.m_pessimisticEstimate;
1476
1477 m_expectedValue = estimate.m_expectedValue;
1478 m_optimisticValue = estimate.m_optimisticValue;
1479 m_pessimisticValue = estimate.m_pessimisticValue;
1480
1481 m_expectedCached = estimate.m_expectedCached;
1482 m_optimisticCached = estimate.m_optimisticCached;
1483 m_pessimisticCached = estimate.m_pessimisticCached;
1484
1485 m_pertExpected = estimate.m_pertExpected;
1486 m_pertCached = estimate.m_pertCached;
1487
1488 m_type = estimate.m_type;
1489 m_calendar = estimate.m_calendar;
1490 m_risktype = estimate.m_risktype;
1491 m_unit = estimate.m_unit;
1492 changed(Node::EstimateProperty);
1493 }
1494
variance() const1495 double Estimate::variance() const
1496 {
1497 double d = deviation();
1498 return d * d;
1499 }
1500
variance(Duration::Unit unit) const1501 double Estimate::variance(Duration::Unit unit) const
1502 {
1503 double d = deviation(unit);
1504 return d * d;
1505 }
1506
deviation() const1507 double Estimate::deviation() const
1508 {
1509 return (m_pessimisticEstimate - m_optimisticEstimate) / 6;
1510 }
1511
deviation(Duration::Unit unit) const1512 double Estimate::deviation(Duration::Unit unit) const
1513 {
1514 if (unit == m_unit) {
1515 return deviation();
1516 }
1517 double p = pessimisticValue().toDouble(unit);
1518 double o = optimisticValue().toDouble(unit);
1519 double v = (p - o) / 6;
1520 return v;
1521 }
1522
pertExpected() const1523 Duration Estimate::pertExpected() const {
1524 if (m_risktype == Risk_Low) {
1525 if (! m_pertCached) {
1526 m_pertExpected = (optimisticValue() + pessimisticValue() + (expectedValue()*4))/6;
1527 m_pertCached = true;
1528 }
1529 return m_pertExpected;
1530 } else if (m_risktype == Risk_High) {
1531 if (! m_pertCached) {
1532 m_pertExpected = (optimisticValue() + (pessimisticValue()*2) + (expectedValue()*4))/7;
1533 m_pertCached = true;
1534 }
1535 return m_pertExpected;
1536 }
1537 return expectedValue(); // risk==none
1538 }
1539
pertOptimistic() const1540 Duration Estimate::pertOptimistic() const {
1541 if (m_risktype != Risk_None) {
1542 return pertExpected() - Duration(variance(Duration::Unit_ms));
1543 }
1544 return optimisticValue();
1545 }
1546
pertPessimistic() const1547 Duration Estimate::pertPessimistic() const {
1548 if (m_risktype != Risk_None) {
1549 return pertExpected() + Duration(variance(Duration::Unit_ms));
1550 }
1551 return pessimisticValue();
1552 }
1553
value(int valueType,bool pert) const1554 Duration Estimate::value(int valueType, bool pert) const {
1555 if (valueType == Estimate::Use_Expected) {
1556 return pert ? pertExpected() : expectedValue();
1557 } else if (valueType == Estimate::Use_Optimistic) {
1558 return pert ? pertOptimistic() : optimisticValue();
1559 } else if (valueType == Estimate::Use_Pessimistic) {
1560 return pert ? pertPessimistic() : pessimisticValue();
1561 }
1562 return expectedValue();
1563 }
1564
setUnit(Duration::Unit unit)1565 void Estimate::setUnit(Duration::Unit unit)
1566 {
1567 m_unit = unit;
1568 m_expectedCached = false;
1569 m_optimisticCached = false;
1570 m_pessimisticCached = false;
1571 m_pertCached = false;
1572 changed(Node::EstimateProperty);
1573 }
1574
load(KoXmlElement & element,XMLLoaderObject & status)1575 bool Estimate::load(KoXmlElement &element, XMLLoaderObject &status) {
1576 setType(element.attribute("type"));
1577 setRisktype(element.attribute("risk"));
1578 if (status.version() <= "0.6") {
1579 m_unit = (Duration::Unit)(element.attribute("display-unit", QString().number(Duration::Unit_h)).toInt());
1580 QList<qint64> s = status.project().standardWorktime()->scales();
1581 m_expectedEstimate = scale(Duration::fromString(element.attribute("expected")), m_unit, s);
1582 m_optimisticEstimate = scale(Duration::fromString(element.attribute("optimistic")), m_unit, s);
1583 m_pessimisticEstimate = scale(Duration::fromString(element.attribute("pessimistic")), m_unit, s);
1584 } else {
1585 if (status.version() <= "0.6.2") {
1586 // 0 pos in unit is now Unit_Y, so add 3 to get the correct new unit
1587 m_unit = (Duration::Unit)(element.attribute("unit", QString().number(Duration::Unit_ms - 3)).toInt() + 3);
1588 } else {
1589 m_unit = Duration::unitFromString(element.attribute("unit"));
1590 }
1591 m_expectedEstimate = element.attribute("expected", "0.0").toDouble();
1592 m_optimisticEstimate = element.attribute("optimistic", "0.0").toDouble();
1593 m_pessimisticEstimate = element.attribute("pessimistic", "0.0").toDouble();
1594
1595 m_calendar = status.project().findCalendar(element.attribute("calendar-id"));
1596 }
1597 return true;
1598 }
1599
save(QDomElement & element) const1600 void Estimate::save(QDomElement &element) const {
1601 QDomElement me = element.ownerDocument().createElement("estimate");
1602 element.appendChild(me);
1603 me.setAttribute("expected", QString::number(m_expectedEstimate));
1604 me.setAttribute("optimistic", QString::number(m_optimisticEstimate));
1605 me.setAttribute("pessimistic", QString::number(m_pessimisticEstimate));
1606 me.setAttribute("type", typeToString());
1607 if (m_calendar) {
1608 me.setAttribute("calendar-id", m_calendar->id());
1609 }
1610 me.setAttribute("risk", risktypeToString());
1611 me.setAttribute("unit", Duration::unitToString(m_unit));
1612 }
1613
typeToString(bool trans) const1614 QString Estimate::typeToString(bool trans) const {
1615 return typeToStringList(trans).at(m_type);
1616 }
1617
typeToString(Estimate::Type typ,bool trans)1618 QString Estimate::typeToString(Estimate::Type typ, bool trans)
1619 {
1620 return typeToStringList(trans).value(typ);
1621 }
1622
typeToStringList(bool trans)1623 QStringList Estimate::typeToStringList(bool trans) {
1624 return QStringList()
1625 << (trans ? i18n("Effort") : QString("Effort"))
1626 << (trans ? i18n("Duration") : QString("Duration"));
1627 }
1628
setType(Type type)1629 void Estimate::setType(Type type)
1630 {
1631 m_type = type;
1632 m_expectedCached = false;
1633 m_optimisticCached = false;
1634 m_pessimisticCached = false;
1635 m_pertCached = false;
1636 changed(Node::EstimateProperty);
1637 }
1638
setType(const QString & type)1639 void Estimate::setType(const QString& type) {
1640 if (type == "Effort")
1641 setType(Type_Effort);
1642 else if (type == "Duration" || /*old format*/ type == "FixedDuration")
1643 setType(Type_Duration);
1644 else if (/*old format*/type == "Length")
1645 setType(Type_Duration);
1646 else if (type == "Type_FixedDuration") // Typo, keep old xml files working
1647 setType(Type_Duration);
1648 else
1649 setType(Type_Effort); // default
1650 }
1651
risktypeToString(bool trans) const1652 QString Estimate::risktypeToString(bool trans) const {
1653 return risktypeToStringList(trans).at(m_risktype);
1654 }
1655
risktypeToStringList(bool trans)1656 QStringList Estimate::risktypeToStringList(bool trans) {
1657 return QStringList()
1658 << (trans ? i18n("None") : QString("None"))
1659 << (trans ? i18n("Low") : QString("Low"))
1660 << (trans ? i18n("High") : QString("High"));
1661 }
1662
setRisktype(const QString & type)1663 void Estimate::setRisktype(const QString& type) {
1664 if (type == "High")
1665 setRisktype(Risk_High);
1666 else if (type == "Low")
1667 setRisktype(Risk_Low);
1668 else
1669 setRisktype(Risk_None); // default
1670 }
1671
setRisktype(Risktype type)1672 void Estimate::setRisktype(Risktype type)
1673 {
1674 m_pertCached = false;
1675 m_risktype = type;
1676 changed(Node::EstimateRiskProperty);
1677 }
1678
setCalendar(Calendar * calendar)1679 void Estimate::setCalendar(Calendar *calendar)
1680 {
1681 m_calendar = calendar;
1682 m_expectedCached = false;
1683 m_optimisticCached = false;
1684 m_pessimisticCached = false;
1685 m_pertCached = false;
1686 changed(Node::EstimateProperty);
1687 }
1688
setExpectedEstimate(double value)1689 void Estimate::setExpectedEstimate(double value)
1690 {
1691 m_expectedEstimate = value;
1692 m_expectedCached = false;
1693 m_pertCached = false;
1694 changed(Node::EstimateProperty);
1695 }
1696
setOptimisticEstimate(double value)1697 void Estimate::setOptimisticEstimate(double value)
1698 {
1699 m_optimisticEstimate = value;
1700 m_optimisticCached = false;
1701 m_pertCached = false;
1702 changed(Node::EstimateOptimisticProperty);
1703 }
1704
setPessimisticEstimate(double value)1705 void Estimate::setPessimisticEstimate(double value)
1706 {
1707 m_pessimisticEstimate = value;
1708 m_pessimisticCached = false;
1709 m_pertCached = false;
1710 changed(Node::EstimatePessimisticProperty);
1711 }
1712
setOptimisticRatio(int percent)1713 void Estimate::setOptimisticRatio(int percent)
1714 {
1715 int p = percent>0 ? -percent : percent;
1716 m_optimisticValue = expectedValue()*(100+p)/100;
1717 m_optimisticEstimate = scale(m_optimisticValue, m_unit, scales());
1718 m_optimisticCached = true;
1719 m_pertCached = false;
1720 changed(Node::EstimateOptimisticProperty);
1721 }
1722
optimisticRatio() const1723 int Estimate::optimisticRatio() const {
1724 if (m_expectedEstimate == 0.0)
1725 return 0;
1726 return (int)((optimisticValue()*100)/expectedValue())-100;
1727 }
1728
setPessimisticRatio(int percent)1729 void Estimate::setPessimisticRatio(int percent)
1730 {
1731 int p = percent<0 ? -percent : percent;
1732 m_pessimisticValue = expectedValue()*(100+p)/100;
1733 m_pessimisticEstimate = scale(m_pessimisticValue, m_unit, scales());
1734 m_pessimisticCached = true;
1735 m_pertCached = false;
1736 changed(Node::EstimatePessimisticProperty);
1737 }
1738
pessimisticRatio() const1739 int Estimate::pessimisticRatio() const {
1740 if (m_expectedEstimate == 0.0)
1741 return 0;
1742 return (int)((pessimisticValue()*100)/expectedValue())-100;
1743 }
1744
1745 // internal
setOptimisticValue()1746 void Estimate::setOptimisticValue()
1747 {
1748 m_optimisticValue = scale(m_optimisticEstimate, m_unit, scales());
1749 m_optimisticCached = true;
1750 m_pertCached = false;
1751 }
1752
1753 // internal
setExpectedValue()1754 void Estimate::setExpectedValue()
1755 {
1756 m_expectedValue = scale(m_expectedEstimate, m_unit, scales());
1757 m_expectedCached = true;
1758 m_pertCached = false;
1759 }
1760
1761 // internal
setPessimisticValue()1762 void Estimate::setPessimisticValue()
1763 {
1764 m_pessimisticValue = scale(m_pessimisticEstimate, m_unit, scales());
1765 m_pessimisticCached = true;
1766 m_pertCached = false;
1767 }
1768
optimisticValue() const1769 Duration Estimate::optimisticValue() const
1770 {
1771 if (! m_optimisticCached) {
1772 const_cast<Estimate*>(this)->setOptimisticValue();
1773 }
1774 return m_optimisticValue;
1775 }
1776
pessimisticValue() const1777 Duration Estimate::pessimisticValue() const
1778 {
1779 if (! m_pessimisticCached) {
1780 const_cast<Estimate*>(this)->setPessimisticValue();
1781 }
1782 return m_pessimisticValue;
1783 }
1784
expectedValue() const1785 Duration Estimate::expectedValue() const
1786 {
1787 if (! m_expectedCached) {
1788 const_cast<Estimate*>(this)->setExpectedValue();
1789 }
1790 return m_expectedValue;
1791 }
1792
scale(const Duration & value,Duration::Unit unit,const QList<qint64> & scales)1793 double Estimate::scale(const Duration &value, Duration::Unit unit, const QList<qint64> &scales)
1794 {
1795 //debugPlan<<value.toDouble(unit)<<","<<unit<<scales;
1796 QList<qint64> lst = scales;
1797 switch (lst.count()) {
1798 case Duration::Unit_Y:
1799 lst << (qint64)(365 * 24) * 60 * 60 * 1000; // add milliseconds in a year
1800 Q_FALLTHROUGH();
1801 case Duration::Unit_M:
1802 lst << (qint64)(30 * 24) * 60 * 60 * 1000; // add milliseconds in a month
1803 Q_FALLTHROUGH();
1804 case Duration::Unit_w:
1805 lst << (qint64)(7 * 24) * 60 * 60 * 1000; // add milliseconds in a week
1806 Q_FALLTHROUGH();
1807 case Duration::Unit_d:
1808 lst << 24 * 60 * 60 * 1000; // add milliseconds in day
1809 Q_FALLTHROUGH();
1810 case Duration::Unit_h:
1811 lst << 60 * 60 * 1000; // add milliseconds in hour
1812 Q_FALLTHROUGH();
1813 case Duration::Unit_m:
1814 lst << 60 * 1000; // add milliseconds in minute
1815 Q_FALLTHROUGH();
1816 case Duration::Unit_s:
1817 lst << 1000; // add milliseconds in second
1818 Q_FALLTHROUGH();
1819 case Duration::Unit_ms:
1820 lst << 1; // add milliseconds in a millisecond
1821 Q_FALLTHROUGH();
1822 default:
1823 break;
1824 }
1825 double v = (double)(value.milliseconds());
1826 v /= lst[ unit ];
1827 //debugPlan<<value.toString()<<","<<unit<<"="<<v;
1828 return v;
1829 }
1830
scale(double value,Duration::Unit unit,const QList<qint64> & scales)1831 Duration Estimate::scale(double value, Duration::Unit unit, const QList<qint64> &scales)
1832 {
1833 //debugPlan<<value<<","<<unit<<scales;
1834 QList<qint64> lst = scales;
1835 switch (lst.count()) {
1836 case Duration::Unit_Y:
1837 lst << (qint64)(365 * 24) * 60 * 60 * 1000; // add milliseconds in a year
1838 Q_FALLTHROUGH();
1839 case Duration::Unit_M:
1840 lst << (qint64)(30 * 24) * 60 * 60 * 1000; // add milliseconds in a month
1841 Q_FALLTHROUGH();
1842 case Duration::Unit_w:
1843 lst << (qint64)(7 * 24) * 60 * 60 * 1000; // add milliseconds in a week
1844 Q_FALLTHROUGH();
1845 case Duration::Unit_d:
1846 lst << 24 * 60 * 60 * 1000; // add milliseconds in day
1847 Q_FALLTHROUGH();
1848 case Duration::Unit_h:
1849 lst << 60 * 60 * 1000; // add milliseconds in hour
1850 Q_FALLTHROUGH();
1851 case Duration::Unit_m:
1852 lst << 60 * 1000; // add milliseconds in minute
1853 Q_FALLTHROUGH();
1854 case Duration::Unit_s:
1855 lst << 1000; // add milliseconds in second
1856 Q_FALLTHROUGH();
1857 case Duration::Unit_ms:
1858 lst << 1; // add milliseconds in a millisecond
1859 Q_FALLTHROUGH();
1860 default:
1861 break;
1862 }
1863 qint64 v = (qint64)(value * lst[ unit ]);
1864 //debugPlan<<value<<","<<unit<<"="<<v;
1865 return Duration(v, Duration::Unit_ms);
1866 }
1867
1868 //static
defaultScales()1869 QList<qint64> Estimate::defaultScales()
1870 {
1871 QList<qint64> lst;
1872 lst << (qint64)(365 * 24) * 60 * 60 * 1000 // add milliseconds in a year
1873 << (qint64)(30 * 24) * 60 * 60 * 1000 // add milliseconds in a month
1874 << (qint64)(7 * 24) * 60 * 60 * 1000 // add milliseconds in a week
1875 << 24 * 60 * 60 * 1000 // add milliseconds in day
1876 << 60 * 60 * 1000 // add milliseconds in hour
1877 << 60 * 1000 // add milliseconds in minute
1878 << 1000 // add milliseconds in second
1879 << 1; // add milliseconds in a millisecond
1880 return lst;
1881 }
1882
scales() const1883 QList<qint64> Estimate::scales() const
1884 {
1885 QList<qint64> s;
1886 if (m_type == Type_Duration && m_calendar == 0) {
1887 return s; // Use default scaling (24h a day...)
1888 }
1889 if (m_parent == 0) {
1890 return s;
1891 }
1892 Project *p = static_cast<Project*>(m_parent->projectNode());
1893 if (p == 0) {
1894 return s;
1895 }
1896 s << p->standardWorktime()->scales();
1897 return s;
1898 }
1899
1900
1901 } //KPlato namespace
1902