2 * task.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.
14 <!-- Task element, child of projects and for subtasks -->
15 <!ELEMENT Task (Id, Name, ParentTask*, Note*,
18 actualStart, actualEnd,
20 SubTasks*, Depends*, Previous*, Followers*,
21 Allocations*, bookedResources*, note*)>
22 <!ATTLIST Task ProjectID CDATA #IMPLIED
23 complete CDATA #REQUIRED
24 Priority CDATA #IMPLIED
25 Type (Task|Milestone) #REQUIRED>
27 <!ELEMENT ParentTask (#PCDATA)>
28 <!ELEMENT Id (#PCDATA)>
29 <!ELEMENT Name (#PCDATA)>
30 <!ELEMENT Note (#PCDATA)>
31 <!ELEMENT ProjectID (#PCDATA)>
32 <!ELEMENT TaskType (#PCDATA)>
33 <!ELEMENT Priority (#PCDATA)>
34 <!ELEMENT start (#PCDATA)>
35 <!ELEMENT end (#PCDATA)>
36 <!ELEMENT minStart (#PCDATA)>
37 <!ELEMENT maxStart (#PCDATA)>
38 <!ELEMENT minEnd (#PCDATA)>
39 <!ELEMENT maxEnd (#PCDATA)>
40 <!ELEMENT actualStart (#PCDATA)>
41 <!ELEMENT actualEnd (#PCDATA)>
42 <!ELEMENT planStart (#PCDATA)>
43 <!ELEMENT planEnd (#PCDATA)>
44 <!ELEMENT SubTasks (Task+)>
45 <!ELEMENT Depends (TaskID+)>
46 <!ELEMENT TaskID (#PCDATA)>
47 <!ELEMENT Previous (TaskID+)>
48 <!ELEMENT Followers (TaskID+)>
49 <!ELEMENT Allocations (Allocation+)>
50 <!ELEMENT Allocation EMPTY>
51 <!ELEMENT bookedResources (ResourceID+)>
52 <!ELEMENT ResourceID (#PCDATA)>
53 <!ELEMENT note (#PCDATA)>
59 ResourceID CDATA #REQUIRED>
61 <!-- Date values contain human readable date -->
63 humanReadable CDATA #REQUIRED>
65 humanReadable CDATA #REQUIRED>
67 humanReadable CDATA #REQUIRED>
69 humanReadable CDATA #REQUIRED>
71 humanReadable CDATA #REQUIRED>
73 humanReadable CDATA #REQUIRED>
75 humanReadable CDATA #REQUIRED>
77 humanReadable CDATA #REQUIRED>
86 int Task::debugLevel = 0;
89 TaskList::getTask(const QString& id)
91 for (Task* t = first(); t != 0; t = next())
98 Task::Task(Project* proj, const QString& id_, const QString& n, Task* p,
99 const QString f, int l)
100 : CoreAttributes(proj, id_, n, p), file(f), line(l)
102 allocations.setAutoDelete(TRUE);
109 startCredit = endCredit = 0.0;
111 schedulingDone = FALSE;
116 // Inherit flags from parent task.
117 for (QStringList::Iterator it = p->flags.begin();
118 it != p->flags.end(); ++it)
121 // Set attributes that are inherited from parent task.
122 projectId = p->projectId;
123 priority = p->priority;
124 minStart = p->minStart;
125 maxStart = p->maxEnd;
126 minEnd = p->minStart;
128 responsible = p->responsible;
129 account = p->account;
133 // Set attributes that are inherited from global attributes.
134 projectId = proj->getCurrentId();
135 priority = proj->getPriority();
136 minStart = minEnd = proj->getStart();
137 maxStart = maxEnd = proj->getEnd();
140 planStart = planEnd = 0;
141 planDuration = planLength = planEffort = 0.0;
143 actualStart = actualEnd = 0;
144 actualDuration = actualLength = actualEffort = 0.0;
147 duration = length = effort = 0.0;
151 Task::fatalError(const QString& msg) const
153 qWarning("%s:%d:%s\n", file.latin1(), line, msg.latin1());
157 Task::schedule(time_t& date, time_t slotDuration)
159 // Task is already scheduled.
162 qFatal("Task %s is already scheduled", id.latin1());
166 bool limitChanged = FALSE;
168 (scheduling == Task::ASAP ||
169 (length == 0.0 && duration == 0.0 && effort == 0.0 && !milestone)))
171 /* No start time has been specified. The start time is either
172 * start time of the parent (or the project start time if the
173 * tasks has no previous tasks) or the start time is
174 * determined by the end date of the last previous task. */
176 if (depends.count() == 0)
179 start = project->getStart();
180 else if (getParent()->start != 0)
181 start = getParent()->start;
186 else if ((es = earliestStart()) > 0)
192 return TRUE; // Task cannot be scheduled yet.
198 (scheduling == Task::ALAP ||
199 (length == 0.0 && duration == 0.0 && effort == 0.0 && !milestone)))
201 /* No end time has been specified. The end time is either end
202 * time of the parent (or the project end time if the tasks
203 * has no previous tasks) or the end time is determined by the
204 * start date of the earliest following task. */
206 if (preceeds.count() == 0)
209 end = project->getEnd();
210 else if (getParent()->end != 0)
211 end = getParent()->end;
216 else if ((le = latestEnd()) > 0)
221 else return TRUE; // Task cannot be scheduled yet.
226 if (scheduling == Task::ASAP)
230 lastSlot = start - 1;
235 tentativeEnd = date + slotDuration - 1;
237 qWarning("Scheduling of %s starts at %s (%s)",
238 id.latin1(), time2ISO(lastSlot).latin1(),
239 time2ISO(date).latin1());
241 /* Do not schedule anything if the time slot is not directly
242 * following the time slot that was previously scheduled. */
243 if (!((date - slotDuration <= lastSlot) && (lastSlot < date)))
244 return !limitChanged;
245 lastSlot = date + slotDuration - 1;
256 tentativeStart = date;
258 qWarning("Scheduling of ALAP task %s starts at %s (%s)",
259 id.latin1(), time2ISO(lastSlot).latin1(),
260 time2ISO(date).latin1());
262 /* Do not schedule anything if the current time slot is not
263 * directly preceeding the previously scheduled time slot. */
264 if (!((date + slotDuration <= lastSlot) &&
265 (lastSlot < date + 2 * slotDuration)))
266 return !limitChanged;
271 qWarning("Scheduling %s at %s",
272 id.latin1(), time2ISO(date).latin1());
274 if ((duration > 0.0) || (length > 0.0))
276 /* Length specifies the number of working days (as daily load)
277 * and duration specifies the number of calender days. */
278 if (!allocations.isEmpty())
279 bookResources(date, slotDuration);
281 doneDuration += ((double) slotDuration) / ONEDAY;
282 if (!(isWeekend(date) || project->isVacation(date)))
283 doneLength += ((double) slotDuration) / ONEDAY;
286 qWarning("Length: %f/%f Duration: %f/%f",
288 doneDuration, duration);
289 // Check whether we are done with this task.
290 if ((length > 0.0 && doneLength >= length * 0.999999) ||
291 (duration > 0.0 && doneDuration >= duration * 0.999999))
293 if (scheduling == ASAP)
295 if (doneEffort > 0.0)
298 date = end - slotDuration + 1;
301 end = date + slotDuration - 1;
306 if (doneEffort > 0.0)
308 start = tentativeStart;
315 project->removeActiveTask(this);
319 else if (effort > 0.0)
321 /* The effort of the task has been specified. We have to look
322 * how much the resources can contribute over the following
323 * workings days until we have reached the specified
325 // if (project->isVacation(date))
327 bookResources(date, slotDuration);
328 // Check whether we are done with this task.
329 if (doneEffort >= effort)
331 if (scheduling == ASAP)
338 start = tentativeStart;
341 project->removeActiveTask(this);
347 // Task is a milestone.
348 if (scheduling == ASAP)
358 project->removeActiveTask(this);
361 else if (start != 0 && end != 0)
363 // Task with start and end date but no duration criteria.
364 if (!allocations.isEmpty() && !project->isVacation(date))
365 bookResources(date, slotDuration);
367 if ((scheduling == ASAP && (date + slotDuration) >= end) ||
368 (scheduling == ALAP && date <= start))
370 project->removeActiveTask(this);
379 Task::scheduleContainer(bool safeMode)
388 // Check that this is really a container task
389 if ((t = subFirst()))
391 if (t->start == 0 || t->end == 0)
399 for (t = subNext() ; t != 0; t = subNext())
401 /* Make sure that all sub tasks have been scheduled. If not we
402 * can't yet schedule this task. */
403 if (t->start == 0 || t->end == 0)
406 if (t->start < nstart)
415 propagateStart(safeMode);
420 propagateEnd(safeMode);
423 schedulingDone = TRUE;
429 Task::propagateStart(bool safeMode)
435 qWarning("PS1: Setting start of %s to %s",
436 id.latin1(), time2ISO(start).latin1());
438 for (Task* t = previous.first(); t != 0; t = previous.next())
439 if (t->end == 0 && t->scheduling == ALAP &&
442 t->end = t->latestEnd();
444 qWarning("PS2: Setting end of %s to %s",
445 t->id.latin1(), time2ISO(t->end).latin1());
446 t->propagateEnd(safeMode);
447 if (safeMode && t->isActive())
448 project->addActiveTask(t);
451 /* Propagate start time to sub tasks which have only an implicit
452 * dependancy on the parent task. Do not touch container tasks. */
453 for (Task* t = subFirst(); t != 0; t = subNext())
455 if (t->start == 0 && t->previous.isEmpty() &&
456 t->sub.isEmpty() && t->scheduling == ASAP)
460 qWarning("PS3: Setting start of %s to %s",
461 t->id.latin1(), time2ISO(t->start).latin1());
462 if (safeMode && t->isActive())
463 project->addActiveTask(t);
464 t->propagateStart(safeMode);
468 if (safeMode && parent)
469 getParent()->scheduleContainer(TRUE);
473 Task::propagateEnd(bool safeMode)
479 qWarning("PE1: Setting end of %s to %s",
480 id.latin1(), time2ISO(end).latin1());
482 for (Task* t = followers.first(); t != 0; t = followers.next())
483 if (t->start == 0 && t->scheduling == ASAP &&
484 t->earliestStart() != 0)
486 t->start = t->earliestStart();
488 qWarning("PE2: Setting start of %s to %s",
489 t->id.latin1(), time2ISO(t->start).latin1());
490 t->propagateStart(safeMode);
491 if (safeMode && t->isActive())
492 project->addActiveTask(t);
494 /* Propagate end time to sub tasks which have only an implicit
495 * dependancy on the parent task. Do not touch container tasks. */
496 for (Task* t = subFirst(); t != 0; t = subNext())
497 if (t->end == 0 && t->followers.isEmpty() &&
498 t->sub.isEmpty() && t->scheduling == ALAP)
502 qWarning("PE3: Setting end of %s to %s",
503 t->id.latin1(), time2ISO(t->end).latin1());
504 if (safeMode && t->isActive())
505 project->addActiveTask(t);
506 t->propagateEnd(safeMode);
509 if (safeMode && parent)
510 getParent()->scheduleContainer(TRUE);
514 Task::propagateInitialValues()
517 propagateStart(FALSE);
520 // Check if the some data of sub tasks can already be propagated.
522 scheduleContainer(TRUE);
526 Task::bookResources(time_t date, time_t slotDuration)
528 bool allocFound = FALSE;
530 for (Allocation* a = allocations.first();
531 a != 0 && (effort == 0.0 || doneEffort < effort);
532 a = allocations.next())
534 if (a->isPersistent() && a->getLockedResource())
536 bookResource(a->getLockedResource(), date, slotDuration,
539 else if (bookResource(a->getResource(), date, slotDuration,
543 if (a->isPersistent())
544 a->setLockedResource(a->getResource());
548 /* TODO: Try to free the main resource from a lower
550 for (Resource* r = a->first(); r != 0; r = a->next())
551 if (bookResource(r, date, slotDuration, a->getLoad()))
554 if (a->isPersistent())
555 a->setLockedResource(r);
564 Task::bookResource(Resource* r, time_t date, time_t slotDuration,
568 double intervalLoad = project->convertToDailyLoad(slotDuration);
570 for (Resource* rit = r->subResourcesFirst(); rit != 0;
571 rit = r->subResourcesNext())
573 if ((*rit).isAvailable(date, slotDuration, loadFactor, this))
575 (*rit).book(new Booking(
576 Interval(date, date + slotDuration - 1), this,
577 account ? account->getKotrusId() : QString(""),
579 addBookedResource(rit);
581 /* Move the start date to make sure that there is
582 * some work going on on the start date. */
585 if (scheduling == ASAP)
587 else if (scheduling == ALAP)
588 end = date + slotDuration - 1;
590 qFatal("Unknown scheduling mode");
594 tentativeStart = date;
595 tentativeEnd = date + slotDuration - 1;
596 doneEffort += intervalLoad * (*rit).getEfficiency();
605 Task::needsEarlierTimeSlot(time_t date)
607 if (scheduling == ALAP && lastSlot > 0 && !schedulingDone &&
608 date > lastSlot && sub.isEmpty())
610 if (scheduling == ASAP && lastSlot > 0 && !schedulingDone &&
611 date > lastSlot + 1 && sub.isEmpty())
618 Task::isCompleted(time_t date) const
622 // some completion degree was specified.
623 return ((complete / 100.0) *
624 (actualEnd - actualStart) + actualStart) > date;
628 return (project->getNow() > date);
632 Task::earliestStart()
635 for (Task* t = depends.first(); t != 0; t = depends.next())
637 // All tasks this task depends on must have an end date set.
640 // Milestones are assumed to have duration 0.
642 date = t->end + (t->milestone ? 0 : 1);
652 for (Task* t = preceeds.first(); t != 0; t = preceeds.next())
654 // All tasks this task preceeds must have an start date set.
657 if (date == 0 || t->start < date)
665 Task::getPlanCalcDuration() const
667 time_t delta = planEnd - planStart;
669 return (project->convertToDailyLoad(delta));
671 return (double) delta / ONEDAY;
675 Task::getPlanLoad(const Interval& period, Resource* resource)
681 for (Task* t = subFirst(); t != 0; t = subNext())
682 load += t->getPlanLoad(period, resource);
686 load += resource->getPlanLoad(period, this);
688 for (Resource* r = planBookedResources.first(); r != 0;
689 r = planBookedResources.next())
690 load += r->getPlanLoad(period, this);
696 Task::getActualCalcDuration() const
698 time_t delta = actualEnd - actualStart;
700 return (project->convertToDailyLoad(delta));
702 return (double) delta / ONEDAY;
706 Task::getActualLoad(const Interval& period, Resource* resource)
712 for (Task* t = subFirst(); t != 0; t = subNext())
713 load += t->getActualLoad(period, resource);
717 load += resource->getActualLoad(period, this);
719 for (Resource* r = actualBookedResources.first(); r != 0;
720 r = actualBookedResources.next())
721 load += r->getActualLoad(period, this);
727 Task::getPlanCredits(const Interval& period, Resource* resource,
730 double credits = 0.0;
732 if (recursive && subFirst())
734 for (Task* t = subFirst(); t != 0; t = subNext())
735 credits += t->getPlanCredits(period, resource, recursive);
739 credits += resource->getPlanCredits(period, this);
741 for (Resource* r = planBookedResources.first(); r != 0;
742 r = planBookedResources.next())
743 credits += r->getPlanCredits(period, this);
745 if (period.contains(planStart))
746 credits += startCredit;
747 if (period.contains(planEnd))
748 credits += endCredit;
754 Task::getActualCredits(const Interval& period, Resource* resource,
757 double credits = 0.0;
759 if (recursive && subFirst())
761 for (Task* t = subFirst(); t != 0; t = subNext())
762 credits += t->getActualCredits(period, resource, recursive);
766 credits += resource->getActualCredits(period, this);
768 for (Resource* r = actualBookedResources.first(); r != 0;
769 r = actualBookedResources.next())
770 credits += r->getActualCredits(period, this);
772 if (period.contains(actualStart))
773 credits += startCredit;
774 if (period.contains(actualEnd))
775 credits += endCredit;
781 Task::xRef(QDict<Task>& hash)
785 for (QStringList::Iterator it = dependsIds.begin();
786 it != dependsIds.end(); ++it)
788 QString absId = resolveId(*it);
790 if ((t = hash.find(absId)) == 0)
792 fatalError(QString("Unknown dependency '") + absId + "'");
795 else if (depends.find(t) != -1)
797 fatalError(QString("No need to specify dependency '") + absId +
805 t->followers.append(this);
809 for (QStringList::Iterator it = preceedsIds.begin();
810 it != preceedsIds.end(); ++it)
812 QString absId = resolveId(*it);
814 if ((t = hash.find(absId)) == 0)
816 fatalError(QString("Unknown dependency '") + absId + "'");
819 else if (preceeds.find(t) != -1)
821 fatalError(QString("No need to specify dependency '") + absId +
829 t->previous.append(this);
837 Task::resolveId(QString relId)
839 /* Converts a relative ID to an absolute ID. Relative IDs start
840 * with a number of bangs. A set of bangs means 'Name of the n-th
841 * parent task' with n being the number of bangs. */
847 for (i = 0; i < relId.length() && relId.mid(i, 1) == "!"; ++i)
851 fatalError(QString("Illegal relative ID '") + relId + "'");
857 return t->id + "." + relId.right(relId.length() - i);
859 return relId.right(relId.length() - i);
863 Task::preScheduleOk()
865 if ((planEffort > 0 || actualEffort > 0) && allocations.count() == 0)
867 fatalError(QString().sprintf(
868 "No allocations specified for effort based task %s",
873 // Check plan values.
874 int durationSpec = 0;
875 if (planEffort > 0.0)
877 if (planLength > 0.0)
879 if (planDuration > 0.0)
883 if (planStart != 0 || !depends.isEmpty())
885 if (planEnd != 0 || !preceeds.isEmpty())
888 if (durationSpec > 1)
890 fatalError(QString().sprintf("In task %s:", id.latin1()) +
891 "You can specify either a length, a duration or an effort.");
894 else if (durationSpec == 1)
898 fatalError(QString().sprintf("In task %s:", id.latin1()) +
899 "You cannot specify a duration criteria for a "
905 fatalError(QString().sprintf("In task %s:", id.latin1()) +
906 "You cannot specify a duration criteria together with "
907 "a start and end criteria.");
911 else if (limitSpec != 2 &&
912 !(limitSpec == 1 && milestone) &&
913 !(limitSpec <= 1 && !sub.isEmpty()))
915 fatalError(QString().sprintf("In task %s:", id.latin1()) +
916 "If you do not specify a plan duration criteria "
917 "you have to specify a start and end criteria.");
921 // Check actual values
923 if (actualEffort > 0.0 || planEffort > 0.0)
925 if (actualLength > 0.0 || planLength > 0.0)
927 if (actualDuration > 0.0 || planDuration > 0.0)
931 if (planStart != 0 || actualStart != 0 || !depends.isEmpty())
933 if (planEnd != 0 || actualEnd != 0 || !preceeds.isEmpty())
936 if (durationSpec > 1)
938 fatalError(QString().sprintf("In task %s:", id.latin1()) +
939 "You can specify either an actual length, duration or effort.");
942 else if (durationSpec == 1)
946 fatalError(QString().sprintf("In task %s:", id.latin1()) +
947 "You cannot specify an actual duration criteria for a "
953 fatalError(QString().sprintf("In task %s:", id.latin1()) +
954 "You cannot specify an actual duration criteria "
955 "together with a start and end criteria.");
959 else if (limitSpec != 2 &&
960 !(limitSpec == 1 && milestone) &&
961 !(limitSpec <= 1 && !sub.isEmpty()))
963 fatalError(QString().sprintf("In task %s:", id.latin1()) +
964 "If you do not specify an actual duration criteria you "
965 "have to specify a start and end criteria.");
971 if (durationSpec > 0)
973 fatalError("A container tasks may never have a duration criteria");
976 if (!allocations.isEmpty())
978 fatalError("A container tasks may never have resource "
984 double intervalLoad = project->convertToDailyLoad(project->getScheduleGranularity());
986 for (Allocation* a = allocations.first(); a != 0; a = allocations.next())
988 if (a->getLoad() < intervalLoad * 100.0)
990 qWarning("Warning: Load is smaller than scheduling granularity "
991 "(Task: %s, Resource: %s). Minimal load is %.2f.",
992 id.latin1(), a->getResource()->getId().latin1(), intervalLoad + 0.005);
993 a->setLoad((int) (intervalLoad * 100.0));
1005 // All sub task must fit into their parent task.
1006 for (Task* t = subFirst(); t != 0; t = subNext())
1008 if (start > t->start)
1010 fatalError(QString().sprintf(
1011 "Task %s starts ealier than parent", t->id.latin1()));
1016 fatalError(QString().sprintf(
1017 "Task %s ends later than parent", t->id.latin1()));
1025 fatalError(QString("Task '") + id + "' has no start time.");
1028 if (minStart != 0 && start < minStart)
1030 fatalError(QString().sprintf(
1031 "Start time %s of task %s is earlier than requested minimum %s",
1032 time2ISO(start).latin1(), id.latin1(),
1033 time2ISO(minStart).latin1()));
1036 if (minStart != 0 && start > maxStart)
1038 fatalError(QString().sprintf(
1039 "Start time %s of task %s is later than requested maximum %s",
1040 time2ISO(start).latin1(), id.latin1(),
1041 time2ISO(maxStart).latin1()));
1044 if (start < project->getStart() || start > project->getEnd())
1046 fatalError(QString().sprintf(
1047 "Start time %s of task %s is outside of project period",
1048 time2ISO(start).latin1(), id.latin1()));
1053 fatalError(QString("Task '") + id + "' has no end time.");
1056 if (minEnd != 0 && end < minEnd)
1058 fatalError(QString().sprintf(
1059 "End time %s of task %s is earlier than requested minimum %s",
1060 time2ISO(end).latin1(), id.latin1(), time2ISO(minEnd).latin1()));
1063 if (maxEnd != 0 && end > maxEnd)
1065 fatalError(QString().sprintf(
1066 "End time %s of task %s is later than requested maximum %s",
1067 time2ISO(end).latin1(), id.latin1(), time2ISO(maxEnd).latin1()));
1070 if (end < project->getStart() || end > project->getEnd())
1072 fatalError(QString().sprintf(
1073 "End time %s of task %s is outside of project period",
1074 time2ISO(end).latin1(), id.latin1()));
1077 // Check if all previous tasks end before start of this task.
1078 for (Task* t = previous.first(); t != 0; t = previous.next())
1081 fatalError(QString().sprintf(
1082 "Task %s ends at %s but needs to preceed task %s "
1083 "which starts at %s",
1084 t->id.latin1(), time2ISO(t->end).latin1(),
1085 id.latin1(), time2ISO(start).latin1()));
1088 // Check if all following task start after this tasks end.
1089 for (Task* t = followers.first(); t != 0; t = followers.next())
1092 fatalError(QString().sprintf(
1093 "Task %s starts at %s but needs to follow task %s "
1095 t->id.latin1(), time2ISO(t->start).latin1(),
1096 id.latin1(), time2ISO(end).latin1()));
1100 if (!schedulingDone)
1102 fatalError(QString().sprintf(
1103 "Task %s has not been marked completed. It is scheduled to last "
1104 "from %s to %s. This might be a bug in the TaskJuggler "
1105 "scheduler.", id.latin1(), time2ISO(start).latin1(),
1106 time2ISO(end).latin1()));
1116 if (schedulingDone || !sub.isEmpty())
1119 if ((scheduling == ASAP && start != 0) ||
1120 (scheduling == ALAP && end != 0))
1127 Task::isPlanActive(const Interval& period) const
1131 work = Interval(planStart, planStart + 1);
1133 work = Interval(planStart, planEnd);
1134 return period.overlaps(work);
1138 Task::isActualActive(const Interval& period) const
1142 work = Interval(actualStart, actualStart + 1);
1144 work = Interval(actualStart, actualEnd);
1145 return period.overlaps(work);
1149 Task::getSubTaskList(TaskList& tl)
1151 for (Task* t = subFirst(); t != 0; t = subNext())
1154 t->getSubTaskList(tl);
1159 Task::isSubTask(Task* tsk)
1161 for (Task* t = subFirst(); t != 0; t = subNext())
1162 if (t == tsk || t->isSubTask(tsk))
1169 Task::treeSortKey(QString& key)
1173 key = QString().sprintf("%06d", sequenceNo) + key;
1178 for (Task* t = getParent()->subFirst(); t != 0;
1179 t = getParent()->subNext(), i++)
1182 key = QString().sprintf("%06d", i) + key;
1185 getParent()->treeSortKey(key);
1194 duration = planDuration;
1195 length = planLength;
1196 effort = planEffort;
1198 schedulingDone = FALSE;
1199 bookedResources.clear();
1201 if (actualStart == 0.0)
1202 actualStart = planStart;
1203 if (actualEnd == 0.0)
1204 actualEnd = planEnd;
1205 if (actualDuration == 0.0)
1206 actualDuration = planDuration;
1207 if (actualLength == 0.0)
1208 actualLength = planLength;
1209 if (actualEffort == 0.0)
1210 actualEffort = planEffort;
1218 planDuration = doneDuration;
1219 planLength = doneLength;
1220 planEffort = doneEffort;
1221 planBookedResources = bookedResources;
1225 Task::prepareActual()
1227 start = actualStart;
1230 duration = actualDuration;
1231 length = actualLength;
1232 effort = actualEffort;
1234 schedulingDone = FALSE;
1235 bookedResources.clear();
1239 Task::finishActual()
1241 actualStart = start;
1243 actualDuration = doneDuration;
1244 actualLength = doneLength;
1245 actualEffort = doneEffort;
1246 actualBookedResources = bookedResources;
1249 QDomElement Task::xmlElement( QDomDocument& doc )
1251 QDomElement taskElem = doc.createElement( "Task" );
1252 QDomElement tempElem;
1255 taskElem.appendChild( ReportXML::createXMLElem( doc, "Id", getId()));
1256 taskElem.appendChild( ReportXML::createXMLElem( doc, "Name",getName() ));
1257 CoreAttributes *parent = getParent();
1259 taskElem.appendChild( ReportXML::ReportXML::createXMLElem( doc, "ParentTask", parent->getId()));
1261 if( !note.isEmpty())
1262 taskElem.appendChild( ReportXML::createXMLElem( doc, "Note", getNote()));
1264 tempElem = ReportXML::createXMLElem( doc, "minStart", QString::number( minStart ));
1265 tempElem.setAttribute( "humanReadable", time2ISO( minStart ));
1266 taskElem.appendChild( tempElem );
1268 tempElem = ReportXML::createXMLElem( doc, "maxStart", QString::number( maxStart ));
1269 tempElem.setAttribute( "humanReadable", time2ISO( maxStart ));
1270 taskElem.appendChild( tempElem );
1272 tempElem = ReportXML::createXMLElem( doc, "minEnd", QString::number( minEnd ));
1273 tempElem.setAttribute( "humanReadable", time2ISO( minEnd ));
1274 taskElem.appendChild( tempElem );
1276 tempElem = ReportXML::createXMLElem( doc, "maxEnd", QString::number( maxEnd ));
1277 tempElem.setAttribute( "humanReadable", time2ISO( maxEnd ));
1278 taskElem.appendChild( tempElem );
1280 tempElem = ReportXML::createXMLElem( doc, "actualStart", QString::number( actualStart ));
1281 tempElem.setAttribute( "humanReadable", time2ISO( actualStart ));
1282 taskElem.appendChild( tempElem );
1284 tempElem = ReportXML::createXMLElem( doc, "actualEnd", QString::number( actualEnd ));
1285 tempElem.setAttribute( "humanReadable", time2ISO( actualEnd ));
1286 taskElem.appendChild( tempElem );
1288 tempElem = ReportXML::createXMLElem( doc, "planStart", QString::number( planStart ));
1289 tempElem.setAttribute( "humanReadable", time2ISO( planStart ));
1290 taskElem.appendChild( tempElem );
1292 tempElem = ReportXML::createXMLElem( doc, "planEnd", QString::number( planEnd ));
1293 tempElem.setAttribute( "humanReadable", time2ISO( planEnd ));
1294 taskElem.appendChild( tempElem );
1297 taskElem.appendChild( ReportXML::createXMLElem( doc, "maxStart", QString::number( maxStart )));
1298 taskElem.appendChild( ReportXML::createXMLElem( doc, "minEnd", QString::number( minEnd )));
1299 taskElem.appendChild( ReportXML::createXMLElem( doc, "maxEnd", QString::number( maxEnd )));
1300 taskElem.appendChild( ReportXML::createXMLElem( doc, "actualStart", QString::number( actualStart )));
1301 taskElem.appendChild( ReportXML::createXMLElem( doc, "actualEnd", QString::number( actualEnd )));
1302 taskElem.appendChild( ReportXML::createXMLElem( doc, "planStart", QString::number( planStart )));
1303 taskElem.appendChild( ReportXML::createXMLElem( doc, "planEnd", QString::number( planEnd )));
1305 taskElem.setAttribute( "ProjectID", projectId );
1306 taskElem.setAttribute( "Priority", getPriority() );
1307 taskElem.setAttribute( "complete", complete );
1308 taskElem.setAttribute( "Type", milestone ? "Milestone" : "Task" );
1310 /* Now start the subtasks */
1312 QDomElement subTaskElem = doc.createElement( "SubTasks" );
1313 for (Task* t = subFirst(); t != 0; t = subNext())
1317 QDomElement sTask = t->xmlElement( doc );
1318 subTaskElem.appendChild( sTask );
1323 taskElem.appendChild( subTaskElem);
1325 /* Tasks (by id) on which this task depends */
1326 if( dependsIds.count() > 0 )
1328 QDomElement deps = doc.createElement( "Depends" );
1330 for (QValueListConstIterator<QString> it1= dependsIds.begin(); it1 != dependsIds.end(); ++it1)
1332 deps.appendChild( ReportXML::createXMLElem( doc, "TaskID", *it1 ));
1334 taskElem.appendChild( deps );
1337 /* list of tasks by id which are previous */
1338 if( previous.count() > 0 )
1340 QDomElement prevs = doc.createElement( "Previous" );
1342 TaskList tl( previous );
1343 for (Task* t = tl.first(); t != 0; t = tl.next())
1347 prevs.appendChild( ReportXML::createXMLElem( doc, "TaskID", t->getId()));
1350 taskElem.appendChild( prevs );
1353 /* list of tasks by id which follow */
1354 if( followers.count() > 0 )
1356 QDomElement foll = doc.createElement( "Followers" );
1358 TaskList tl( followers );
1359 for (Task* t = tl.first(); t != 0; t = tl.next())
1363 foll.appendChild( ReportXML::createXMLElem( doc, "TaskID", t->getId()));
1367 taskElem.appendChild( foll );
1371 if( allocations.count() > 0 )
1373 QDomElement alloc = doc.createElement( "Allocations" );
1375 QPtrList<Allocation> al(allocations);
1376 for (Allocation* a = al.first(); a != 0; a = al.next())
1378 alloc.appendChild( a->xmlElement( doc ));
1380 taskElem.appendChild( alloc );
1383 /* booked Ressources */
1384 if( bookedResources.count() > 0 )
1386 QDomElement bres = doc.createElement( "bookedResources" );
1388 QPtrList<Resource> br(bookedResources);
1389 for (Resource* r = br.first(); r != 0; r = br.next())
1391 bres.appendChild( r->xmlIDElement( doc ));
1393 taskElem.appendChild( bres );
1398 if( ! note.isEmpty())
1400 QDomElement e = doc.createElement( "note" );
1401 taskElem.appendChild( e );
1402 t = doc.createTextNode( note );
1410 TaskList::compareItems(QCollection::Item i1, QCollection::Item i2)
1412 Task* t1 = static_cast<Task*>(i1);
1413 Task* t2 = static_cast<Task*>(i2);
1420 t1->treeSortKey(key1);
1422 t2->treeSortKey(key2);
1423 return key1 < key2 ? -1 : 1;
1426 return t1->start == t2->start ? 0 :
1427 t1->start > t2->start ? -1 : 1;
1429 return t1->start == t2->start ? 0 :
1430 t1->start < t2->start ? -1 : 1;
1432 return t1->end == t2->end ? 0 :
1433 t1->end > t2->end ? -1 : 1;
1435 return t1->end == t2->end ? 0 :
1436 t1->end < t2->end ? -1 : 1;
1438 if (t1->priority == t2->priority)
1439 return 0; // TODO: Use duration as next criteria
1441 return (t1->priority - t2->priority);
1443 if (t1->priority == t2->priority)
1444 return 0; // TODO: Use duration as next criteria
1446 return (t2->priority - t1->priority);
1450 t1->responsible->getFullName(fn1);
1452 t2->responsible->getFullName(fn2);
1453 return - fn1.compare(fn2);
1455 case ResponsibleDown:
1458 t1->responsible->getFullName(fn1);
1460 t2->responsible->getFullName(fn2);
1461 return fn1.compare(fn2);
1464 return CoreAttributesList::compareItems(i1, i2);
1470 void Task::toTodo( KCal::Todo* todo, KCal::CalendarLocal *cal )
1475 // todo->setReadOnly( true );
1477 /* Start-Time of the task */
1478 dt.setTime_t( getPlanStart() );
1479 todo->setDtStart( dt );
1480 todo->setHasDueDate( true );
1482 /* Due-Time of the todo -> plan End */
1483 dt.setTime_t( getPlanEnd());
1484 todo->setDtDue( dt );
1485 todo->setHasStartDate(true);
1487 /* Description and summary -> project ID */
1488 todo->setDescription( getNote() );
1489 todo->setSummary( getName() );
1490 todo->setPriority( getPriority() );
1491 todo->setCompleted( getComplete() );
1494 QPtrList<Resource> resList;
1495 resList = getPlanBookedResources();
1496 QStringList strList;
1499 for ( res = resList.first(); res; res = resList.next() )
1501 strList.append( res->getName());
1503 todo->setResources(strList);