1 /* This file is part of the KDE project
2    Copyright (C) 2009, 2010 Dag Andersen <danders@get2net.dk>
3    Copyright (C) 2016 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 
21 // clazy:excludeall=qstring-arg
22 #include "kptappointment.h"
23 #include "kptcalendar.h"
24 #include "kptdatetime.h"
25 #include "kptproject.h"
26 #include "kptresource.h"
27 #include "kptnode.h"
28 #include "kpttask.h"
29 #include "kptschedule.h"
30 
31 #include <QTest>
32 #include <QStringList>
33 #include <QString>
34 
35 
36 namespace QTest
37 {
38     template<>
toString(const KPlato::DateTime & dt)39     char *toString(const KPlato::DateTime &dt)
40     {
41         QString s;
42         switch (dt.timeSpec()) {
43             case Qt::LocalTime: s = " LocalTime"; break;
44             case Qt::UTC: s = " UTC"; break;
45             case Qt::OffsetFromUTC: s = " OffsetFromUTC"; break;
46             case Qt::TimeZone: s = " TimeZone (" + dt.timeZone().id() + ')'; break;
47         }
48         return toString(QString("%1T%2 %3").arg(dt.date().toString(Qt::ISODate), dt.time().toString("hh:mm:ss.zzz"), s));
49     }
50 
51     template<>
toString(const KPlato::Duration & d)52     char *toString(const KPlato::Duration &d)
53     {
54         return toString(d.toString());
55     }
56 }
57 
58 namespace KPlato
59 {
60 
61 class Debug
62 {
63 public:
Debug()64     Debug() {}
65 static
print(Calendar * c,const QString & str,bool full=true)66 void print(Calendar *c, const QString &str, bool full = true) {
67     Q_UNUSED(full);
68     QTimeZone tz = c->timeZone();
69     QString s = tz.isValid() ? QString::fromLatin1(tz.id()) : QStringLiteral("LocalTime");
70 
71     qDebug()<<"Debug info: Calendar"<<c->name()<<s<<str;
72     for (int wd = 1; wd <= 7; ++wd) {
73         CalendarDay *d = c->weekday(wd);
74         qDebug()<<"   "<<wd<<':'<<d->stateToString(d->state());
75         foreach (TimeInterval *t,  d->timeIntervals()) {
76             qDebug()<<"      interval:"<<t->first<<t->second<<'('<<Duration(qint64(t->second)).toString()<<')';
77         }
78     }
79     foreach (const CalendarDay *d, c->days()) {
80         qDebug()<<"   "<<d->date()<<':';
81         foreach (TimeInterval *t,  d->timeIntervals()) {
82             qDebug()<<"      interval:"<<t->first<<t->second;
83         }
84     }
85 }
86 static
printAvailable(Resource * r,const QString & lead=QString ())87 QString printAvailable(Resource *r, const QString &lead = QString()) {
88     QStringList sl;
89     sl<<lead<<"Available:"
90         <<(r->availableFrom().isValid()
91                 ? r->availableFrom().toString()
92                 : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString()))
93         <<(r->availableUntil().isValid()
94                 ? r->availableUntil().toString()
95                 : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString()))
96         <<QString::number(r->units())<<"%"
97         <<"cost: normal"<<QString::number(r->normalRate())<<" overtime"<<QString::number(r->overtimeRate());
98     return sl.join(" ");
99 }
100 static
print(Resource * r,const QString & str,bool full=true)101 void print(Resource *r, const QString &str, bool full = true) {
102     qDebug()<<"Debug info: Resource"<<r->name()<<str;
103     qDebug()<<"Parent group:"<<(r->parentGroup()
104                 ? (r->parentGroup()->name() + " Type: "+ r->parentGroup()->typeToString())
105                 : QString("None"));
106     qDebug()<<"Available:"
107         <<(r->availableFrom().isValid()
108                 ? r->availableFrom().toString()
109                 : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString()))
110         <<(r->availableUntil().isValid()
111                 ? r->availableUntil().toString()
112                 : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString()))
113         <<r->units()<<'%';
114     qDebug()<<"Type:"<<r->typeToString();
115     if (r->type() == Resource::Type_Team) {
116         qDebug()<<"Team members:"<<r->teamMembers().count();
117         foreach (Resource *tm, r->teamMembers()) {
118             qDebug()<<"   "<<tm->name()<<"Available:"
119                     <<(r->availableFrom().isValid()
120                             ? r->availableFrom().toString()
121                             : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString()))
122                     <<(r->availableUntil().isValid()
123                             ? r->availableUntil().toString()
124                             : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString()))
125                     <<r->units()<<'%';
126         }
127     } else {
128         Calendar *cal = r->calendar(true);
129         QString s;
130         if (cal) {
131             s = cal->name();
132         } else {
133             cal = r->calendar(false);
134             if (cal) {
135                 s = cal->name() + " (Default)";
136             } else {
137                 s = "No calendar";
138             }
139         }
140         qDebug()<<"Calendar:"<<s;
141         if (cal) {
142             print(cal, "Resource calendar");
143         }
144     }
145     if (! full) return;
146     qDebug()<<"External appointments:"<<r->numExternalAppointments();
147     foreach (Appointment *a, r->externalAppointmentList()) {
148         qDebug()<<"   appointment:"<<a->startTime().toString()<<a->endTime().toString();
149         foreach(const AppointmentInterval &i, a->intervals().map()) {
150             qDebug()<<"      "<<i.startTime().toString()<<i.endTime().toString()<<i.load();
151         }
152     }
153 }
154 static
print(Project * p,const QString & str,bool all=false)155 void print(Project *p, const QString &str, bool all = false) {
156     qDebug()<<"Debug info: Project"<<p->name()<<str;
157     qDebug()<<"project target start:"<<QTest::toString(QDateTime(p->constraintStartTime()));
158     qDebug()<<"  project target end:"<<QTest::toString(QDateTime(p->constraintEndTime()));
159     if (p->isScheduled()) {
160         qDebug()<<"  project start time:"<<QTest::toString(QDateTime(p->startTime()));
161         qDebug()<<"    project end time:"<<QTest::toString(QDateTime(p->endTime()));
162     } else {
163         qDebug()<<" Not scheduled";
164     }
165 
166     if (! all) {
167         return;
168     }
169     for (int i = 0; i < p->numChildren(); ++i) {
170         qDebug();
171         print(static_cast<Task*>(p->childNode(i)), true);
172     }
173 }
174 static
print(Project * p,Task * t,const QString & str,bool full=true)175 void print(Project *p, Task *t, const QString &str, bool full = true) {
176     Q_UNUSED(full);
177     print(p, str);
178     print(t);
179 }
180 static
print(Task * t,const QString & str,bool full=true)181 void print(Task *t, const QString &str, bool full = true) {
182     qDebug()<<"Debug info: Task"<<t->name()<<str;
183     print(t, full);
184 }
185 static
print(Task * t,bool full=true)186 void print(Task *t, bool full = true) {
187     QString pad;
188     if (t->level() > 0) {
189         pad = QString("%1").arg("", t->level()*2, ' ');
190     }
191     qDebug()<<pad<<"Task"<<t->name()<<t->typeToString()<<t->constraintToString();
192     if (t->isScheduled()) {
193         qDebug()<<pad<<"     earlyStart:"<<QTest::toString(QDateTime(t->earlyStart()));
194         qDebug()<<pad<<"      lateStart:"<<QTest::toString(QDateTime(t->lateStart()));
195         qDebug()<<pad<<"    earlyFinish:"<<QTest::toString(QDateTime(t->earlyFinish()));
196         qDebug()<<pad<<"     lateFinish:"<<QTest::toString(QDateTime(t->lateFinish()));
197         qDebug()<<pad<<"      startTime:"<<QTest::toString(QDateTime(t->startTime()));
198         qDebug()<<pad<<"        endTime:"<<QTest::toString(QDateTime(t->endTime()));
199     } else {
200         qDebug()<<pad<<"   Not scheduled";
201     }
202     qDebug()<<pad;
203     switch (t->constraint()) {
204         case Node::MustStartOn:
205         case Node::StartNotEarlier:
206             qDebug()<<pad<<"startConstraint:"<<QTest::toString(QDateTime(t->constraintStartTime()));
207             break;
208         case Node::FixedInterval:
209             qDebug()<<pad<<"startConstraint:"<<QTest::toString(QDateTime(t->constraintStartTime()));
210         case Node::MustFinishOn:
211         case Node::FinishNotLater:
212             qDebug()<<pad<<"  endConstraint:"<<QTest::toString(QDateTime(t->constraintEndTime()));
213             break;
214         default: break;
215     }
216     qDebug()<<pad<<"Estimate   :"<<t->estimate()->expectedEstimate()<<Duration::unitToString(t->estimate()->unit())
217             <<t->estimate()->typeToString()
218             <<(t->estimate()->type() == Estimate::Type_Duration
219                 ? (t->estimate()->calendar()?t->estimate()->calendar()->name():"Fixed")
220                 : QString("%1 h").arg(t->estimate()->expectedValue().toDouble(Duration::Unit_h)));
221 
222     foreach (ResourceGroupRequest *gr, t->requests().requests()) {
223         qDebug()<<pad<<"Group request:"<<gr->group()->name()<<gr->units();
224         foreach (ResourceRequest *rr, gr->resourceRequests()) {
225             qDebug()<<pad<<printAvailable(rr->resource(), "   " + rr->resource()->name());
226         }
227     }
228     if (t->isStartNode()) {
229         qDebug()<<pad<<"Start node";
230     }
231     QStringList rel;
232     foreach (Relation *r, t->dependChildNodes()) {
233         QString type;
234         switch(r->type()) {
235         case Relation::StartStart: type = "SS"; break;
236         case Relation::FinishFinish: type = "FF"; break;
237         default: type = "FS"; break;
238         }
239         rel << QString("(%1) -> %2, %3 %4").arg(r->parent()->name()).arg(r->child()->name()).arg(type).arg(r->lag() == 0?QString():r->lag().toString(Duration::Format_HourFraction));
240     }
241     if (!rel.isEmpty()) {
242         qDebug()<<pad<<"Successors:"<<rel.join(" : ");
243     }
244     if (t->isEndNode()) {
245         qDebug()<<pad<<"End node";
246     }
247     rel.clear();
248     foreach (Relation *r, t->dependParentNodes()) {
249         QString type;
250         switch(r->type()) {
251         case Relation::StartStart: type = "SS"; break;
252         case Relation::FinishFinish: type = "FF"; break;
253         default: type = "FS"; break;
254         }
255         rel << QString("%1 -> (%2), %3 %4").arg(r->parent()->name()).arg(r->child()->name()).arg(type).arg(r->lag() == 0?QString():r->lag().toString(Duration::Format_HourFraction));
256     }
257     if (!rel.isEmpty()) {
258         qDebug()<<pad<<"Predeccessors:"<<rel.join(" : ");
259     }
260     qDebug()<<pad;
261     Schedule *s = t->currentSchedule();
262     if (s) {
263         qDebug()<<pad<<"Appointments:"<<s->appointments().count();
264         foreach (Appointment *a, s->appointments()) {
265             qDebug()<<pad<<"  Resource:"<<a->resource()->resource()->name()<<"booked:"<<QTest::toString(QDateTime(a->startTime()))<<QTest::toString(QDateTime(a->endTime()))<<"effort:"<<a->effort(a->startTime(), a->endTime()).toDouble(Duration::Unit_h)<<'h';
266             if (! full) { continue; }
267             foreach(const AppointmentInterval &i, a->intervals().map()) {
268                 qDebug()<<pad<<"    "<<QTest::toString(QDateTime(i.startTime()))<<QTest::toString(QDateTime(i.endTime()))<<i.load()<<"effort:"<<i.effort(i.startTime(), i.endTime()).toDouble(Duration::Unit_h)<<'h';
269             }
270         }
271     }
272     if (t->runningAccount()) {
273         qDebug()<<pad<<"Running account :"<<t->runningAccount()->name();
274     }
275     if (t->startupAccount()) {
276         qDebug()<<pad<<"Startup account :"<<t->startupAccount()->name()<<" cost:"<<t->startupCost();
277     }
278     if (t->shutdownAccount()) {
279         qDebug()<<pad<<"Shutdown account:"<<t->shutdownAccount()->name()<<" cost:"<<t->shutdownCost();
280     }
281     if (full) {
282         for (int i = 0; i < t->numChildren(); ++i) {
283             qDebug()<<pad;
284             print(static_cast<Task*>(t->childNode(i)), full);
285         }
286     }
287 }
288 static
print(const Completion & c,const QString & name,const QString & s=QString ())289 void print(const Completion &c, const QString &name, const QString &s = QString()) {
290     qDebug()<<"Completion:"<<name<<s;
291     qDebug()<<"  Entry mode:"<<c.entryModeToString();
292     qDebug()<<"     Started:"<<c.isStarted()<<c.startTime();
293     qDebug()<<"    Finished:"<<c.isFinished()<<c.finishTime();
294     qDebug()<<"  Completion:"<<c.percentFinished()<<'%';
295 
296     if (! c.usedEffortMap().isEmpty()) {
297         qDebug()<<"     Used effort:";
298         foreach (const Resource *r, c.usedEffortMap().keys()) {  // clazy:exclude=container-anti-pattern
299             Completion::UsedEffort *ue = c.usedEffort(r);
300             foreach (const QDate &d, ue->actualEffortMap().keys()) { // clazy:exclude=container-anti-pattern
301                 qDebug()<<"         "<<r->name()<<':';
302                 qDebug()<<"             "<<d.toString(Qt::ISODate)<<':'<<c.actualEffort(d).toString()<<c.actualCost(d);
303             }
304         }
305     }
306 }
307 static
print(const EffortCostMap & m,const QString & s=QString ())308 void print(const EffortCostMap &m, const QString &s = QString()) {
309     qDebug()<<"EffortCostMap"<<s;
310     if (m.days().isEmpty()) {
311         qDebug()<<"   Empty";
312         return;
313     }
314     qDebug()<<m.startDate().toString(Qt::ISODate)<<m.endDate().toString(Qt::ISODate)
315             <<" total="
316             <<m.totalEffort().toString()
317             <<m.totalCost()
318             <<'('
319             <<m.bcwpTotalEffort()
320             <<m.bcwpTotalCost()
321             <<')';
322 
323     if (! m.days().isEmpty()) {
324         foreach (const QDate &d, m.days().keys()) { // clazy:exclude=container-anti-pattern
325             qDebug()<<"   "<<d.toString(Qt::ISODate)<<':'<<m.hoursOnDate(d)<<'h'<<m.costOnDate(d)
326                                                     <<'('<<m.bcwpEffortOnDate(d)<<'h'<<m.bcwpCostOnDate(d)<<')';
327         }
328     }
329 }
330 static
printSchedulingLog(const ScheduleManager & sm,const QString & s)331 void printSchedulingLog(const ScheduleManager &sm, const QString &s)
332 {
333     qDebug()<<"Scheduling log"<<s;
334     qDebug()<<"Scheduling:"<<sm.name()<<(sm.recalculate()?QString("recalculate from %1").arg(sm.recalculateFrom().toString()):"");
335     foreach (const QString &s, sm.expected()->logMessages()) {
336         qDebug()<<s;
337     }
338 }
339 static
print(Account * a,long id=-1,const QString & s=QString ())340 void print(Account *a, long id = -1, const QString &s = QString())
341 {
342     qDebug()<<"Debug info Account:"<<a->name()<<s;
343     qDebug()<<"Account:"<<a->name()<<(a->isDefaultAccount() ? "Default" : "");
344     EffortCostMap ec = a->plannedCost(id);
345     qDebug()<<"Planned cost:"<<ec.totalCost()<<"effort:"<<ec.totalEffort().toString();
346     if (! a->isElement()) {
347         foreach (Account *c, a->accountList()) {
348             print(c);
349         }
350         return;
351     }
352     qDebug()<<"Cost places:";
353     foreach (Account::CostPlace *cp, a->costPlaces()) {
354         qDebug()<<"     Node:"<<(cp->node() ? cp->node()->name() : "");
355         qDebug()<<"  running:"<<cp->running();
356         qDebug()<<"  startup:"<<cp->startup();
357         qDebug()<<" shutdown:"<<cp->shutdown();
358     }
359 }
360 
361 static
print(const AppointmentInterval & i,const QString & indent=QString ())362 void print(const AppointmentInterval &i, const QString &indent = QString())
363 {
364     QString s = indent + "Interval:";
365     if (! i.isValid()) {
366         qDebug()<<s<<"Not valid";
367     } else {
368         qDebug()<<s<<i.startTime()<<i.endTime()<<i.load()<<'%';
369     }
370 }
371 
372 static
print(const AppointmentIntervalList & lst,const QString & s=QString ())373 void print(const AppointmentIntervalList &lst, const QString &s = QString())
374 {
375     qDebug()<<"Interval list:"<<lst.map().count()<<s;
376     foreach (const AppointmentInterval &i, lst.map()) {
377         print(i, "  ");
378     }
379 }
380 
381 };
382 
383 }
384