2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
23 * Dirk Bartley, March 2007
29 #include "util/comboutil.h"
30 #include "jobgraphs/jobplot.h"
33 JobPlotPass::JobPlotPass()
38 JobPlotPass& JobPlotPass::operator=(const JobPlotPass &cp)
41 recordLimitCheck = cp.recordLimitCheck;
42 daysLimitCheck = cp.daysLimitCheck;
43 recordLimitSpin = cp.recordLimitSpin;
44 daysLimitSpin = cp.daysLimitSpin;
45 jobCombo = cp.jobCombo;
46 clientCombo = cp.clientCombo;
47 volumeCombo = cp.volumeCombo;
48 fileSetCombo = cp.fileSetCombo;
49 purgedCombo = cp.purgedCombo;
50 levelCombo = cp.levelCombo;
51 statusCombo = cp.statusCombo;
56 * Constructor for the controls class which inherits QScrollArea and a ui header
58 JobPlotControls::JobPlotControls()
64 * Constructor, this class does not inherit anything but pages.
66 JobPlot::JobPlot(QTreeWidgetItem *parentTreeWidgetItem, JobPlotPass &passVals)
70 pgInitialize(tr("JobPlot"), parentTreeWidgetItem);
71 readSplitterSettings();
72 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
73 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/applications-graphics.png")));
76 /* this invokes the pass values = operator function */
79 /* If the values of the controls are predetermined (from joblist), then set
80 * this class as current window at the front of the stack */
97 * This is called when the page selector has this page selected
99 void JobPlot::currentStackItem()
109 * Slot for the refresh push button, also called from constructor.
111 void JobPlot::reGraph()
122 * Setup the control widgets for the graph, this are the objects from JobPlotControls
124 void JobPlot::setupControls()
126 QStringList graphType = QStringList() << /* tr("Fitted") <<*/ tr("Sticks")
127 << tr("Lines") << tr("Steps") << tr("None");
128 controls->plotTypeCombo->addItems(graphType);
130 fillSymbolCombo(controls->fileSymbolTypeCombo);
131 fillSymbolCombo(controls->byteSymbolTypeCombo);
133 readControlSettings();
135 controls->fileCheck->setCheckState(Qt::Checked);
136 controls->byteCheck->setCheckState(Qt::Checked);
137 connect(controls->plotTypeCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(setPlotType(QString)));
138 connect(controls->fileSymbolTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setFileSymbolType(int)));
139 connect(controls->byteSymbolTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setByteSymbolType(int)));
140 connect(controls->fileCheck, SIGNAL(stateChanged(int)), this, SLOT(fileCheckChanged(int)));
141 connect(controls->byteCheck, SIGNAL(stateChanged(int)), this, SLOT(byteCheckChanged(int)));
142 connect(controls->refreshButton, SIGNAL(pressed()), this, SLOT(reGraph()));
144 controls->clientComboBox->addItem(tr("Any"));
145 controls->clientComboBox->addItems(m_console->client_list);
147 QStringList volumeList;
148 getVolumeList(volumeList);
149 controls->volumeComboBox->addItem(tr("Any"));
150 controls->volumeComboBox->addItems(volumeList);
151 controls->jobComboBox->addItem(tr("Any"));
152 controls->jobComboBox->addItems(m_console->job_list);
154 levelComboFill(controls->levelComboBox);
156 boolComboFill(controls->purgedComboBox);
158 controls->fileSetComboBox->addItem(tr("Any"));
159 controls->fileSetComboBox->addItems(m_console->fileset_list);
160 QStringList statusLongList;
161 getStatusList(statusLongList);
162 controls->statusComboBox->addItem(tr("Any"));
163 controls->statusComboBox->addItems(statusLongList);
166 controls->limitCheckBox->setCheckState(m_pass.recordLimitCheck);
167 controls->limitSpinBox->setValue(m_pass.recordLimitSpin);
168 controls->daysCheckBox->setCheckState(m_pass.daysLimitCheck);
169 controls->daysSpinBox->setValue(m_pass.daysLimitSpin);
171 comboSel(controls->jobComboBox, m_pass.jobCombo);
172 comboSel(controls->clientComboBox, m_pass.clientCombo);
173 comboSel(controls->volumeComboBox, m_pass.volumeCombo);
174 comboSel(controls->fileSetComboBox, m_pass.fileSetCombo);
175 comboSel(controls->purgedComboBox, m_pass.purgedCombo);
176 comboSel(controls->levelComboBox, m_pass.levelCombo);
177 comboSel(controls->statusComboBox, m_pass.statusCombo);
180 /* Set Defaults for check and spin for limits */
181 controls->limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
182 controls->limitSpinBox->setValue(mainWin->m_recordLimitVal);
183 controls->daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
184 controls->daysSpinBox->setValue(mainWin->m_daysLimitVal);
189 * Setup the control widgets for the graph, this are the objects from JobPlotControls
191 void JobPlot::runQuery()
195 query += "SELECT DISTINCT "
196 " Job.Starttime AS JobStart,"
197 " Job.Jobfiles AS FileCount,"
198 " Job.JobBytes AS Bytes,"
199 " Job.JobId AS JobId"
201 " JOIN Client ON (Client.ClientId=Job.ClientId)"
202 " JOIN Status ON (Job.JobStatus=Status.JobStatus)"
203 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)";
205 QStringList conditions;
206 comboCond(conditions, controls->jobComboBox, "Job.Name");
207 comboCond(conditions, controls->clientComboBox, "Client.Name");
208 int volumeIndex = controls->volumeComboBox->currentIndex();
209 if ((volumeIndex != -1) && (controls->volumeComboBox->itemText(volumeIndex) != tr("Any"))) {
210 query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
211 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
212 conditions.append("Media.VolumeName='" + controls->volumeComboBox->itemText(volumeIndex) + "'");
214 comboCond(conditions, controls->fileSetComboBox, "FileSet.FileSet");
215 boolComboCond(conditions, controls->purgedComboBox, "Job.PurgedFiles");
216 levelComboCond(conditions, controls->levelComboBox, "Job.Level");
217 comboCond(conditions, controls->statusComboBox, "Status.JobStatusLong");
219 /* If Limit check box For limit by days is checked */
220 if (controls->daysCheckBox->checkState() == Qt::Checked) {
221 QDateTime stamp = QDateTime::currentDateTime().addDays(-controls->daysSpinBox->value());
222 QString since = stamp.toString(Qt::ISODate);
223 conditions.append("Job.Starttime>'" + since + "'");
226 foreach (QString condition, conditions) {
228 query += " WHERE " + condition;
231 query += " AND " + condition;
235 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
236 /* If Limit check box for limit records returned is checked */
237 if (controls->limitCheckBox->checkState() == Qt::Checked) {
239 limit.setNum(controls->limitSpinBox->value());
240 query += " LIMIT " + limit;
243 if (mainWin->m_sqlDebug) {
244 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
248 if (m_console->sql_cmd(query, results)) {
251 QStringList fieldlist;
254 /* Iterate through the record returned from the query */
255 foreach (resultline, results) {
256 PlotJobData *plotJobData = new PlotJobData();
257 fieldlist = resultline.split("\t");
259 QString statusCode("");
260 /* Iterate through fields in the record */
261 foreach (field, fieldlist) {
262 field = field.trimmed(); /* strip leading & trailing spaces */
264 plotJobData->dt = QDateTime::fromString(field, mainWin->m_dtformat);
265 } else if (column == 1) {
266 plotJobData->files = field.toDouble();
267 } else if (column == 2) {
268 plotJobData->bytes = field.toDouble();
271 m_pjd.prepend(plotJobData);
276 if ((controls->volumeComboBox->itemText(volumeIndex) != tr("Any")) && (results.count() == 0)){
277 /* for context sensitive searches, let the user know if there were no
279 QMessageBox::warning(this, "Bat",
280 tr("The Jobs query returned no results.\n"
281 "Press OK to continue?"), QMessageBox::Ok );
286 * The user interface that used to be in the ui header. I wanted to have a
287 * scroll area which is not in designer.
289 void JobPlot::setupUserInterface()
291 QSizePolicy sizePolicy(static_cast<QSizePolicy::Policy>(1), static_cast<QSizePolicy::Policy>(5));
292 sizePolicy.setHorizontalStretch(0);
293 sizePolicy.setVerticalStretch(0);
294 sizePolicy.setVerticalStretch(0);
295 sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
296 sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
297 m_gridLayout = new QGridLayout(this);
298 m_gridLayout->setSpacing(6);
299 m_gridLayout->setMargin(9);
300 m_gridLayout->setObjectName(QString::fromUtf8("m_gridLayout"));
301 m_splitter = new QSplitter(this);
302 m_splitter->setObjectName(QString::fromUtf8("m_splitter"));
303 m_splitter->setOrientation(Qt::Horizontal);
304 m_jobPlot = new QwtPlot(m_splitter);
305 m_jobPlot->setObjectName(QString::fromUtf8("m_jobPlot"));
306 m_jobPlot->setSizePolicy(sizePolicy);
307 m_jobPlot->setMinimumSize(QSize(0, 0));
308 QScrollArea *area = new QScrollArea(m_splitter);
309 area->setObjectName(QString::fromUtf8("area"));
310 controls = new JobPlotControls();
311 area->setWidget(controls);
313 m_splitter->addWidget(m_jobPlot);
314 m_splitter->addWidget(area);
316 m_gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
320 * Add the curves to the plot
322 void JobPlot::addCurve()
324 m_jobPlot->setTitle(tr("Files and Bytes backed up"));
325 m_jobPlot->insertLegend(new QwtLegend(), QwtPlot::RightLegend);
328 m_jobPlot->enableAxis(QwtPlot::yRight);
329 m_jobPlot->setAxisTitle(QwtPlot::yRight, tr("<-- Bytes Kb"));
330 m_jobPlot->setAxisTitle(m_jobPlot->xBottom, tr("date of backup -->"));
331 m_jobPlot->setAxisTitle(m_jobPlot->yLeft, tr("Number of Files -->"));
332 m_jobPlot->setAxisScaleDraw(QwtPlot::xBottom, new DateTimeScaleDraw());
335 m_fileCurve = new QwtPlotCurve( tr("Files") );
336 m_fileCurve->setPen(QPen(Qt::red));
337 m_fileCurve->setCurveType(m_fileCurve->Yfx);
338 m_fileCurve->setYAxis(QwtPlot::yLeft);
340 m_byteCurve = new QwtPlotCurve(tr("Bytes"));
341 m_byteCurve->setPen(QPen(Qt::blue));
342 m_byteCurve->setCurveType(m_byteCurve->Yfx);
343 m_byteCurve->setYAxis(QwtPlot::yRight);
344 setPlotType(controls->plotTypeCombo->currentText());
345 setFileSymbolType(controls->fileSymbolTypeCombo->currentIndex());
346 setByteSymbolType(controls->byteSymbolTypeCombo->currentIndex());
348 m_fileCurve->attach(m_jobPlot);
349 m_byteCurve->attach(m_jobPlot);
352 int size = m_pjd.count();
363 tval = (double *)malloc(size * sizeof(double));
364 fval = (double *)malloc(size * sizeof(double));
365 bval = (double *)malloc(size * sizeof(double));
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]);
377 m_fileCurve->setData(tval,fval,size);
378 m_byteCurve->setData(tval,bval,size);
380 for (int year=2000; year<2010; year++) {
381 for (int month=1; month<=12; month++) {
384 QTextStream(&monthBegin) << year << "-" << month << "-01 00:00:00";
386 QTextStream(&monthBegin) << year << "-0" << month << "-01 00:00:00";
388 QDateTime mdt = QDateTime::fromString(monthBegin, mainWin->m_dtformat);
389 double monbeg = mdt.toTime_t();
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);
399 mX->setXValue(monbeg);
400 mX->attach(m_jobPlot);
404 #if !defined(__GNU_C)
412 * slot to respond to the plot type combo changing
414 void JobPlot::setPlotType(QString currentText)
416 QwtPlotCurve::CurveStyle style = QwtPlotCurve::NoCurve;
417 if (currentText == tr("Fitted")) {
418 style = QwtPlotCurve::Lines;
419 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
420 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
421 } else if (currentText == tr("Sticks")) {
422 style = QwtPlotCurve::Sticks;
423 } else if (currentText == tr("Lines")) {
424 style = QwtPlotCurve::Lines;
425 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
426 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
427 } else if (currentText == tr("Steps")) {
428 style = QwtPlotCurve::Steps;
429 } else if (currentText == tr("None")) {
430 style = QwtPlotCurve::NoCurve;
432 m_fileCurve->setStyle(style);
433 m_byteCurve->setStyle(style);
437 void JobPlot::fillSymbolCombo(QComboBox *q)
439 q->addItem( tr("Ellipse"), (int)QwtSymbol::Ellipse);
440 q->addItem( tr("Rect"), (int)QwtSymbol::Rect);
441 q->addItem( tr("Diamond"), (int)QwtSymbol::Diamond);
442 q->addItem( tr("Triangle"), (int)QwtSymbol::Triangle);
443 q->addItem( tr("DTrianle"), (int)QwtSymbol::DTriangle);
444 q->addItem( tr("UTriangle"), (int)QwtSymbol::UTriangle);
445 q->addItem( tr("LTriangle"), (int)QwtSymbol::LTriangle);
446 q->addItem( tr("RTriangle"), (int)QwtSymbol::RTriangle);
447 q->addItem( tr("Cross"), (int)QwtSymbol::Cross);
448 q->addItem( tr("XCross"), (int)QwtSymbol::XCross);
449 q->addItem( tr("HLine"), (int)QwtSymbol::HLine);
450 q->addItem( tr("Vline"), (int)QwtSymbol::VLine);
451 q->addItem( tr("Star1"), (int)QwtSymbol::Star1);
452 q->addItem( tr("Star2"), (int)QwtSymbol::Star2);
453 q->addItem( tr("Hexagon"), (int)QwtSymbol::Hexagon);
454 q->addItem( tr("None"), (int)QwtSymbol::NoSymbol);
459 * slot to respond to the symbol type combo changing
461 void JobPlot::setFileSymbolType(int index)
463 setSymbolType(index, 0);
466 void JobPlot::setByteSymbolType(int index)
468 setSymbolType(index, 1);
470 void JobPlot::setSymbolType(int index, int type)
473 sym.setPen(QColor(Qt::black));
478 style = controls->fileSymbolTypeCombo->itemData(index);
479 sym.setStyle( (QwtSymbol::Style)style.toInt() );
480 sym.setBrush(QColor(Qt::yellow));
481 m_fileCurve->setSymbol(sym);
484 style = controls->byteSymbolTypeCombo->itemData(index);
485 sym.setStyle( (QwtSymbol::Style)style.toInt() );
486 sym.setBrush(QColor(Qt::blue));
487 m_byteCurve->setSymbol(sym);
494 * slot to respond to the file check box changing state
496 void JobPlot::fileCheckChanged(int newstate)
498 if (newstate == Qt::Unchecked) {
499 m_fileCurve->detach();
500 m_jobPlot->enableAxis(QwtPlot::yLeft, false);
502 m_fileCurve->attach(m_jobPlot);
503 m_jobPlot->enableAxis(QwtPlot::yLeft);
509 * slot to respond to the byte check box changing state
511 void JobPlot::byteCheckChanged(int newstate)
513 if (newstate == Qt::Unchecked) {
514 m_byteCurve->detach();
515 m_jobPlot->enableAxis(QwtPlot::yRight, false);
517 m_byteCurve->attach(m_jobPlot);
518 m_jobPlot->enableAxis(QwtPlot::yRight);
524 * Save user settings associated with this page
526 void JobPlot::writeSettings()
528 QSettings settings(m_console->m_dir->name(), "bat");
529 settings.beginGroup("JobPlot");
530 settings.setValue("m_splitterSizes", m_splitter->saveState());
531 settings.setValue("fileSymbolTypeCombo", controls->fileSymbolTypeCombo->currentText());
532 settings.setValue("byteSymbolTypeCombo", controls->byteSymbolTypeCombo->currentText());
533 settings.setValue("plotTypeCombo", controls->plotTypeCombo->currentText());
538 * Read settings values for Controls
540 void JobPlot::readControlSettings()
542 QSettings settings(m_console->m_dir->name(), "bat");
543 settings.beginGroup("JobPlot");
544 int fileSymbolTypeIndex = controls->fileSymbolTypeCombo->findText(settings.value("fileSymbolTypeCombo").toString(), Qt::MatchExactly);
545 if (fileSymbolTypeIndex == -1) fileSymbolTypeIndex = 2;
546 controls->fileSymbolTypeCombo->setCurrentIndex(fileSymbolTypeIndex);
547 int byteSymbolTypeIndex = controls->byteSymbolTypeCombo->findText(settings.value("byteSymbolTypeCombo").toString(), Qt::MatchExactly);
548 if (byteSymbolTypeIndex == -1) byteSymbolTypeIndex = 3;
549 controls->byteSymbolTypeCombo->setCurrentIndex(byteSymbolTypeIndex);
550 int plotTypeIndex = controls->plotTypeCombo->findText(settings.value("plotTypeCombo").toString(), Qt::MatchExactly);
551 if (plotTypeIndex == -1) plotTypeIndex = 2;
552 controls->plotTypeCombo->setCurrentIndex(plotTypeIndex);
557 * Read and restore user settings associated with this page
559 void JobPlot::readSplitterSettings()
561 QSettings settings(m_console->m_dir->name(), "bat");
562 settings.beginGroup("JobPlot");
563 if (settings.contains("m_splitterSizes")) {
564 m_splitter->restoreState(settings.value("m_splitterSizes").toByteArray());