]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/jobgraphs/jobplot.cpp
This is the application of a patch from Ricardo. Includes a great qty of translation...
[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 John Walker.
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    m_name = tr("JobPlot");
81    pgInitialize(parentTreeWidgetItem);
82    readSplitterSettings();
83    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
84    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/applications-graphics.png")));
85    m_drawn = false;
86
87    /* this invokes the pass values = operator function */
88    m_pass = passVals;
89    m_closeable = true;
90    dockPage();
91    /* If the values of the controls are predetermined (from joblist), then set
92     * this class as current window at the front of the stack */
93    if (m_pass.use)
94       setCurrent();
95    m_jobPlot->replot();
96 }
97
98 /*
99  * Kill, crush Destroy
100  */
101 JobPlot::~JobPlot()
102 {
103    if (m_drawn)
104       writeSettings();
105    m_pjd.clear();
106 }
107
108 /*
109  * This is called when the page selector has this page selected
110  */
111 void JobPlot::currentStackItem()
112 {
113    if (!m_drawn) {
114       setupControls();
115       reGraph();
116       m_drawn=true;
117    }
118 }
119
120 /*
121  * Slot for the refresh push button, also called from constructor.
122  */
123 void JobPlot::reGraph()
124 {
125    /* clear m_pjd */
126    m_pjd.clear();
127    runQuery();
128    m_jobPlot->clear();
129    addCurve();
130    m_jobPlot->replot();
131 }
132
133 /*
134  * Setup the control widgets for the graph, this are the objects from JobPlotControls
135  */
136 void JobPlot::setupControls()
137 {
138    QStringList graphType = QStringList() << /* tr("Fitted") <<*/ tr("Sticks")
139                                          << tr("Lines") << tr("Steps") << tr("None");
140    controls->plotTypeCombo->addItems(graphType);
141
142    fillSymbolCombo(controls->fileSymbolTypeCombo);
143    fillSymbolCombo(controls->byteSymbolTypeCombo);
144
145    readControlSettings();
146
147    controls->fileCheck->setCheckState(Qt::Checked);
148    controls->byteCheck->setCheckState(Qt::Checked);
149    connect(controls->plotTypeCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(setPlotType(QString)));
150    connect(controls->fileSymbolTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setFileSymbolType(int)));
151    connect(controls->byteSymbolTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setByteSymbolType(int)));
152    connect(controls->fileCheck, SIGNAL(stateChanged(int)), this, SLOT(fileCheckChanged(int)));
153    connect(controls->byteCheck, SIGNAL(stateChanged(int)), this, SLOT(byteCheckChanged(int)));
154    connect(controls->refreshButton, SIGNAL(pressed()), this, SLOT(reGraph()));
155
156    controls->clientComboBox->addItem(tr("Any"));
157    controls->clientComboBox->addItems(m_console->client_list);
158
159    QStringList volumeList;
160    m_console->getVolumeList(volumeList);
161    controls->volumeComboBox->addItem(tr("Any"));
162    controls->volumeComboBox->addItems(volumeList);
163    controls->jobComboBox->addItem(tr("Any"));
164    controls->jobComboBox->addItems(m_console->job_list);
165
166    levelComboFill(controls->levelComboBox);
167
168    boolComboFill(controls->purgedComboBox);
169
170    controls->fileSetComboBox->addItem(tr("Any"));
171    controls->fileSetComboBox->addItems(m_console->fileset_list);
172    QStringList statusLongList;
173    m_console->getStatusList(statusLongList);
174    controls->statusComboBox->addItem(tr("Any"));
175    controls->statusComboBox->addItems(statusLongList);
176
177    if (m_pass.use) {
178       controls->limitCheckBox->setCheckState(m_pass.recordLimitCheck);
179       controls->limitSpinBox->setValue(m_pass.recordLimitSpin);
180       controls->daysCheckBox->setCheckState(m_pass.daysLimitCheck);
181       controls->daysSpinBox->setValue(m_pass.daysLimitSpin);
182
183       comboSel(controls->jobComboBox, m_pass.jobCombo);
184       comboSel(controls->clientComboBox, m_pass.clientCombo);
185       comboSel(controls->volumeComboBox, m_pass.volumeCombo);
186       comboSel(controls->fileSetComboBox, m_pass.fileSetCombo);
187       comboSel(controls->purgedComboBox, m_pass.purgedCombo);
188       comboSel(controls->levelComboBox, m_pass.levelCombo);
189       comboSel(controls->statusComboBox, m_pass.statusCombo);
190
191    } else {
192       /* Set Defaults for check and spin for limits */
193       controls->limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
194       controls->limitSpinBox->setValue(mainWin->m_recordLimitVal);
195       controls->daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
196       controls->daysSpinBox->setValue(mainWin->m_daysLimitVal);
197    } 
198 }
199
200 /*
201  * Setup the control widgets for the graph, this are the objects from JobPlotControls
202  */
203 void JobPlot::runQuery()
204 {
205    /* Set up query */
206    QString query("");
207    query += "SELECT DISTINCT "
208             " Job.Starttime AS JobStart,"
209             " Job.Jobfiles AS FileCount,"
210             " Job.JobBytes AS Bytes,"
211             " Job.JobId AS JobId"
212             " FROM Job"
213             " JOIN Client ON (Client.ClientId=Job.ClientId)"
214             " JOIN Status ON (Job.JobStatus=Status.JobStatus)"
215             " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)";
216
217    QStringList conditions;
218    comboCond(conditions, controls->jobComboBox, "Job.Name");
219    comboCond(conditions, controls->clientComboBox, "Client.Name");
220    int volumeIndex = controls->volumeComboBox->currentIndex();
221    if ((volumeIndex != -1) && (controls->volumeComboBox->itemText(volumeIndex) != tr("Any"))) {
222       query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
223                " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
224       conditions.append("Media.VolumeName='" + controls->volumeComboBox->itemText(volumeIndex) + "'");
225    }
226    comboCond(conditions, controls->fileSetComboBox, "FileSet.FileSet");
227    boolComboCond(conditions, controls->purgedComboBox, "Job.PurgedFiles");
228    levelComboCond(conditions, controls->levelComboBox, "Job.Level");
229    comboCond(conditions, controls->statusComboBox, "Status.JobStatusLong");
230
231    /* If Limit check box For limit by days is checked  */
232    if (controls->daysCheckBox->checkState() == Qt::Checked) {
233       QDateTime stamp = QDateTime::currentDateTime().addDays(-controls->daysSpinBox->value());
234       QString since = stamp.toString(Qt::ISODate);
235       conditions.append("Job.Starttime>'" + since + "'");
236    }
237    bool first = true;
238    foreach (QString condition, conditions) {
239       if (first) {
240          query += " WHERE " + condition;
241          first = false;
242       } else {
243          query += " AND " + condition;
244       }
245    }
246    /* Descending */
247    query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
248    /* If Limit check box for limit records returned is checked  */
249    if (controls->limitCheckBox->checkState() == Qt::Checked) {
250       QString limit;
251       limit.setNum(controls->limitSpinBox->value());
252       query += " LIMIT " + limit;
253    }
254
255    if (mainWin->m_sqlDebug) {
256       Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
257    }
258    QString resultline;
259    QStringList results;
260    if (m_console->sql_cmd(query, results)) {
261
262       QString field;
263       QStringList fieldlist;
264    
265       int row = 0;
266       /* Iterate through the record returned from the query */
267       foreach (resultline, results) {
268          PlotJobData *plotJobData = new PlotJobData();
269          fieldlist = resultline.split("\t");
270          int column = 0;
271          QString statusCode("");
272          /* Iterate through fields in the record */
273          foreach (field, fieldlist) {
274             field = field.trimmed();  /* strip leading & trailing spaces */
275             if (column == 0) {
276                plotJobData->dt = QDateTime::fromString(field, mainWin->m_dtformat);
277             } else if (column == 1) {
278                plotJobData->files = field.toDouble();
279             } else if (column == 2) {
280                plotJobData->bytes = field.toDouble();
281             }
282             column++;
283             m_pjd.prepend(plotJobData);
284          }
285          row++;
286       }
287    } 
288    if ((controls->volumeComboBox->itemText(volumeIndex) != tr("Any")) && (results.count() == 0)){
289       /* for context sensitive searches, let the user know if there were no
290        *        * results */
291       QMessageBox::warning(this, "Bat",
292           tr("The Jobs query returned no results.\n"
293          "Press OK to continue?"), QMessageBox::Ok );
294    }
295 }
296
297 /*
298  * The user interface that used to be in the ui header.  I wanted to have a
299  * scroll area which is not in designer.
300  */
301 void JobPlot::setupUserInterface()
302 {
303    QSizePolicy sizePolicy(static_cast<QSizePolicy::Policy>(1), static_cast<QSizePolicy::Policy>(5));
304    sizePolicy.setHorizontalStretch(0);
305    sizePolicy.setVerticalStretch(0);
306    sizePolicy.setVerticalStretch(0);
307    sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
308    sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
309    m_gridLayout = new QGridLayout(this);
310    m_gridLayout->setSpacing(6);
311    m_gridLayout->setMargin(9);
312    m_gridLayout->setObjectName(QString::fromUtf8("m_gridLayout"));
313    m_splitter = new QSplitter(this);
314    m_splitter->setObjectName(QString::fromUtf8("m_splitter"));
315    m_splitter->setOrientation(Qt::Horizontal);
316    m_jobPlot = new QwtPlot(m_splitter);
317    m_jobPlot->setObjectName(QString::fromUtf8("m_jobPlot"));
318    m_jobPlot->setSizePolicy(sizePolicy);
319    m_jobPlot->setMinimumSize(QSize(0, 0));
320    QScrollArea *area = new QScrollArea(m_splitter);
321    area->setObjectName(QString::fromUtf8("area"));
322    controls = new JobPlotControls();
323    area->setWidget(controls);
324    
325    m_splitter->addWidget(m_jobPlot);
326    m_splitter->addWidget(area);
327
328    m_gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
329 }
330
331 /*
332  * Add the curves to the plot
333  */
334 void JobPlot::addCurve()
335 {
336    m_jobPlot->setTitle(tr("Files and Bytes backed up"));
337    m_jobPlot->insertLegend(new QwtLegend(), QwtPlot::RightLegend);
338
339    // Set axis titles
340    m_jobPlot->enableAxis(QwtPlot::yRight);
341    m_jobPlot->setAxisTitle(QwtPlot::yRight, tr("<-- Bytes Kb"));
342    m_jobPlot->setAxisTitle(m_jobPlot->xBottom, tr("date of backup -->"));
343    m_jobPlot->setAxisTitle(m_jobPlot->yLeft, tr("Number of Files -->"));
344    m_jobPlot->setAxisScaleDraw(QwtPlot::xBottom, new DateTimeScaleDraw());
345
346    // Insert new curves
347    m_fileCurve = new QwtPlotCurve( tr("Files") );
348    m_fileCurve->setPen(QPen(Qt::red));
349    m_fileCurve->setCurveType(m_fileCurve->Yfx);
350    m_fileCurve->setYAxis(QwtPlot::yLeft);
351
352    m_byteCurve = new QwtPlotCurve(tr("Bytes"));
353    m_byteCurve->setPen(QPen(Qt::blue));
354    m_byteCurve->setCurveType(m_byteCurve->Yfx);
355    m_byteCurve->setYAxis(QwtPlot::yRight);
356    setPlotType(controls->plotTypeCombo->currentText());
357    setFileSymbolType(controls->fileSymbolTypeCombo->currentIndex());
358    setByteSymbolType(controls->byteSymbolTypeCombo->currentIndex());
359
360    m_fileCurve->attach(m_jobPlot);
361    m_byteCurve->attach(m_jobPlot);
362
363    // attach data
364    int size = m_pjd.count();
365    double tval[size];
366    double fval[size];
367    double bval[size];
368    int j = 0;
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
406 /*
407  * slot to respond to the plot type combo changing
408  */
409 void JobPlot::setPlotType(QString currentText)
410 {
411    QwtPlotCurve::CurveStyle style = QwtPlotCurve::NoCurve;
412    if (currentText == tr("Fitted")) {
413       style = QwtPlotCurve::Lines;
414       m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
415       m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
416    } else if (currentText == tr("Sticks")) {
417       style = QwtPlotCurve::Sticks;
418    } else if (currentText == tr("Lines")) {
419       style = QwtPlotCurve::Lines;
420       m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
421       m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
422    } else if (currentText == tr("Steps")) {
423       style = QwtPlotCurve::Steps;
424    } else if (currentText == tr("None")) {
425       style = QwtPlotCurve::NoCurve;
426    }
427    m_fileCurve->setStyle(style);
428    m_byteCurve->setStyle(style);
429    m_jobPlot->replot();
430 }
431
432 void JobPlot::fillSymbolCombo(QComboBox *q)
433 {
434   q->addItem( tr("Ellipse"), (int)QwtSymbol::Ellipse);
435   q->addItem( tr("Rect"), (int)QwtSymbol::Rect); 
436   q->addItem( tr("Diamond"), (int)QwtSymbol::Diamond);
437   q->addItem( tr("Triangle"), (int)QwtSymbol::Triangle);
438   q->addItem( tr("DTrianle"), (int)QwtSymbol::DTriangle);
439   q->addItem( tr("UTriangle"), (int)QwtSymbol::UTriangle);
440   q->addItem( tr("LTriangle"), (int)QwtSymbol::LTriangle);
441   q->addItem( tr("RTriangle"), (int)QwtSymbol::RTriangle);
442   q->addItem( tr("Cross"), (int)QwtSymbol::Cross);
443   q->addItem( tr("XCross"), (int)QwtSymbol::XCross);
444   q->addItem( tr("HLine"), (int)QwtSymbol::HLine);
445   q->addItem( tr("Vline"), (int)QwtSymbol::VLine);
446   q->addItem( tr("Star1"), (int)QwtSymbol::Star1);
447   q->addItem( tr("Star2"), (int)QwtSymbol::Star2);
448   q->addItem( tr("Hexagon"), (int)QwtSymbol::Hexagon); 
449   q->addItem( tr("None"), (int)QwtSymbol::NoSymbol);
450 }
451
452
453 /*
454  * slot to respond to the symbol type combo changing
455  */
456 void JobPlot::setFileSymbolType(int index)
457 {
458    setSymbolType(index, 0);
459 }
460
461 void JobPlot::setByteSymbolType(int index)
462 {
463    setSymbolType(index, 1);
464 }
465 void JobPlot::setSymbolType(int index, int type)
466 {
467    QwtSymbol sym;
468    sym.setPen(QColor(Qt::black));
469    sym.setSize(7);
470
471    QVariant style;
472    if (0 == type) {
473       style = controls->fileSymbolTypeCombo->itemData(index);
474       sym.setStyle( (QwtSymbol::Style)style.toInt() );
475       sym.setBrush(QColor(Qt::yellow));
476       m_fileCurve->setSymbol(sym);
477    
478    } else {
479       style = controls->byteSymbolTypeCombo->itemData(index);
480       sym.setStyle( (QwtSymbol::Style)style.toInt() );
481       sym.setBrush(QColor(Qt::blue));
482       m_byteCurve->setSymbol(sym);
483
484    }
485    m_jobPlot->replot();
486 }
487
488 /*
489  * slot to respond to the file check box changing state
490  */
491 void JobPlot::fileCheckChanged(int newstate)
492 {
493    if (newstate == Qt::Unchecked) {
494       m_fileCurve->detach();
495       m_jobPlot->enableAxis(QwtPlot::yLeft, false);
496    } else {
497       m_fileCurve->attach(m_jobPlot);
498       m_jobPlot->enableAxis(QwtPlot::yLeft);
499    }
500    m_jobPlot->replot();
501 }
502
503 /*
504  * slot to respond to the byte check box changing state
505  */
506 void JobPlot::byteCheckChanged(int newstate)
507 {
508    if (newstate == Qt::Unchecked) {
509       m_byteCurve->detach();
510       m_jobPlot->enableAxis(QwtPlot::yRight, false);
511    } else {
512       m_byteCurve->attach(m_jobPlot);
513       m_jobPlot->enableAxis(QwtPlot::yRight);
514    }
515    m_jobPlot->replot();
516 }
517
518 /*
519  * Save user settings associated with this page
520  */
521 void JobPlot::writeSettings()
522 {
523    QSettings settings(m_console->m_dir->name(), "bat");
524    settings.beginGroup("JobPlot");
525    settings.setValue("m_splitterSizes", m_splitter->saveState());
526    settings.setValue("fileSymbolTypeCombo", controls->fileSymbolTypeCombo->currentText());
527    settings.setValue("byteSymbolTypeCombo", controls->byteSymbolTypeCombo->currentText());
528    settings.setValue("plotTypeCombo", controls->plotTypeCombo->currentText());
529    settings.endGroup();
530 }
531
532 /* 
533  * Read settings values for Controls
534  */
535 void JobPlot::readControlSettings()
536 {
537    QSettings settings(m_console->m_dir->name(), "bat");
538    settings.beginGroup("JobPlot");
539    int fileSymbolTypeIndex = controls->fileSymbolTypeCombo->findText(settings.value("fileSymbolTypeCombo").toString(), Qt::MatchExactly);
540    if (fileSymbolTypeIndex == -1) fileSymbolTypeIndex = 2;
541       controls->fileSymbolTypeCombo->setCurrentIndex(fileSymbolTypeIndex);
542    int byteSymbolTypeIndex = controls->byteSymbolTypeCombo->findText(settings.value("byteSymbolTypeCombo").toString(), Qt::MatchExactly);
543    if (byteSymbolTypeIndex == -1) byteSymbolTypeIndex = 3;
544       controls->byteSymbolTypeCombo->setCurrentIndex(byteSymbolTypeIndex);
545    int plotTypeIndex = controls->plotTypeCombo->findText(settings.value("plotTypeCombo").toString(), Qt::MatchExactly);
546    if (plotTypeIndex == -1) plotTypeIndex = 2;
547       controls->plotTypeCombo->setCurrentIndex(plotTypeIndex);
548    settings.endGroup();
549 }
550
551 /*
552  * Read and restore user settings associated with this page
553  */
554 void JobPlot::readSplitterSettings()
555 {
556    QSettings settings(m_console->m_dir->name(), "bat");
557    settings.beginGroup("JobPlot");
558    m_splitter->restoreState(settings.value("m_splitterSizes").toByteArray());
559    settings.endGroup();
560 }