OSDN Git Service

Fixed but in handling of default working hours.
[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
14 #include <config.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <qdom.h>
18 #include <qdict.h>
19
20 #include "Project.h"
21 #include "Utility.h"
22 #include "kotrus.h"
23
24 int Project::debugLevel = 0;
25
26 Project::Project()
27 {
28         taskList.setAutoDelete(TRUE);
29         resourceList.setAutoDelete(TRUE);
30         accountList.setAutoDelete(TRUE);
31         activeAsap.setSorting(CoreAttributesList::PrioDown);
32         activeAlap.setSorting(CoreAttributesList::PrioDown);
33         priority = 500;
34         /* The following settings are country and culture dependent. Those
35          * defaults are probably true for many Western countries, but have to be
36          * changed in project files. */
37         dailyWorkingHours = 8.0;
38         yearlyWorkingDays = 252;
39         scheduleGranularity = ONEHOUR;
40         start = 0;
41         end = 0;
42         now = time(0);
43         copyright = "";
44         minEffort = 0.0;
45         maxEffort = 1.0;
46         rate = 0.0;
47         currency = "";
48         currencyDigits = 3;
49         kotrus = 0;
50         xmlreport = 0;
51 #ifdef HAVE_ICAL
52 #ifdef HAVE_KDE
53         icalReport = 0;
54 #endif
55 #endif
56         
57         /* Initialize working hours with default values that match the Monday -
58          * Friday 9 - 6 (with 1 hour lunch break) pattern used by many western
59          * countries. */
60         // Sunday
61         workingHours[0] = new QPtrList<Interval>();
62         workingHours[0]->setAutoDelete(TRUE);
63
64         // Monday
65         workingHours[1] = new QPtrList<Interval>();
66         workingHours[1]->setAutoDelete(TRUE);
67         workingHours[1]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
68         workingHours[1]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
69         // Tuesday
70         workingHours[2] = new QPtrList<Interval>();
71         workingHours[2]->setAutoDelete(TRUE);
72         workingHours[2]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
73         workingHours[2]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
74         // Wednesday
75         workingHours[3] = new QPtrList<Interval>();
76         workingHours[3]->setAutoDelete(TRUE);
77         workingHours[3]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
78         workingHours[3]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
79         // Thursday
80         workingHours[4] = new QPtrList<Interval>();
81         workingHours[4]->setAutoDelete(TRUE);
82         workingHours[4]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
83         workingHours[4]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
84         // Friday
85         workingHours[5] = new QPtrList<Interval>();
86         workingHours[5]->setAutoDelete(TRUE);
87         workingHours[5]->append(new Interval(9 * ONEHOUR, 12 * ONEHOUR - 1));
88         workingHours[5]->append(new Interval(13 * ONEHOUR, 18 * ONEHOUR - 1));
89
90         // Saturday
91         workingHours[6] = new QPtrList<Interval>();
92         workingHours[6]->setAutoDelete(TRUE);
93 }
94
95 bool
96 Project::addId(const QString& id)
97 {
98         if (projectIDs.findIndex(id) != -1)
99                 return FALSE;
100         else
101                 projectIDs.append(id);
102         return TRUE;
103 }
104
105 QString
106 Project::getIdIndex(const QString& i) const
107 {
108         int idx;
109         if ((idx = projectIDs.findIndex(i)) == -1)
110                 return QString("?");
111         QString idxStr;
112         do
113         {
114                 idxStr = QChar('A' + idx % ('Z' - 'A')) + idxStr;
115                 idx /= 'Z' - 'A';
116         } while (idx > 'Z' - 'A');
117
118         return idxStr;
119 }
120
121 bool
122 Project::addTask(Task* t)
123 {
124         taskList.append(t);
125         return TRUE;
126 }
127
128 bool
129 Project::pass2()
130 {
131         QDict<Task> idHash;
132         bool error = FALSE;
133
134         taskList.createIndex();
135         resourceList.createIndex();
136         accountList.createIndex();
137
138         // Initialize random generator.
139         srand((int) start);
140         
141         // Create hash to map task IDs to pointers.
142         for (Task* t = taskList.first(); t != 0; t = taskList.next())
143         {
144                 idHash.insert(t->getId(), t);
145         }
146
147         // Create cross links from dependency lists.
148         for (Task* t = taskList.first(); t != 0; t = taskList.next())
149         {
150                 if (!t->xRef(idHash))
151                         error = TRUE;
152         }
153
154         bool hasActualValues = FALSE;
155         for (Task* t = taskList.first(); t != 0; t = taskList.next())
156         {
157                 if (!t->preScheduleOk())
158                         error = TRUE;
159                 if (t->hasActualValues())
160                         hasActualValues = TRUE;
161         }
162
163         if (error)
164                 return FALSE;
165
166         if (debugLevel > 0)
167                 qWarning("Scheduling plan scenario...");
168         preparePlan();
169         schedule();
170         finishPlan();
171
172         if (hasActualValues)
173         {
174                 if (debugLevel > 0)
175                         qWarning("Scheduling actual scenario...");
176                 prepareActual();
177                 schedule();
178                 finishActual();
179         }
180
181         for (Task* t = taskList.first(); t != 0; t = taskList.next())
182                 t->computeBuffers();
183
184         return TRUE;
185 }
186
187 void
188 Project::preparePlan()
189 {
190         for (Task* t = taskList.first(); t != 0; t = taskList.next())
191                 t->preparePlan();
192         for (Task* t = taskList.first(); t != 0; t = taskList.next())
193                 t->propagateInitialValues();
194         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
195                 r->preparePlan();
196 }
197
198 void
199 Project::finishPlan()
200 {
201         for (Task* t = taskList.first(); t != 0; t = taskList.next())
202                 t->finishPlan();
203         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
204                 r->finishPlan();
205 }
206
207 void
208 Project::prepareActual()
209 {
210         for (Task* t = taskList.first(); t != 0; t = taskList.next())
211                 t->prepareActual();
212         for (Task* t = taskList.first(); t != 0; t = taskList.next())
213                 t->propagateInitialValues();
214         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
215                 r->prepareActual();
216 }
217
218 void
219 Project::finishActual()
220 {
221         for (Task* t = taskList.first(); t != 0; t = taskList.next())
222                 t->finishActual();
223         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
224                 r->finishActual();
225 }
226
227 bool
228 Project::schedule()
229 {
230         bool error = FALSE;
231
232         TaskList sortedTasks(taskList);
233         sortedTasks.setSorting(CoreAttributesList::PrioDown);
234         sortedTasks.sort();
235
236         time_t timeDelta = scheduleGranularity;
237         bool done = FALSE;
238         time_t day;
239
240         updateActiveTaskList(sortedTasks);
241         for (day = start; !(activeAsap.isEmpty() && activeAlap.isEmpty()) &&
242                  day >= start && day < end; day += timeDelta)
243         {
244                 timeDelta = scheduleGranularity;
245                 do
246                 {
247                         done = TRUE;
248                         for (Task* t = activeAlap.first(); t != 0; t = activeAlap.next())
249                         {
250                                 if (!t->schedule(day, scheduleGranularity))
251                                 {
252                                         done = FALSE;
253                                         break;  // Start with top priority tasks again.
254                                 }
255                                 if (t->needsEarlierTimeSlot(day + scheduleGranularity))
256                                         timeDelta = -scheduleGranularity;
257                         }
258                 } while (!done);
259
260                 if (timeDelta < 0)
261                         continue;
262                 uint i = 0;
263                 do
264                 {
265                         done = TRUE;
266                         i = 0;
267                         for (Task* t = activeAsap.first(); t != 0; t = activeAsap.next())
268                         {
269                                 i++;
270                                 if (!t->schedule(day, scheduleGranularity))
271                                 {
272                                         done = FALSE;
273                                         break;  // Start with top priority tasks again.
274                                 }
275                         }
276                 } while (!done);
277                 if (i != activeAsap.count())
278                         qFatal("activeAsap list corrupted");
279         }
280
281         if (!activeAsap.isEmpty() || !activeAlap.isEmpty())
282         {
283                 for (Task* t = activeAsap.first(); t != 0; t = activeAsap.next())
284                         qWarning("Task %s does not fit into the project time frame",
285                                          t->getId().latin1());
286                 for (Task* t = activeAlap.first(); t != 0; t = activeAlap.next())
287                         qWarning("Task %s does not fit into the project time frame",
288                                          t->getId().latin1());
289                 error = TRUE;
290         }
291
292         if (!checkSchedule())
293                 error = TRUE;
294
295         return !error;
296 }
297
298 void
299 Project::updateActiveTaskList(TaskList& sortedTasks)
300 {
301         for (Task* t = sortedTasks.first(); t != 0; t = sortedTasks.next())
302                 if (t->isActive())
303                         addActiveTask(t);
304 }
305
306 bool
307 Project::checkSchedule()
308 {
309         TaskList tl = taskList;
310         tl.setSorting(CoreAttributesList::StartDown);
311         tl.sort();
312         int errors = 0;
313         for (Task* t = tl.first(); t != 0; t = tl.next())
314         {
315                 if (!t->scheduleOk())
316                         errors++;
317                 if (errors >= 10)
318                         break;
319         }
320
321         return errors == 0;
322 }
323
324 void
325 Project::setKotrus(Kotrus* k)
326 {
327         if (kotrus)
328                 delete kotrus;
329         kotrus = k;
330 }
331
332 void
333 Project::generateReports()
334 {
335         if (debugLevel > 0)
336                 qWarning("Generating reports...");
337
338         // Generate task reports
339         for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
340                  h = htmlTaskReports.next())
341                 h->generate();
342
343         // Generate resource reports
344         for (HTMLResourceReport* r = htmlResourceReports.first(); r != 0;
345                  r = htmlResourceReports.next())
346                 r->generate();
347
348         // Generate account reports
349         for (HTMLAccountReport* r = htmlAccountReports.first(); r != 0;
350                  r = htmlAccountReports.next())
351                 r->generate();
352
353         for (ExportReport* e = exportReports.first(); e != 0;
354                  e = exportReports.next())
355                 e->generate();
356
357         if( xmlreport )
358            xmlreport->generate();
359 #ifdef HAVE_ICAL
360 #ifdef HAVE_KDE
361         if( icalReport )
362            icalReport->generate();
363 #endif
364 #endif
365
366 }
367
368 bool
369 Project::needsActualDataForReports()
370 {
371         bool needsActual = FALSE;
372
373         // Generate task reports
374         for (HTMLTaskReport* h = htmlTaskReports.first(); h != 0;
375                  h = htmlTaskReports.next())
376                 if (h->getShowActual())
377                         needsActual = TRUE;
378         // Generate resource reports
379         for (HTMLResourceReport* h = htmlResourceReports.first(); h != 0;
380                  h = htmlResourceReports.next())
381                 if (h->getShowActual())
382                         needsActual = TRUE;
383
384         // Generate account reports
385         for (HTMLAccountReport* h = htmlAccountReports.first(); h != 0;
386                  h = htmlAccountReports.next())
387                 if (h->getShowActual())
388                         needsActual = TRUE;
389
390         return needsActual;
391 }
392
393 void
394 Project::removeActiveTask(Task* t)
395 {
396         t->setScheduled();
397
398         if (debugLevel > 2)
399                 qWarning("Deactivating %s", t->getId().latin1());
400
401         if (t->getScheduling() == Task::ASAP)
402                 activeAsap.removeRef(t);
403         else
404                 activeAlap.removeRef(t);
405 }
406
407 void
408 Project::addActiveTask(Task* t)
409 {
410         if (t->getScheduling() == Task::ASAP)
411         {
412                 if (activeAsap.findRef(t) == -1)
413                 {
414                         if (debugLevel > 2)
415                                 qWarning("Activating %s", t->getId().latin1());
416                         activeAsap.inSort(t);
417                 }
418         }
419         else
420         {
421                 if (activeAlap.findRef(t) == -1)
422                 {
423                         if (debugLevel > 2)
424                                 qWarning("Activating %s", t->getId().latin1()); 
425                         activeAlap.inSort(t);
426                 }
427         }
428 }
429
430 bool
431 Project::readKotrus()
432 {
433         if (!kotrus)
434                 return TRUE;
435                 
436         for (Resource* r = resourceList.first(); r != 0; r = resourceList.next())
437                 r->dbLoadBookings(r->getKotrusId(), 0);
438
439         return TRUE;
440 }
441
442 bool
443 Project::updateKotrus()
444 {
445         return TRUE;
446 }
447
448
449 bool
450 Project::loadFromXML( const QString& inpFile )
451 {
452    QDomDocument doc;
453    QFile file( inpFile );
454
455    doc.setContent( &file );
456    qDebug(  "Loading XML " + inpFile );
457
458    QDomElement elemProject = doc.documentElement();
459
460    if( !elemProject.isNull())
461    {
462       parseDomElem( elemProject );
463    }
464    else
465    {
466       qDebug("Empty !" );
467    }
468    return true;
469 }
470
471
472 void Project::parseDomElem( QDomElement& parentElem )
473 {
474    QDomElement elem = parentElem.firstChild().toElement();
475
476    for( ; !elem.isNull(); elem = elem.nextSibling().toElement() )
477    {
478       QString tagName = elem.tagName();
479       
480       qDebug(  "|| elemType: " + tagName );
481       
482       if( tagName == "Task" )
483       {
484          QString tId = elem.attribute("Id");
485          Task *t = new Task( this, tId, QString(), 0, QString(), 0 );
486
487          t->loadFromXML( elem, this  );
488          addTask( t );
489       }
490       else if( tagName == "Name" )
491       {
492          setName( elem.text() );
493       }
494       else if( tagName == "Version" )
495          setVersion( elem.text() );
496       else if( tagName == "Priority" )
497          setPriority( elem.text().toInt());
498       else if( tagName == "start" )
499          setStart( elem.text().toLong());
500       else if( tagName == "end" )
501          setEnd( elem.text().toLong());
502                
503       // parseDomElem( elem );
504    }
505 }