OSDN Git Service

* Support for later completion of task and resources added. By
[tjqt4port/tj2qt4.git] / taskjuggler / Project.cpp
1 /*
2  * Project.cpp - TaskJuggler
3  *
4  * Copyright (c) 2001, 2002 by Chris Schlaeger <cs@suse.de>
5  *
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.
9  *
10  * $Id$
11  */
12
13 #include <config.h>
14 #include <stdio.h>
15
16 #include <qdict.h>
17
18 #include "Project.h"
19 #include "Utility.h"
20 #include "kotrus.h"
21
22 int Project::debugLevel = 0;
23
24 Project::Project()
25 {
26         taskList.setAutoDelete(TRUE);
27         resourceList.setAutoDelete(TRUE);
28         activeAsap.setSorting(CoreAttributesList::PrioDown);
29         activeAlap.setSorting(CoreAttributesList::PrioDown);
30         priority = 500;
31         dailyWorkingHours = 8.0;
32         scheduleGranularity = ONEHOUR;
33         start = 0;
34         end = 0;
35         now = time(0);
36         copyright = "";
37         minEffort = 0.0;
38         maxEffort = 1.0;
39         rate = 0.0;
40         currency = "";
41         currencyDigits = 3;
42         kotrus = 0;
43         xmlreport = 0;
44 }
45
46 bool
47 Project::addId(const QString& id)
48 {
49         if (projectIDs.findIndex(id) != -1)
50                 return FALSE;
51         else
52                 projectIDs.append(id);
53         return TRUE;
54 }
55
56 QString
57 Project::getIdIndex(const QString& i) const
58 {
59         int idx;
60         if ((idx = projectIDs.findIndex(i)) == -1)
61                 return QString("?");
62         QString idxStr;
63         do
64         {
65                 idxStr = QChar('A' + idx % ('Z' - 'A')) + idxStr;
66                 idx /= 'Z' - 'A';
67         } while (idx > 'Z' - 'A');
68
69         return idxStr;
70 }
71
72 bool
73 Project::addTask(Task* t)
74 {
75         taskList.append(t);
76         return TRUE;
77 }
78
79 bool
80 Project::pass2()
81 {
82         QDict<Task> idHash;
83         bool error = FALSE;
84
85         taskList.createIndex();
86         resourceList.createIndex();
87         accountList.createIndex();
88
89         // Create hash to map task IDs to pointers.
90         for (Task* t = taskList.first(); t != 0; t = taskList.next())
91         {
92                 idHash.insert(t->getId(), t);
93         }
94
95         // Create cross links from dependency lists.
96         for (Task* t = taskList.first(); t != 0; t = taskList.next())
97         {
98                 if (!t->xRef(idHash))
99                         error = TRUE;
100         }
101
102         bool hasActualValues = FALSE;
103         for (Task* t = taskList.first(); t != 0; t = taskList.next())
104         {
105                 if (!t->preScheduleOk())
106                         error = TRUE;
107                 if (t->hasActualValues())
108                         hasActualValues = TRUE;
109         }
110
111         if (error)
112                 return FALSE;
113
114         if (debugLevel > 0)
115                 qWarning("Scheduling plan scenario...");
116         preparePlan();
117         schedule();
118         finishPlan();
119
120         if (hasActualValues)
121         {
122                 if (debugLevel > 0)
123                         qWarning("Scheduling actual scenario...");
124                 prepareActual();
125                 schedule();
126                 finishActual();
127         }
128
129         return TRUE;
130 }
131
132 void
133 Project::preparePlan()
134 {
135         for (Task* t = taskList.first(); t != 0; t = taskList.next())
136                 t->preparePlan();
137         for (Task* t = taskList.first(); t != 0; t = taskList.next())
138                 t->propagateInitialValues();
139         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
140                 r->preparePlan();
141 }
142
143 void
144 Project::finishPlan()
145 {
146         for (Task* t = taskList.first(); t != 0; t = taskList.next())
147                 t->finishPlan();
148         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
149                 r->finishPlan();
150 }
151
152 void
153 Project::prepareActual()
154 {
155         for (Task* t = taskList.first(); t != 0; t = taskList.next())
156                 t->prepareActual();
157         for (Task* t = taskList.first(); t != 0; t = taskList.next())
158                 t->propagateInitialValues();
159         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
160                 r->prepareActual();
161 }
162
163 void
164 Project::finishActual()
165 {
166         for (Task* t = taskList.first(); t != 0; t = taskList.next())
167                 t->finishActual();
168         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
169                 r->finishActual();
170 }
171
172 bool
173 Project::schedule()
174 {
175         bool error = FALSE;
176
177         TaskList sortedTasks(taskList);
178         sortedTasks.setSorting(CoreAttributesList::PrioDown);
179         sortedTasks.sort();
180
181         time_t timeDelta = scheduleGranularity;
182         bool done = FALSE;
183         time_t day;
184
185         updateActiveTaskList(sortedTasks);
186         for (day = start; !(activeAsap.isEmpty() && activeAlap.isEmpty()) &&
187                  day >= start && day < end; day += timeDelta)
188         {
189                 timeDelta = scheduleGranularity;
190                 do
191                 {
192                         done = TRUE;
193                         for (Task* t = activeAlap.first(); t != 0; t = activeAlap.next())
194                         {
195                                 if (!t->schedule(day, scheduleGranularity))
196                                 {
197                                         done = FALSE;
198                                         break;  // Start with top priority tasks again.
199                                 }
200                                 if (t->needsEarlierTimeSlot(day + scheduleGranularity))
201                                         timeDelta = -scheduleGranularity;
202                         }
203                 } while (!done);
204
205                 if (timeDelta < 0)
206                         continue;
207                 uint i = 0;
208                 do
209                 {
210                         done = TRUE;
211                         i = 0;
212                         for (Task* t = activeAsap.first(); t != 0; t = activeAsap.next())
213                         {
214                                 i++;
215                                 if (!t->schedule(day, scheduleGranularity))
216                                 {
217                                         done = FALSE;
218                                         break;  // Start with top priority tasks again.
219                                 }
220                         }
221                 } while (!done);
222                 if (i != activeAsap.count())
223                         qFatal("activeAsap list corrupted");
224         }
225
226         if (!activeAsap.isEmpty() || !activeAlap.isEmpty())
227         {
228                 for (Task* t = activeAsap.first(); t != 0; t = activeAsap.next())
229                         qWarning("Task %s does not fit into the project time frame",
230                                          t->getId().latin1());
231                 for (Task* t = activeAlap.first(); t != 0; t = activeAlap.next())
232                         qWarning("Task %s does not fit into the project time frame",
233                                          t->getId().latin1());
234                 error = TRUE;
235         }
236
237         if (!checkSchedule())
238                 error = TRUE;
239
240         return !error;
241 }
242
243 void
244 Project::updateActiveTaskList(TaskList& sortedTasks)
245 {
246         for (Task* t = sortedTasks.first(); t != 0; t = sortedTasks.next())
247                 if (t->isActive())
248                         addActiveTask(t);
249 }
250
251 bool
252 Project::checkSchedule()
253 {
254         TaskList tl = taskList;
255         tl.setSorting(CoreAttributesList::StartDown);
256         tl.sort();
257         int errors = 0;
258         for (Task* t = tl.first(); t != 0; t = tl.next())
259         {
260                 if (!t->scheduleOk())
261                         errors++;
262                 if (errors >= 10)
263                         break;
264         }
265
266         return errors == 0;
267 }
268
269 void
270 Project::setKotrus(Kotrus* k)
271 {
272         if (kotrus)
273                 delete kotrus;
274         kotrus = k;
275 }
276
277 void
278 Project::generateReports()
279 {
280         if (debugLevel > 0)
281                 qWarning("Generating reports...");
282
283         // Generate task reports
284         for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
285                  h = htmlTaskReports.next())
286                 h->generate();
287
288         // Generate resource reports
289         for (HTMLResourceReport* r = htmlResourceReports.first(); r != 0;
290                  r = htmlResourceReports.next())
291                 r->generate();
292
293         // Generate account reports
294         for (HTMLAccountReport* r = htmlAccountReports.first(); r != 0;
295                  r = htmlAccountReports.next())
296                 r->generate();
297
298         for (ExportReport* e = exportReports.first(); e != 0;
299                  e = exportReports.next())
300                 e->generate();
301
302         if( xmlreport )
303            xmlreport->generate();
304 #ifdef HAVE_KDE
305         if( icalReport )
306            icalReport->generate();
307 #endif
308 }
309
310 bool
311 Project::needsActualDataForReports()
312 {
313         bool needsActual = FALSE;
314
315         // Generate task reports
316         for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
317                  h = htmlTaskReports.next())
318                 if (h->getShowActual())
319                         needsActual = TRUE;
320         // Generate resource reports
321         for (HTMLResourceReport* h = htmlResourceReports.first(); h != 0;
322                  h = htmlResourceReports.next())
323                 if (h->getShowActual())
324                         needsActual = TRUE;
325
326         // Generate account reports
327         for (HTMLAccountReport* h = htmlAccountReports.first(); h != 0;
328                  h = htmlAccountReports.next())
329                 if (h->getShowActual())
330                         needsActual = TRUE;
331
332         return needsActual;
333 }
334
335 void
336 Project::removeActiveTask(Task* t)
337 {
338         t->setScheduled();
339
340         if (debugLevel > 2)
341                 qWarning("Deactivating %s", t->getId().latin1());
342
343         if (t->getScheduling() == Task::ASAP)
344                 activeAsap.removeRef(t);
345         else
346                 activeAlap.removeRef(t);
347 }
348
349 void
350 Project::addActiveTask(Task* t)
351 {
352         if (t->getScheduling() == Task::ASAP)
353         {
354                 if (activeAsap.findRef(t) == -1)
355                 {
356                         if (debugLevel > 2)
357                                 qWarning("Activating %s", t->getId().latin1());
358                         activeAsap.inSort(t);
359                 }
360         }
361         else
362         {
363                 if (activeAlap.findRef(t) == -1)
364                 {
365                         if (debugLevel > 2)
366                                 qWarning("Activating %s", t->getId().latin1()); 
367                         activeAlap.inSort(t);
368                 }
369         }
370 }
371
372 bool
373 Project::readKotrus()
374 {
375         if (!kotrus)
376                 return TRUE;
377                 
378         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
379                 r->dbLoadBookings(r->getKotrusId(), 0);
380
381         return TRUE;
382 }
383
384 bool
385 Project::updateKotrus()
386 {
387         return TRUE;
388 }
389