####### kdevelop will overwrite this part!!! (begin)##########
-SUBDIRS = taskjuggler docs
+SUBDIRS = taskjuggler docs TestSuite
EXTRA_DIST = taskjuggler.kdevprj admin AUTHORS COPYING ChangeLog INSTALL README TODO taskjuggler.lsm
--- /dev/null
+Makefile.in
+Makefile
+
--- /dev/null
+all:
+ @ ./runtests
+
--- /dev/null
+project test "Test" "$Id" 2000-01-01 2000-03-10
+
+include "workforce.tjsp"
+
+task t1 "Task1" {
+ start 2000-01-01
+ effort 10d
+ allocate r1
+}
+
+task t2 "Task2" {
+ depends t1
+ effort 10d
+ allocate r2
+}
+
+task t3 "Task3" {
+ start 2000-01-01
+ length 13d
+ allocate r3
+}
+
+task t4 "Task4" {
+ depends t3
+ duration 26d
+}
+
+task t5 "Task5" {
+ depends t1
+ priority 1000
+ length 5d
+ allocate r2
+}
+
+htmltaskreport "project_a.html" {
+ columns name, start, end, daily
+ sorttasks nameup
+ hideresource 0
+}
+
+htmlresourcereport "project_a_res.html" {
+ columns name, daily
+ hidetask 0
+ sorttasks nameup
+}
--- /dev/null
+project test "Test" "$Id" 2000-01-01 2000-02-31
+
+include "workforce.tjsp"
+
+task t1 "Task1" {
+ start 2000-01-01
+ effort 10d
+ allocate r1
+}
+
+task t3 "Task3" {
+ start 2000-01-01
+ length 13d
+ allocate r3
+}
+
+task t5 "Task5" {
+ depends t1
+ priority 1000
+ length 5d
+ allocate r2
+}
+
+export "project_b1_export.tjsp" {
+ hideresource 0
+}
+
--- /dev/null
+project test "Test" "$Id" 2000-01-01 2000-03-10
+
+include "workforce.tjsp"
+
+include "project_b1_export.tjsp"
+
+
+task t2 "Task2" {
+ depends t1
+ effort 10d
+ allocate r2
+}
+
+task t4 "Task4" {
+ depends t3
+ duration 26d
+}
+
+htmltaskreport "project_b.html" {
+ columns name, start, end, daily
+ sorttasks nameup
+ hideresource 0
+}
+
+htmlresourcereport "project_b_res.html" {
+ columns name, daily
+ hidetask 0
+ sorttasks nameup
+}
+
--- /dev/null
+../../../taskjuggler/taskjuggler project_a.tjp
+../../../taskjuggler/taskjuggler project_b1.tjp
+../../../taskjuggler/taskjuggler project_b2.tjp
+
+cmp project_a.html project_b.html
+if [ $? -ne 0 ] ; then
+ exit 1
+fi
+
+cmp project_a_res.html project_b_res.html
+if [ $? -ne 0 ] ; then
+ exit 1
+fi
+
+rm *.html project_b1_export.tjsp
+
--- /dev/null
+vacation "Monday off" 2000-01-03
+vacation "Wednesday off" 2000-01-05
+vacation "Few days off" 2000-01-12 - 2000-01-15
+
+resource team "Team" {
+ vacation 2000-01-21 - 2000-01-25
+
+ resource r1 "Resource1" {
+ vacation 2000-01-17
+ }
+
+ resource r2 "Resource2" {
+ efficiency 0.5
+ }
+}
+
+supplement resource r2 {
+ vacation 2000-01-18
+}
+
+resource r3 "Resource3" {
+}
+
+
--- /dev/null
+#! /bin/sh
+
+set errors=0
+
+dirs=`find * -type d -maxdepth 0`
+
+for d in $dirs ; do
+ if [ $d != "CVS" ] ; then
+ cd $d
+ ./runtest
+ errors=$(( $errors + $? ))
+ if [ $? -ne 0 ] ; then
+ echo "Test $d failed"
+ fi
+ cd ..
+ fi
+done
+
+exit $errors
+
if [ $d != "CVS" ] ; then
echo "Testing in $d"
cd $d
- ../testdir
+ if [ -x ./testdir ] ; then
+ ./testdir
+ else
+ ../testdir
+ fi
errors=$(( $errors + $? ))
cd ..
fi
<item>Property reference is now sorted in alphabetical order.
-<item>Added missing 'export' report to documentation.
+<item>Added missing 'export' report to documentation. Export reports
+can now contain the scheduled tasks as well as the resource
+allocations.
+
+<item>New keywords 'planbooking' and 'actualbooking' to enter fixed
+bookings of resources in the resource declaration.
<item>Added new example project to illustrate the use of export in big
projects that are split into sub projects.
<item>HTML comment in HTML report files is now using correct syntax.
+<item>Partial fix for correct timezone handling.
+
</itemize>
<p>
string may include line breaks.
<label id="date"><tag/DATE/A DATE is an ISO-compliant date in the
-format YYYY-MM-DD[-hh:mm]. Hour, minute,
-and second are optional. If not specified, the values are set to 0.
+format YYYY-MM-DD[-hh:mm[:ss]][-TIMEZONE]. Hour, minutes,
+seconds and the TIMEZONE are optional. If not specified, the values
+are set to 0. The local timezone is used if no other is specified. If
+the timezone is not known taskjuggler will fall back to UTC. See
+the source code (taskjuggler/Utility.cpp) for details.
<label id="time"><tag/TIME/A time in the format HH:MM.
Optional attributes:
<descrip>
+<tag/hideresource <expression>/
+expression: <ref id="logicalexpression" name="LOGICALEXPRESSION">
+
+Report only resources that do not have flags meeting the described logical
+expression.
+
<tag/hidetask <expression>/
expression: <ref id="logicalexpression" name="LOGICALEXPRESSION">
-List only tasks that do not have flags meeting the described logical
+Report only tasks that do not have flags meeting the described logical
expression.
+<tag/rollupresource <expression>/
+expression: <ref id="logicalexpression" name="LOGICALEXPRESSION">
+
+Do not show subresources of resources with flags meeting the described
+logical expression.
+
<tag/rolluptask <expression>/
expression: <ref id="logicalexpression" name="LOGICALEXPRESSION">
fixed start and end dates for all tasks. The tasks only have start and
end times and their flags. No other attributes are exported.
+If requested the resource usage for the tasks is reported as well. But
+only those allocations are listed that belong to tasks listed in the
+same export report.
+
The export report can be used to share certain tasks or milestones
with other projects.
case IdDown:
return c1->getId() - c2->getId();
case NameUp:
- return c2->getName().compare(c1->getName());
- case NameDown:
return c1->getName().compare(c2->getName());
+ case NameDown:
+ return c2->getName().compare(c1->getName());
default:
qFatal("Please implement sorting for mode %d in sub class!", sorting);
}
}
s.setDevice(&f);
- TaskList filteredList;
- filterTaskList(filteredList, 0);
- sortTaskList(filteredList);
+ TaskList filteredTaskList;
+ filterTaskList(filteredTaskList, 0);
+ sortTaskList(filteredTaskList);
- for (Task* t = filteredList.first(); t != 0; t = filteredList.next())
+ ResourceList filteredResourceList;
+ filterResourceList(filteredResourceList, 0);
+ sortResourceList(filteredResourceList);
+
+ generateTaskList(filteredTaskList, filteredResourceList);
+ generateResourceList(filteredTaskList, filteredResourceList);
+
+ f.close();
+ return TRUE;
+}
+
+bool
+ExportReport::generateTaskList(TaskList& filteredTaskList,
+ ResourceList& filteredResourceList)
+{
+ for (Task* t = filteredTaskList.first(); t != 0;
+ t = filteredTaskList.next())
{
- QString start = time2ISO(t->getPlanStart());
- for (uint i = 0; i < start.length(); i++)
- if (start[i] == ' ')
- start[i] = '-';
- QString end = time2ISO(t->getPlanEnd());
- for (uint i = 0; i < end.length(); i++)
- if (end[i] == ' ')
- end[i] = '-';
+ QString start = time2tjp(t->getPlanStart());
+ QString end = time2tjp(t->getPlanEnd());
s << "task " << t->getId() << " \"" << t->getName() << "\""
<< " { start " << start
- << " end " << end;
+ << " end " << end;
if (showActual)
{
- QString start = time2ISO(t->getActualStart());
- for (uint i = 0; i < start.length(); i++)
- if (start[i] == ' ')
- start[i] = '-';
- QString end = time2ISO(t->getActualEnd());
- for (uint i = 0; i < end.length(); i++)
- if (end[i] == ' ')
- end[i] = '-';
+ QString start = time2tjp(t->getActualStart());
+ QString end = time2tjp(t->getActualEnd());
s << "actualStart " << start
<< " actualEnd " << end;
}
-
+
+ if (!filteredResourceList.isEmpty())
+ s << " projectid " << t->getProjectId() << " ";
if (t->isMilestone())
s << "milestone ";
s << " }" << endl;
}
- f.close();
+ return TRUE;
+}
+
+bool
+ExportReport::generateResourceList(TaskList& filteredTaskList,
+ ResourceList& filteredResourceList)
+{
+ for (Resource* r = filteredResourceList.first(); r != 0;
+ r = filteredResourceList.next())
+ {
+ s << "supplement resource " << r->getId() << " {" << endl;
+ BookingList bl = r->getPlanJobs();
+ bl.setAutoDelete(TRUE);
+ for (Booking* b = bl.first(); b != 0; b = bl.next())
+ {
+ if (filteredTaskList.getTask(b->getTask()->getId()))
+ {
+ QString start = time2tjp(b->getStart());
+ QString end = time2tjp(b->getEnd());
+ s << " planbooking " << start << " " << end
+ << " " << b->getProjectId()
+ << " " << b->getTask()->getId() << endl;
+ }
+ }
+ bl = r->getActualJobs();
+ bl.setAutoDelete(TRUE);
+ for (Booking* b = bl.first(); b != 0; b = bl.next())
+ {
+ if (filteredTaskList.getTask(b->getTask()->getId()))
+ {
+ QString start = time2tjp(b->getStart());
+ QString end = time2tjp(b->getEnd());
+ s << " actualbooking " << start << " " << end
+ << " " << b->getProjectId()
+ << " " << b->getTask()->getId() << endl;
+ }
+ }
+ s << "}" << endl;
+ }
+
return TRUE;
}
virtual ~ExportReport() { }
bool generate();
+ bool generateTaskList(TaskList& ftl, ResourceList& frl);
+ bool generateResourceList(TaskList& ftl, ResourceList& frl);
private:
ExportReport() { }
token += c;
if (c == '-')
{
- // this must be a ISO date yyyy-mm-dd[[-hh:mm]-TZ]
+ // this must be a ISO date yyyy-mm-dd[[-hh:mm:[ss]]-TZ]
getDateFragment(token, c);
if (c != '-')
{
return EndOfFile;
}
getDateFragment(token, c);
+ if (c == ':')
+ getDateFragment(token, c);
}
int i = 0;
if (c == '-')
else if (token == KW("vacation"))
{
time_t from, to;
- bool isResourceVacation;
QString name;
- if (!readVacation(from, to, TRUE, &name,
- &isResourceVacation))
+ if (!readVacation(from, to, TRUE, &name))
return FALSE;
- if (isResourceVacation)
- proj->getResource(name)->addVacation(
- new Interval(from, to));
proj->addVacation(name, from, to);
break;
}
if ((tt = nextToken(token)) != COMMA)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
break;
}
}
if ((tt = nextToken(token)) != COMMA)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
break;
}
}
TokenType
ProjectFile::nextToken(QString& buf)
{
+ if (openFiles.isEmpty())
+ return EndOfFile;
+
TokenType tt;
while ((tt = openFiles.last()->nextToken(buf)) == EndOfFile)
{
task->setScheduling(Task::ASAP);
if ((tt = nextToken(token)) != COMMA)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
break;
}
}
task->setScheduling(Task::ALAP);
if ((tt = nextToken(token)) != COMMA)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
break;
}
}
task->addFlag(flag);
if ((tt = nextToken(token)) != COMMA)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
break;
}
}
bool
ProjectFile::readVacation(time_t& from, time_t& to, bool readName,
- QString* n, bool* isResourceVacation)
+ QString* n)
{
TokenType tt;
if (readName)
{
- /* If we find a string then we expect a global vacation
- * definition. If we find an ID then this is an out-of-scope
- * vacation definition for the resource with this particular
- * ID. */
- *isResourceVacation = FALSE;
- if ((tt = nextToken(*n)) == STRING)
- ; // We don't have to do anything
-#if 0
- else if (tt == ID)
- {
- if (!proj->getResource(*n))
- {
- fatalError(QString().sprintf(
- "Resource %s is undefined", n->latin1()));
- return FALSE;
- }
- *isResourceVacation = TRUE;
- }
-#endif
- else
+ if ((tt = nextToken(*n)) != STRING)
{
fatalError("String expected");
return FALSE;
if ((tt = nextToken(token)) != MINUS)
{
// vacation e. g. 2001-11-28
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
from = date2time(start);
to = sameTimeNextDay(date2time(start)) - 1;
}
return FALSE;
}
else
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
proj->addResource(r);
return FALSE;
}
}
+ else if (token == KW("planbooking"))
+ {
+ Booking* b;
+ if ((b = readBooking()) == 0)
+ return FALSE;
+ if (!r->addPlanBooking(b))
+ {
+ fatalError("Resource is already booked during this period");
+ return FALSE;
+ }
+ }
+ else if (token == KW("actualbooking"))
+ {
+ Booking* b;
+ if ((b = readBooking()) == 0)
+ return FALSE;
+ if (!r->addActualBooking(b))
+ {
+ fatalError("Resource is already booked during this period");
+ return FALSE;
+ }
+ }
else if (token == KW("flags"))
{
for ( ; ; )
r->addFlag(flag);
if ((tt = nextToken(token)) != COMMA)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
break;
}
}
}
}
else
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
proj->addShift(s);
return TRUE;
}
+Booking*
+ProjectFile::readBooking()
+{
+ QString token;
+
+ if (nextToken(token) != DATE)
+ {
+ fatalError("Start date expected");
+ return 0;
+ }
+ time_t start = date2time(token);
+ if (start < proj->getStart() || start >= proj->getEnd())
+ {
+ fatalError("Start date must be within the project timeframe");
+ return 0;
+ }
+
+ if (nextToken(token) != DATE)
+ {
+ fatalError("End date expected");
+ return 0;
+ }
+ time_t end = date2time(token);
+ if (end <= proj->getStart() || end > proj->getEnd())
+ {
+ fatalError("End date must be within the project timeframe");
+ return 0;
+ }
+ if (start >= end)
+ {
+ fatalError("End date must be after start date");
+ return 0;
+ }
+
+ QString pid;
+ if (nextToken(pid) != ID || !proj->isValidId(pid))
+ {
+ fatalError("Known project ID expected");
+ return 0;
+ }
+ Task* task;
+ TokenType tt;
+ if (((tt = nextToken(token)) != ID && tt != RELATIVE_ID) ||
+ (task = proj->getTask(token)) == 0)
+ {
+ fatalError("Task ID expected");
+ return 0;
+ }
+ return new Booking(Interval(start, end), task, "", pid);
+}
+
bool
ProjectFile::readAccount(Account* parent)
{
}
}
else
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
proj->addAccount(a);
}
a->addAlternative(r);
} while ((tt = nextToken(token)) == COMMA);
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
}
}
}
else
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
t->addAllocation(a);
return TRUE;
TokenType tt;
if ((tt = nextToken(token)) != LCBRACE)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
return TRUE;
}
report->addColumn(col);
if ((tt = nextToken(token)) != COMMA)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
break;
}
}
TokenType tt;
if ((tt = nextToken(token)) != LCBRACE)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
return TRUE;
}
report->addColumn(col);
if ((tt = nextToken(token)) != COMMA)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
break;
}
}
TokenType tt;
if ((tt = nextToken(token)) != LCBRACE)
{
- openFiles.last()->returnToken(tt, token);
+ returnToken(tt, token);
return TRUE;
}
ExpressionTree* et = new ExpressionTree(op);
report->setRollUpTask(et);
}
+ else if (token == KW("hideresource"))
+ {
+ Operation* op;
+ if ((op = readLogicalExpression()) == 0)
+ return FALSE;
+ ExpressionTree* et = new ExpressionTree(op);
+ report->setHideResource(et);
+ }
+ else if (token == KW("rollupresource"))
+ {
+ Operation* op;
+ if ((op = readLogicalExpression()) == 0)
+ return FALSE;
+ ExpressionTree* et = new ExpressionTree(op);
+ report->setRollUpResource(et);
+ }
else
{
fatalError("Illegal attribute");
time_t
ProjectFile::date2time(const QString& date)
{
- int y, m, d, hour, min;
+ int y, m, d, hour, min, sec;
char tZone[16] = "";
- if (sscanf(date, "%d-%d-%d-%d:%d-%s", &y, &m, &d, &hour, &min, tZone) == 6)
- ;
+ char savedTZ[16] = "";
+ if (sscanf(date, "%d-%d-%d-%d:%d:%d-%s",
+ &y, &m, &d, &hour, &min, &sec, tZone) == 7 ||
+ (sec = 0) || // set sec to 0
+ sscanf(date, "%d-%d-%d-%d:%d-%s",
+ &y, &m, &d, &hour, &min, tZone) == 6)
+ {
+ if (getenv("TZ"))
+ strcpy(getenv("TZ"), savedTZ);
+ const char* tz;
+ if ((tz = timezone2tz(tZone)) == 0)
+ fatalError(QString("Illegal timezone %s").arg(tZone));
+ else
+ setenv("TZ", tz, 1);
+ }
+ else if (sscanf(date, "%d-%d-%d-%d:%d:%d",
+ &y, &m, &d, &hour, &min, &sec) == 6)
+ tZone[0] = '\0';
else if (sscanf(date, "%d-%d-%d-%d:%d", &y, &m, &d, &hour, &min) == 5)
+ {
+ sec = 0;
tZone[0] = '\0';
+ }
else if (sscanf(date, "%d-%d-%d", &y, &m, &d) == 3)
- {
+ {
tZone[0] = '\0';
- hour = min = 0;
+ hour = min = sec = 0;
}
-
- char savedTZ[16] = "";
- if (strcmp(tZone, "") != 0)
+ else
{
- if (getenv("TZ"))
- strcpy(getenv("TZ"), savedTZ);
- setenv("TZ", tZone, 1);
+ qFatal("Illegal date: %s", date.latin1());
+ return 0;
}
-
+
if (y < 1970)
{
fatalError("Year must be larger than 1969");
d = 1;
}
- struct tm t = { 0, min, hour, d, m - 1, y - 1900, 0, 0, -1, 0, 0 };
+ struct tm t = { sec, min, hour, d, m - 1, y - 1900, 0, 0, -1, 0, 0 };
time_t localTime = mktime(&t);
if (strcmp(savedTZ, "") != 0)
TokenType nextToken(QString& token);
void returnToken(TokenType t, const QString& buf)
{
- openFiles.last()->returnToken(t, buf);
+ if (!openFiles.isEmpty())
+ openFiles.last()->returnToken(t, buf);
}
const QString& getFile() { return openFiles.last()->getFile(); }
bool readResourceSupplement();
bool readResourceBody(Resource* r);
bool readVacation(time_t& from, time_t& to, bool readName = FALSE,
- QString* = 0, bool* isResourceVacation = 0);
+ QString* = 0);
bool readAccount(Account* parent);
bool readShift(Shift* parent);
+ Booking* readBooking();
bool readCredit(Account* a);
bool readAllocate(Task* t);
bool readPlanTimeFrame(Task* t, double& d);
bool first = TRUE;
BookingList planJobs = r->getPlanJobs();
+ planJobs.setAutoDelete(TRUE);
for (Booking* b = planJobs.first(); b != 0; b = planJobs.next())
{
if (t == b->getTask())
bool first = TRUE;
BookingList actualJobs = r->getActualJobs();
+ actualJobs.setAutoDelete(TRUE);
for (Booking* b = actualJobs.first(); b != 0; b = actualJobs.next())
{
if (t == b->getTask())
if (planScoreboard)
{
for (uint i = 0; i < sbSize; i++)
- if (planScoreboard[i] > (Booking*) 3)
+ if (planScoreboard[i] > (SbBooking*) 3)
{
uint j;
for (j = i + 1; j < sbSize &&
if (actualScoreboard)
{
for (uint i = 0; i < sbSize; i++)
- if (actualScoreboard[i] > (Booking*) 3)
+ if (actualScoreboard[i] > (SbBooking*) 3)
{
uint j;
for (j = i + 1; j < sbSize &&
void
Resource::initScoreboard()
{
- scoreboard = new (Booking*)[sbSize];
+ scoreboard = new (SbBooking*)[sbSize];
// First mark all scoreboard slots as unavailable.
for (uint i = 0; i < sbSize; i++)
- scoreboard[i] = (Booking*) 1;
+ scoreboard[i] = (SbBooking*) 1;
// Then change all worktime slots to 0 (available) again.
for (time_t day = project->getStart(); day < project->getEnd();
for (time_t date = interval.getStart();
date < interval.getEnd() && date < project->getEnd();
date += project->getScheduleGranularity())
- scoreboard[sbIndex(date)] = (Booking*) 0;
+ scoreboard[sbIndex(date)] = (SbBooking*) 0;
}
}
date < i->getEnd() &&
project->getStart() <= date && date < project->getEnd();
date += project->getScheduleGranularity())
- scoreboard[sbIndex(date)] = (Booking*) 2;
+ scoreboard[sbIndex(date)] = (SbBooking*) 2;
// Mark all global vacation slots as such (2)
for (Interval* i = project->getVacationListFirst(); i != 0;
date < i->getEnd() &&
project->getStart() <= date && date < project->getEnd();
date += project->getScheduleGranularity())
- scoreboard[sbIndex(date)] = (Booking*) 2;
+ scoreboard[sbIndex(date)] = (SbBooking*) 2;
}
}
uint sbIdx = (date - project->getStart()) /
project->getScheduleGranularity();
if (sbIdx < 0 || sbIdx >= sbSize)
- qFatal("Date %s out of range (Resource %s, Index %d)",
+ qFatal("Date %s is outside of the defined project timeframe "
+ "(Resource %s, Index %d)",
time2ISO(date).latin1(),
id.latin1(), sbIdx);
return sbIdx;
}
+time_t
+Resource::index2start(uint idx) const
+{
+ return project->getStart() + idx *
+ project->getScheduleGranularity();
+}
+
+time_t
+Resource::index2end(uint idx) const
+{
+ return project->getStart() + (idx + 1) *
+ project->getScheduleGranularity() - 1;
+}
+
void
Resource::getSubResourceList(ResourceList& rl)
{
for (uint i = sbStart; i < sbEnd; i++)
{
- Booking* b = scoreboard[i];
- if (b < (Booking*) 4)
+ SbBooking* b = scoreboard[i];
+ if (b < (SbBooking*) 4)
continue;
bookedTime += duration;
<= (loadFactor / 100.0);
}
-void
+bool
Resource::book(Booking* nb)
{
- uint index = sbIndex(nb->getStart());
+ uint idx = sbIndex(nb->getStart());
- Booking* b;
+ return bookSlot(idx, nb);
+}
+
+bool
+Resource::bookSlot(uint idx, SbBooking* nb)
+{
+ // Test if the time slot is still available.
+ if (scoreboard[idx] != 0)
+ return FALSE;
+
+ SbBooking* b;
// Try to merge the booking with the booking in the previous slot.
- if (index > 0 && (b = scoreboard[index - 1]) > (Booking*) 3 &&
+ if (idx > 0 && (b = scoreboard[idx - 1]) > (SbBooking*) 3 &&
b->getTask() == nb->getTask() &&
b->getProjectId() == nb->getProjectId())
{
- if (!b->getInterval().append(nb->getInterval()))
- qFatal("Cannot append time slot");
- scoreboard[index] = b;
+ scoreboard[idx] = b;
delete nb;
- return;
+ return TRUE;
}
// Try to merge the booking with the booking in the following slot.
- if (index < sbSize - 1 && (b = scoreboard[index + 1]) > (Booking*) 3 &&
+ if (idx < sbSize - 1 && (b = scoreboard[idx + 1]) > (SbBooking*) 3 &&
b->getTask() == nb->getTask() &&
b->getProjectId() == nb->getProjectId())
{
- if (!b->getInterval().prepend(nb->getInterval()))
- qFatal("Cannot prepend time slot");
- scoreboard[index] = b;
+ scoreboard[idx] = b;
delete nb;
- return;
+ return TRUE;
}
- scoreboard[index] = nb;
+ scoreboard[idx] = nb;
+ return TRUE;
+}
+
+bool
+Resource::bookInterval(Booking* nb)
+{
+ uint sIdx = sbIndex(nb->getStart());
+ uint eIdx = sbIndex(nb->getEnd());
+
+ for (uint i = sIdx; i <= eIdx; i++)
+ if (scoreboard[i])
+ {
+ qWarning("Resource %s is already booked at %s",
+ id.latin1(), time2ISO(index2start(i)).latin1());
+ return FALSE;
+ }
+ for (uint i = sIdx; i <= eIdx; i++)
+ bookSlot(i, new SbBooking(*nb));
+
+ return TRUE;
+}
+
+bool
+Resource::addPlanBooking(Booking* nb)
+{
+ SbBooking** tmp = scoreboard;
+
+ if (planScoreboard)
+ scoreboard = planScoreboard;
+ else
+ initScoreboard();
+ bool retVal = bookInterval(nb);
+ // Cross register booking with task.
+ if (retVal && nb->getTask())
+ nb->getTask()->addPlanBookedResource(this);
+ planScoreboard = scoreboard;
+ scoreboard = tmp;
+ return retVal;
+}
+
+bool
+Resource::addActualBooking(Booking* nb)
+{
+ SbBooking** tmp = scoreboard;
+
+ if (actualScoreboard)
+ scoreboard = actualScoreboard;
+ else
+ initScoreboard();
+ bool retVal = bookInterval(nb);
+ // Cross register booking with task.
+ if (retVal && nb->getTask())
+ nb->getTask()->addActualBookedResource(this);
+ actualScoreboard = scoreboard;
+ scoreboard = tmp;
+ return retVal;
}
double
for (uint i = sbIndex(iv.getStart());
i < sbIndex(iv.getEnd()) && i < sbSize; i++)
{
- Booking* b = planScoreboard[i];
- if (b < (Booking*) 4)
+ SbBooking* b = planScoreboard[i];
+ if (b < (SbBooking*) 4)
continue;
if (task == 0 || task == b->getTask())
bookedTime += project->getScheduleGranularity();
for (Resource* r = subFirst(); r != 0; r = subNext())
subLoad += r->getActualLoad(iv, task);
- for (uint i = sbIndex(iv.getStart());
- i < sbIndex(iv.getEnd()) && i < sbSize; i++)
- {
- Booking* b = actualScoreboard[i];
- if (b < (Booking*) 4)
- continue;
- if (task == 0 || task == b->getTask())
- bookedTime += project->getScheduleGranularity();
- }
+ if (actualScoreboard)
+ for (uint i = sbIndex(iv.getStart());
+ i < sbIndex(iv.getEnd()) && i < sbSize; i++)
+ {
+ SbBooking* b = actualScoreboard[i];
+ if (b < (SbBooking*) 4)
+ continue;
+ if (task == 0 || task == b->getTask())
+ bookedTime += project->getScheduleGranularity();
+ }
return project->convertToDailyLoad(bookedTime) * efficiency + subLoad;
}
for (uint i = sbIndex(iv.getStart());
i < sbIndex(iv.getEnd()) && i < sbSize; i++)
{
- Booking* b = planScoreboard[i];
- if (b < (Booking*) 4)
+ SbBooking* b = planScoreboard[i];
+ if (b < (SbBooking*) 4)
continue;
if ((task == 0 || task == b->getTask()) &&
pids.findIndex(b->getProjectId()) == -1)
for (Resource* r = subFirst(); r != 0; r = subNext())
r->getActualPIDs(iv, task, pids);
- for (uint i = sbIndex(iv.getStart());
- i < sbIndex(iv.getEnd()) && i < sbSize; i++)
- {
- Booking* b = actualScoreboard[i];
- if (b < (Booking*) 4)
- continue;
- if ((task == 0 || task == b->getTask()) &&
- pids.findIndex(b->getProjectId()) == -1)
+ if (actualScoreboard)
+ for (uint i = sbIndex(iv.getStart());
+ i < sbIndex(iv.getEnd()) && i < sbSize; i++)
{
- pids.append(b->getProjectId());
+ SbBooking* b = actualScoreboard[i];
+ if (b < (SbBooking*) 4)
+ continue;
+ if ((task == 0 || task == b->getTask()) &&
+ pids.findIndex(b->getProjectId()) == -1)
+ {
+ pids.append(b->getProjectId());
+ }
}
- }
}
QString
Resource::getPlanJobs()
{
BookingList bl;
-
- Booking* b = 0;
- for (uint i = 0; i < sbSize; i++)
- if (planScoreboard[i] > (Booking*) 3 && planScoreboard[i] != b)
- {
- bl.append(planScoreboard[i]);
- b = planScoreboard[i];
- }
-
+ if (planScoreboard)
+ {
+ SbBooking* b = 0;
+ uint startIdx = 0;
+ for (uint i = 0; i < sbSize; i++)
+ if (planScoreboard[i] != b)
+ {
+ if (b)
+ bl.append(new Booking(Interval(index2start(startIdx),
+ index2end(i - 1)),
+ planScoreboard[startIdx]));
+ if (planScoreboard[i] > (SbBooking*) 3)
+ {
+ b = planScoreboard[i];
+ startIdx = i;
+ }
+ else
+ b = 0;
+ }
+ }
return bl;
}
Resource::getActualJobs()
{
BookingList bl;
-
- Booking* b = 0;
- for (uint i = 0; i < sbSize; i++)
- if (actualScoreboard[i] > (Booking*) 3 && actualScoreboard[i] != b)
- {
- bl.append(actualScoreboard[i]);
- b = actualScoreboard[i];
- }
+ if (actualScoreboard)
+ {
+ SbBooking* b = 0;
+ uint startIdx = 0;
+ for (uint i = 0; i < sbSize; i++)
+ if (actualScoreboard[i] != b)
+ {
+ if (b)
+ bl.append(new Booking(Interval(index2start(startIdx),
+ index2end(i - 1)),
+ actualScoreboard[startIdx]));
+ if (actualScoreboard[i] > (SbBooking*) 3)
+ {
+ b = actualScoreboard[i];
+ startIdx = i;
+ }
+ else
+ b = 0;
+ }
+ }
return bl;
}
void
Resource::preparePlan()
{
- initScoreboard();
+ if (planScoreboard)
+ scoreboard = planScoreboard;
+ else
+ initScoreboard();
}
void
void
Resource::prepareActual()
{
- initScoreboard();
+ if (actualScoreboard)
+ scoreboard = actualScoreboard;
+ else
+ initScoreboard();
}
void
class Project;
class Task;
+class Resource;
-class Booking
+class SbBooking
+{
+public:
+ SbBooking(Task* t, QString a = "", QString i = "")
+ : task(t), account(a), projectId(i) { }
+ ~SbBooking() { }
+
+ Task* getTask() const { return task; }
+
+ void setAccount(const QString a) { account = a; }
+ const QString& getAccount() const { return account; }
+
+ void setProjectId(const QString i) { projectId = i; }
+ const QString& getProjectId() const { return projectId; }
+
+private:
+ // A pointer to the task that caused the booking
+ Task* task;
+ // String identifying the KoTrus account the effort is credited to.
+ QString account;
+ // The Project ID
+ QString projectId;
+};
+
+class Booking : public SbBooking
{
public:
Booking(const Interval& iv, Task* t, QString a = "",
QString i = "")
- : interval(iv), task(t), account(a), projectId(i) { }
+ : SbBooking(t, a, i), interval(iv) { }
+ Booking(const Interval& iv, SbBooking* sb) : SbBooking(*sb),
+ interval(iv) { }
~Booking() { }
time_t getStart() const { return interval.getStart(); }
time_t getDuration() const { return interval.getDuration(); }
Interval& getInterval() { return interval; }
- Task* getTask() const { return task; }
-
- void setAccount(const QString a) { account = a; }
- const QString& getAccount() const { return account; }
-
- void setProjectId(const QString i) { projectId = i; }
- const QString& getProjectId() const { return projectId; }
-
void setLockTS( const QString& ts ) { lockTS = ts; }
const QString& getLockTS() const { return lockTS; }
private:
// The booked time period.
Interval interval;
- // A pointer to the task that caused the booking
- Task* task;
- // String identifying the KoTrus account the effort is credited to.
- QString account;
- // The Project ID
- QString projectId;
-
// The database lock timestamp
QString lockTS;
// the lockers ID
QString lockerId;
-
- // A list of flags that can be used to select a sub-set of tasks.
- FlagList flagList;
} ;
class BookingList : public QPtrList<Booking>
bool isAvailable(time_t day, time_t duration, int loadFactor, Task* t);
- void book(Booking* b);
+ bool book(Booking* b);
+
+ bool bookSlot(uint idx, SbBooking* nb);
+ bool bookInterval(Booking* b);
+ bool addPlanBooking(Booking* b);
+ bool addActualBooking(Booking* b);
double getPlanLoad(const Interval& i, Task* task = 0);
void initScoreboard();
uint sbIndex(time_t date) const;
+ time_t index2start(uint idx) const;
+ time_t index2end(uint idx) const;
+
/// Pointer used by subResourceFirst() and subResourceNext().
Resource* currentSR;
* For each time slot (of length scheduling granularity) we store a
* pointer to a booking, a '1' if slot is off-hours, a '2' if slot is
* during a vacation or 0 if resource is available. */
- Booking** scoreboard;
+ SbBooking** scoreboard;
/// The number of time slots in the project.
uint sbSize;
/// Scoring of planned usages of the resource.
- Booking** planScoreboard;
+ SbBooking** planScoreboard;
/// Scoring of actual usages of the resource.
- Booking** actualScoreboard;
+ SbBooking** actualScoreboard;
} ;
#endif
lastSlot = 0;
schedulingDone = FALSE;
bookedResources.clear();
+ bookedResources = planBookedResources;
if (actualStart == 0.0)
actualStart = planStart;
lastSlot = 0;
schedulingDone = FALSE;
bookedResources.clear();
+ bookedResources = actualBookedResources;
}
void
double getPlanLoad(const Interval& period, Resource* resource = 0);
+ void addPlanBookedResource(Resource* r)
+ {
+ if (planBookedResources.find(r) == -1)
+ planBookedResources.inSort(r);
+ }
bool isPlanBookedResource(Resource* r)
{
return planBookedResources.find(r) != -1;
bool isActualActive(const Interval& period) const;
double getActualLoad(const Interval& period, Resource* r = 0);
+ void addActualBookedResource(Resource* r)
+ {
+ if (actualBookedResources.find(r) == -1)
+ actualBookedResources.inSort(r);
+ }
bool isActualBookedResource(Resource* r)
{
return actualBookedResources.find(r) != -1;
if (bookedResources.find(r) == -1)
bookedResources.inSort(r);
}
-
time_t earliestStart();
time_t latestEnd();
void fatalError(const QString& msg) const;
#include <stdio.h>
#include "Utility.h"
+#include <qdict.h>
+
+static QDict<const char> TZDict;
+static bool TZDictReady = FALSE;
+
+const char*
+timezone2tz(const char* tzone)
+{
+ if (!TZDictReady)
+ {
+ TZDict.insert("PST", "GMP8:00");
+ TZDict.insert("PDT", "GMT7:00");
+ TZDict.insert("MST", "GMT7:00");
+ TZDict.insert("MDT", "GMT6:00");
+ TZDict.insert("CST", "GMT6:00");
+ TZDict.insert("CDT", "GMT5:00");
+ TZDict.insert("EST", "GMT5:00");
+ TZDict.insert("EDT", "GMT4:00");
+ TZDict.insert("CET", "GMT-1:00");
+ TZDict.insert("CEST", "GMT-2:00");
+
+ TZDictReady = TRUE;
+ }
+
+ return TZDict[tzone];
+}
+
const char*
monthAndYear(time_t t)
{
return buf;
}
+QString time2tjp(time_t t)
+{
+ struct tm* tms = localtime(&t);
+ static char buf[128];
+
+ strftime(buf, 127, "%Y-%m-%d-%H:%M:%S-%Z", tms);
+ return buf;
+}
+
QString time2time(time_t t)
{
struct tm* tms = localtime(&t);
#define ONEDAY (60 * 60 * 24)
#define ONEHOUR (60 * 60)
+const char* timezone2tz(const char* tzone);
+
const char* monthAndYear(time_t d);
int daysLeftInMonth(time_t d);
QString time2ISO(time_t t);
+QString time2tjp(time_t t);
+
QString time2time(time_t t);
QString time2date(time_t t);