]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/jobgraphs/jobplot.cpp
1b14e41b2467bce261bf8ac678c7fee274397219
[bacula/bacula] / bacula / src / qt-console / jobgraphs / jobplot.cpp
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20  
21 /*
22  *  JobPlots Class
23  *
24  *   Dirk Bartley, March 2007
25  *
26  */ 
27
28 #include "bat.h"
29 #include <QtGui>
30 #include "util/comboutil.h"
31 #include "jobgraphs/jobplot.h"
32
33
34 JobPlotPass::JobPlotPass()
35 {
36    use = false;
37 }
38
39 JobPlotPass& JobPlotPass::operator=(const JobPlotPass &cp)
40 {
41    use = cp.use;
42    recordLimitCheck = cp.recordLimitCheck;
43    daysLimitCheck = cp.daysLimitCheck;
44    recordLimitSpin = cp.recordLimitSpin;
45    daysLimitSpin = cp.daysLimitSpin;
46    jobCombo = cp.jobCombo;
47    clientCombo = cp.clientCombo;
48    volumeCombo = cp.volumeCombo;
49    fileSetCombo = cp.fileSetCombo;
50    purgedCombo = cp.purgedCombo;
51    levelCombo = cp.levelCombo;
52    statusCombo = cp.statusCombo;
53    return *this;
54 }
55
56 /*
57  * Constructor for the controls class which inherits QScrollArea and a ui header
58  */
59 JobPlotControls::JobPlotControls()
60 {
61    setupUi(this);
62 }
63
64 /*
65  * Constructor, this class does not inherit anything but pages.
66  */
67 JobPlot::JobPlot(QTreeWidgetItem *parentTreeWidgetItem, JobPlotPass &passVals)
68    : Pages()
69 {
70    setupUserInterface();
71    pgInitialize(tr("JobPlot"), parentTreeWidgetItem);
72    readSplitterSettings();
73    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
74    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/applications-graphics.png")));
75    m_drawn = false;
76
77    /* this invokes the pass values = operator function */
78    m_pass = passVals;
79    dockPage();
80    /* If the values of the controls are predetermined (from joblist), then set
81     * this class as current window at the front of the stack */
82    if (m_pass.use)
83       setCurrent();
84    m_jobPlot->replot();
85 }
86
87 /*
88  * Kill, crush Destroy
89  */
90 JobPlot::~JobPlot()
91 {
92    if (m_drawn)
93       writeSettings();
94    m_pjd.clear();
95 }
96
97 /*
98  * This is called when the page selector has this page selected
99  */
100 void JobPlot::currentStackItem()
101 {
102    if (!m_drawn) {
103       setupControls();
104       reGraph();
105       m_drawn=true;
106    }
107 }
108
109 /*
110  * Slot for the refresh push button, also called from constructor.
111  */
112 void JobPlot::reGraph()
113 {
114    /* clear m_pjd */
115    m_pjd.clear();
116    runQuery();
117    m_jobPlot->clear();
118    addCurve();
119    m_jobPlot->replot();
120 }
121
122 /*
123  * Setup the control widgets for the graph, this are the objects from JobPlotControls
124  */
125 void JobPlot::setupControls()
126 {
127    QStringList graphType = QStringList() << /* tr("Fitted") <<*/ tr("Sticks")
128                                          << tr("Lines") << tr("Steps") << tr("None");
129    controls->plotTypeCombo->addItems(graphType);
130
131    fillSymbolCombo(controls->fileSymbolTypeCombo);
132    fillSymbolCombo(controls->byteSymbolTypeCombo);
133
134    readControlSettings();
135
136    controls->fileCheck->setCheckState(Qt::Checked);
137    controls->byteCheck->setCheckState(Qt::Checked);
138    connect(controls->plotTypeCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(setPlotType(QString)));
139    connect(controls->fileSymbolTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setFileSymbolType(int)));
140    connect(controls->byteSymbolTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setByteSymbolType(int)));
141    connect(controls->fileCheck, SIGNAL(stateChanged(int)), this, SLOT(fileCheckChanged(int)));
142    connect(controls->byteCheck, SIGNAL(stateChanged(int)), this, SLOT(byteCheckChanged(int)));
143    connect(controls->refreshButton, SIGNAL(pressed()), this, SLOT(reGraph()));
144
145    controls->clientComboBox->addItem(tr("Any"));
146    controls->clientComboBox->addItems(m_console->client_list);
147
148    QStringList volumeList;
149    getVolumeList(volumeList);
150    controls->volumeComboBox->addItem(tr("Any"));
151    controls->volumeComboBox->addItems(volumeList);
152    controls->jobComboBox->addItem(tr("Any"));
153    controls->jobComboBox->addItems(m_console->job_list);
154
155    levelComboFill(controls->levelComboBox);
156
157    boolComboFill(controls->purgedComboBox);
158
159    controls->fileSetComboBox->addItem(tr("Any"));
160    controls->fileSetComboBox->addItems(m_console->fileset_list);
161    QStringList statusLongList;
162    getStatusList(statusLongList);
163    controls->statusComboBox->addItem(tr("Any"));
164    controls->statusComboBox->addItems(statusLongList);
165
166    if (m_pass.use) {
167       controls->limitCheckBox->setCheckState(m_pass.recordLimitCheck);
168       controls->limitSpinBox->setValue(m_pass.recordLimitSpin);
169       controls->daysCheckBox->setCheckState(m_pass.daysLimitCheck);
170       controls->daysSpinBox->setValue(m_pass.daysLimitSpin);
171
172       comboSel(controls->jobComboBox, m_pass.jobCombo);
173       comboSel(controls->clientComboBox, m_pass.clientCombo);
174       comboSel(controls->volumeComboBox, m_pass.volumeCombo);
175       comboSel(controls->fileSetComboBox, m_pass.fileSetCombo);
176       comboSel(controls->purgedComboBox, m_pass.purgedCombo);
177       comboSel(controls->levelComboBox, m_pass.levelCombo);
178       comboSel(controls->statusComboBox, m_pass.statusCombo);
179
180    } else {
181       /* Set Defaults for check and spin for limits */
182       controls->limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
183       controls->limitSpinBox->setValue(mainWin->m_recordLimitVal);
184       controls->daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
185       controls->daysSpinBox->setValue(mainWin->m_daysLimitVal);
186    } 
187 }
188
189 /*
190  * Setup the control widgets for the graph, this are the objects from JobPlotControls
191  */
192 void JobPlot::runQuery()
193 {
194    /* Set up query */
195    QString query("");
196    query += "SELECT DISTINCT "
197             " Job.Starttime AS JobStart,"
198             " Job.Jobfiles AS FileCount,"
199             " Job.JobBytes AS Bytes,"
200             " Job.JobId AS JobId"
201             " FROM Job"
202             " JOIN Client ON (Client.ClientId=Job.ClientId)"
203             " JOIN Status ON (Job.JobStatus=Status.JobStatus)"
204             " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)";
205
206    QStringList conditions;
207    comboCond(conditions, controls->jobComboBox, "Job.Name");
208    comboCond(conditions, controls->clientComboBox, "Client.Name");
209    int volumeIndex = controls->volumeComboBox->currentIndex();
210    if ((volumeIndex != -1) && (controls->volumeComboBox->itemText(volumeIndex) != tr("Any"))) {
211       query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
212                " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
213       conditions.append("Media.VolumeName='" + controls->volumeComboBox->itemText(volumeIndex) + "'");
214    }
215    comboCond(conditions, controls->fileSetComboBox, "FileSet.FileSet");
216    boolComboCond(conditions, controls->purgedComboBox, "Job.PurgedFiles");
217    levelComboCond(conditions, controls->levelComboBox, "Job.Level");
218    comboCond(conditions, controls->statusComboBox, "Status.JobStatusLong");
219
220    /* If Limit check box For limit by days is checked  */
221    if (controls->daysCheckBox->checkState() == Qt::Checked) {
222       QDateTime stamp = QDateTime::currentDateTime().addDays(-controls->daysSpinBox->value());
223       QString since = stamp.toString(Qt::ISODate);
224       conditions.append("Job.Starttime>'" + since + "'");
225    }
226    bool first = true;
227    foreach (QString condition, conditions) {
228       if (first) {
229          query += " WHERE " + condition;
230          first = false;
231       } else {
232          query += " AND " + condition;
233       }
234    }
235    /* Descending */
236    query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
237    /* If Limit check box for limit records returned is checked  */
238    if (controls->limitCheckBox->checkState() == Qt::Checked) {
239       QString limit;
240       limit.setNum(controls->limitSpinBox->value());
241       query += " LIMIT " + limit;
242    }
243
244    if (mainWin->m_sqlDebug) {
245       Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
246    }
247    QString resultline;
248    QStringList results;
249    if (m_console->sql_cmd(query, results)) {
250
251       QString field;
252       QStringList fieldlist;
253    
254       int row = 0;
255       /* Iterate through the record returned from the query */
256       foreach (resultline, results) {
257          PlotJobData *plotJobData = new PlotJobData();
258          fieldlist = resultline.split("\t");
259          int column = 0;
260          QString statusCode("");
261          /* Iterate through fields in the record */
262          foreach (field, fieldlist) {
263             field = field.trimmed();  /* strip leading & trailing spaces */
264             if (column == 0) {
265                plotJobData->dt = QDateTime::fromString(field, mainWin->m_dtformat);
266             } else if (column == 1) {
267                plotJobData->files = field.toDouble();
268             } else if (column == 2) {
269                plotJobData->bytes = field.toDouble();
270             }
271             column++;
272             m_pjd.prepend(plotJobData);
273          }
274          row++;
275       }
276    } 
277    if ((controls->volumeComboBox->itemText(volumeIndex) != tr("Any")) && (results.count() == 0)){
278       /* for context sensitive searches, let the user know if there were no
279        *        * results */
280       QMessageBox::warning(this, "Bat",
281           tr("The Jobs query returned no results.\n"
282          "Press OK to continue?"), QMessageBox::Ok );
283    }
284 }
285
286 /*
287  * The user interface that used to be in the ui header.  I wanted to have a
288  * scroll area which is not in designer.
289  */
290 void JobPlot::setupUserInterface()
291 {
292    QSizePolicy sizePolicy(static_cast<QSizePolicy::Policy>(1), static_cast<QSizePolicy::Policy>(5));
293    sizePolicy.setHorizontalStretch(0);
294    sizePolicy.setVerticalStretch(0);
295    sizePolicy.setVerticalStretch(0);
296    sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
297    sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
298    m_gridLayout = new QGridLayout(this);
299    m_gridLayout->setSpacing(6);
300    m_gridLayout->setMargin(9);
301    m_gridLayout->setObjectName(QString::fromUtf8("m_gridLayout"));
302    m_splitter = new QSplitter(this);
303    m_splitter->setObjectName(QString::fromUtf8("m_splitter"));
304    m_splitter->setOrientation(Qt::Horizontal);
305    m_jobPlot = new QwtPlot(m_splitter);
306    m_jobPlot->setObjectName(QString::fromUtf8("m_jobPlot"));
307    m_jobPlot->setSizePolicy(sizePolicy);
308    m_jobPlot->setMinimumSize(QSize(0, 0));
309    QScrollArea *area = new QScrollArea(m_splitter);
310    area->setObjectName(QString::fromUtf8("area"));
311    controls = new JobPlotControls();
312    area->setWidget(controls);
313    
314    m_splitter->addWidget(m_jobPlot);
315    m_splitter->addWidget(area);
316
317    m_gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
318 }
319
320 /*
321  * Add the curves to the plot
322  */
323 void JobPlot::addCurve()
324 {
325    m_jobPlot->setTitle(tr("Files and Bytes backed up"));
326    m_jobPlot->insertLegend(new QwtLegend(), QwtPlot::RightLegend);
327
328    // Set axis titles
329    m_jobPlot->enableAxis(QwtPlot::yRight);
330    m_jobPlot->setAxisTitle(QwtPlot::yRight, tr("<-- Bytes Kb"));
331    m_jobPlot->setAxisTitle(m_jobPlot->xBottom, tr("date of backup -->"));
332    m_jobPlot->setAxisTitle(m_jobPlot->yLeft, tr("Number of Files -->"));
333    m_jobPlot->setAxisScaleDraw(QwtPlot::xBottom, new DateTimeScaleDraw());
334
335    // Insert new curves
336    m_fileCurve = new QwtPlotCurve( tr("Files") );
337    m_fileCurve->setPen(QPen(Qt::red));
338    m_fileCurve->setCurveType(m_fileCurve->Yfx);
339    m_fileCurve->setYAxis(QwtPlot::yLeft);
340
341    m_byteCurve = new QwtPlotCurve(tr("Bytes"));
342    m_byteCurve->setPen(QPen(Qt::blue));
343    m_byteCurve->setCurveType(m_byteCurve->Yfx);
344    m_byteCurve->setYAxis(QwtPlot::yRight);
345    setPlotType(controls->plotTypeCombo->currentText());
346    setFileSymbolType(controls->fileSymbolTypeCombo->currentIndex());
347    setByteSymbolType(controls->byteSymbolTypeCombo->currentIndex());
348
349    m_fileCurve->attach(m_jobPlot);
350    m_byteCurve->attach(m_jobPlot);
351
352    // attach data
353    int size = m_pjd.count();
354    int j = 0;
355 #if defined(__GNU_C)
356    double tval[size];
357    double fval[size];
358    double bval[size];
359 #else
360    double *tval;
361    double *fval;
362    double *bval;
363
364    tval = (double *)malloc(size * sizeof(double));
365    fval = (double *)malloc(size * sizeof(double));
366    bval = (double *)malloc(size * sizeof(double));
367 #endif
368
369    foreach (PlotJobData* plotJobData, m_pjd) {
370 //      printf("%.0f %.0f %s\n", plotJobData->bytes, plotJobData->files,
371 //              plotJobData->dt.toString(mainWin->m_dtformat).toUtf8().data());
372       fval[j] = plotJobData->files;
373       bval[j] = plotJobData->bytes / 1024;
374       tval[j] = plotJobData->dt.toTime_t();
375 //      printf("%i %.0f %.0f %.0f\n", j, tval[j], fval[j], bval[j]);
376       j++;
377    }
378    m_fileCurve->setData(tval,fval,size);
379    m_byteCurve->setData(tval,bval,size);
380
381    for (int year=2000; year<2010; year++) {
382       for (int month=1; month<=12; month++) {
383          QString monthBegin;
384          if (month > 9) {
385             QTextStream(&monthBegin) << year << "-" << month << "-01 00:00:00";
386          } else {
387             QTextStream(&monthBegin) << year << "-0" << month << "-01 00:00:00";
388          }
389          QDateTime mdt = QDateTime::fromString(monthBegin, mainWin->m_dtformat);
390          double monbeg = mdt.toTime_t();
391    
392          //  ...a vertical line at the first of each month
393          QwtPlotMarker *mX = new QwtPlotMarker();
394          mX->setLabel(mdt.toString("MMM-d"));
395          mX->setLabelAlignment(Qt::AlignRight|Qt::AlignTop);
396          mX->setLineStyle(QwtPlotMarker::VLine);
397          QPen pen(Qt::darkGray);
398          pen.setStyle(Qt::DashDotDotLine);
399          mX->setLinePen(pen);
400          mX->setXValue(monbeg);
401          mX->attach(m_jobPlot);
402       }
403    }
404
405 #if !defined(__GNU_C)
406    free(tval);
407    free(fval);
408    free(bval);
409 #endif
410 }
411
412 /*
413  * slot to respond to the plot type combo changing
414  */
415 void JobPlot::setPlotType(QString currentText)
416 {
417    QwtPlotCurve::CurveStyle style = QwtPlotCurve::NoCurve;
418    if (currentText == tr("Fitted")) {
419       style = QwtPlotCurve::Lines;
420       m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
421       m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
422    } else if (currentText == tr("Sticks")) {
423       style = QwtPlotCurve::Sticks;
424    } else if (currentText == tr("Lines")) {
425       style = QwtPlotCurve::Lines;
426       m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
427       m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
428    } else if (currentText == tr("Steps")) {
429       style = QwtPlotCurve::Steps;
430    } else if (currentText == tr("None")) {
431       style = QwtPlotCurve::NoCurve;
432    }
433    m_fileCurve->setStyle(style);
434    m_byteCurve->setStyle(style);
435    m_jobPlot->replot();
436 }
437
438 void JobPlot::fillSymbolCombo(QComboBox *q)
439 {
440   q->addItem( tr("Ellipse"), (int)QwtSymbol::Ellipse);
441   q->addItem( tr("Rect"), (int)QwtSymbol::Rect); 
442   q->addItem( tr("Diamond"), (int)QwtSymbol::Diamond);
443   q->addItem( tr("Triangle"), (int)QwtSymbol::Triangle);
444   q->addItem( tr("DTrianle"), (int)QwtSymbol::DTriangle);
445   q->addItem( tr("UTriangle"), (int)QwtSymbol::UTriangle);
446   q->addItem( tr("LTriangle"), (int)QwtSymbol::LTriangle);
447   q->addItem( tr("RTriangle"), (int)QwtSymbol::RTriangle);
448   q->addItem( tr("Cross"), (int)QwtSymbol::Cross);
449   q->addItem( tr("XCross"), (int)QwtSymbol::XCross);
450   q->addItem( tr("HLine"), (int)QwtSymbol::HLine);
451   q->addItem( tr("Vline"), (int)QwtSymbol::VLine);
452   q->addItem( tr("Star1"), (int)QwtSymbol::Star1);
453   q->addItem( tr("Star2"), (int)QwtSymbol::Star2);
454   q->addItem( tr("Hexagon"), (int)QwtSymbol::Hexagon); 
455   q->addItem( tr("None"), (int)QwtSymbol::NoSymbol);
456 }
457
458
459 /*
460  * slot to respond to the symbol type combo changing
461  */
462 void JobPlot::setFileSymbolType(int index)
463 {
464    setSymbolType(index, 0);
465 }
466
467 void JobPlot::setByteSymbolType(int index)
468 {
469    setSymbolType(index, 1);
470 }
471 void JobPlot::setSymbolType(int index, int type)
472 {
473    QwtSymbol sym;
474    sym.setPen(QColor(Qt::black));
475    sym.setSize(7);
476
477    QVariant style;
478    if (0 == type) {
479       style = controls->fileSymbolTypeCombo->itemData(index);
480       sym.setStyle( (QwtSymbol::Style)style.toInt() );
481       sym.setBrush(QColor(Qt::yellow));
482       m_fileCurve->setSymbol(sym);
483    
484    } else {
485       style = controls->byteSymbolTypeCombo->itemData(index);
486       sym.setStyle( (QwtSymbol::Style)style.toInt() );
487       sym.setBrush(QColor(Qt::blue));
488       m_byteCurve->setSymbol(sym);
489
490    }
491    m_jobPlot->replot();
492 }
493
494 /*
495  * slot to respond to the file check box changing state
496  */
497 void JobPlot::fileCheckChanged(int newstate)
498 {
499    if (newstate == Qt::Unchecked) {
500       m_fileCurve->detach();
501       m_jobPlot->enableAxis(QwtPlot::yLeft, false);
502    } else {
503       m_fileCurve->attach(m_jobPlot);
504       m_jobPlot->enableAxis(QwtPlot::yLeft);
505    }
506    m_jobPlot->replot();
507 }
508
509 /*
510  * slot to respond to the byte check box changing state
511  */
512 void JobPlot::byteCheckChanged(int newstate)
513 {
514    if (newstate == Qt::Unchecked) {
515       m_byteCurve->detach();
516       m_jobPlot->enableAxis(QwtPlot::yRight, false);
517    } else {
518       m_byteCurve->attach(m_jobPlot);
519       m_jobPlot->enableAxis(QwtPlot::yRight);
520    }
521    m_jobPlot->replot();
522 }
523
524 /*
525  * Save user settings associated with this page
526  */
527 void JobPlot::writeSettings()
528 {
529    QSettings settings(m_console->m_dir->name(), "bat");
530    settings.beginGroup("JobPlot");
531    settings.setValue("m_splitterSizes", m_splitter->saveState());
532    settings.setValue("fileSymbolTypeCombo", controls->fileSymbolTypeCombo->currentText());
533    settings.setValue("byteSymbolTypeCombo", controls->byteSymbolTypeCombo->currentText());
534    settings.setValue("plotTypeCombo", controls->plotTypeCombo->currentText());
535    settings.endGroup();
536 }
537
538 /* 
539  * Read settings values for Controls
540  */
541 void JobPlot::readControlSettings()
542 {
543    QSettings settings(m_console->m_dir->name(), "bat");
544    settings.beginGroup("JobPlot");
545    int fileSymbolTypeIndex = controls->fileSymbolTypeCombo->findText(settings.value("fileSymbolTypeCombo").toString(), Qt::MatchExactly);
546    if (fileSymbolTypeIndex == -1) fileSymbolTypeIndex = 2;
547       controls->fileSymbolTypeCombo->setCurrentIndex(fileSymbolTypeIndex);
548    int byteSymbolTypeIndex = controls->byteSymbolTypeCombo->findText(settings.value("byteSymbolTypeCombo").toString(), Qt::MatchExactly);
549    if (byteSymbolTypeIndex == -1) byteSymbolTypeIndex = 3;
550       controls->byteSymbolTypeCombo->setCurrentIndex(byteSymbolTypeIndex);
551    int plotTypeIndex = controls->plotTypeCombo->findText(settings.value("plotTypeCombo").toString(), Qt::MatchExactly);
552    if (plotTypeIndex == -1) plotTypeIndex = 2;
553       controls->plotTypeCombo->setCurrentIndex(plotTypeIndex);
554    settings.endGroup();
555 }
556
557 /*
558  * Read and restore user settings associated with this page
559  */
560 void JobPlot::readSplitterSettings()
561 {
562    QSettings settings(m_console->m_dir->name(), "bat");
563    settings.beginGroup("JobPlot");
564    if (settings.contains("m_splitterSizes")) { 
565       m_splitter->restoreState(settings.value("m_splitterSizes").toByteArray());
566    }
567    settings.endGroup();
568 }