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