2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
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.
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.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
24 * Dirk Bartley, March 2007
30 #include "util/comboutil.h"
31 #include "jobgraphs/jobplot.h"
34 JobPlotPass::JobPlotPass()
39 JobPlotPass& JobPlotPass::operator=(const JobPlotPass &cp)
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;
57 * Constructor for the controls class which inherits QScrollArea and a ui header
59 JobPlotControls::JobPlotControls()
65 * Constructor, this class does not inherit anything but pages.
67 JobPlot::JobPlot(QTreeWidgetItem *parentTreeWidgetItem, JobPlotPass &passVals)
71 pgInitialize(tr("JobPlot"), parentTreeWidgetItem);
72 readSplitterSettings();
73 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
74 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/applications-graphics.png")));
77 /* this invokes the pass values = operator function */
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 */
98 * This is called when the page selector has this page selected
100 void JobPlot::currentStackItem()
110 * Slot for the refresh push button, also called from constructor.
112 void JobPlot::reGraph()
123 * Setup the control widgets for the graph, this are the objects from JobPlotControls
125 void JobPlot::setupControls()
127 QStringList graphType = QStringList() << /* tr("Fitted") <<*/ tr("Sticks")
128 << tr("Lines") << tr("Steps") << tr("None");
129 controls->plotTypeCombo->addItems(graphType);
131 fillSymbolCombo(controls->fileSymbolTypeCombo);
132 fillSymbolCombo(controls->byteSymbolTypeCombo);
134 readControlSettings();
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()));
145 controls->clientComboBox->addItem(tr("Any"));
146 controls->clientComboBox->addItems(m_console->client_list);
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);
155 levelComboFill(controls->levelComboBox);
157 boolComboFill(controls->purgedComboBox);
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);
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);
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);
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);
190 * Setup the control widgets for the graph, this are the objects from JobPlotControls
192 void JobPlot::runQuery()
196 query += "SELECT DISTINCT "
197 " Job.Starttime AS JobStart,"
198 " Job.Jobfiles AS FileCount,"
199 " Job.JobBytes AS Bytes,"
200 " Job.JobId AS JobId"
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)";
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) + "'");
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");
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 + "'");
227 foreach (QString condition, conditions) {
229 query += " WHERE " + condition;
232 query += " AND " + condition;
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) {
240 limit.setNum(controls->limitSpinBox->value());
241 query += " LIMIT " + limit;
244 if (mainWin->m_sqlDebug) {
245 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
249 if (m_console->sql_cmd(query, results)) {
252 QStringList fieldlist;
255 /* Iterate through the record returned from the query */
256 foreach (resultline, results) {
257 PlotJobData *plotJobData = new PlotJobData();
258 fieldlist = resultline.split("\t");
260 QString statusCode("");
261 /* Iterate through fields in the record */
262 foreach (field, fieldlist) {
263 field = field.trimmed(); /* strip leading & trailing spaces */
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();
272 m_pjd.prepend(plotJobData);
277 if ((controls->volumeComboBox->itemText(volumeIndex) != tr("Any")) && (results.count() == 0)){
278 /* for context sensitive searches, let the user know if there were no
280 QMessageBox::warning(this, "Bat",
281 tr("The Jobs query returned no results.\n"
282 "Press OK to continue?"), QMessageBox::Ok );
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.
290 void JobPlot::setupUserInterface()
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);
314 m_splitter->addWidget(m_jobPlot);
315 m_splitter->addWidget(area);
317 m_gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
321 * Add the curves to the plot
323 void JobPlot::addCurve()
325 m_jobPlot->setTitle(tr("Files and Bytes backed up"));
326 m_jobPlot->insertLegend(new QwtLegend(), QwtPlot::RightLegend);
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());
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);
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());
349 m_fileCurve->attach(m_jobPlot);
350 m_byteCurve->attach(m_jobPlot);
353 int size = m_pjd.count();
364 tval = (double *)malloc(size * sizeof(double));
365 fval = (double *)malloc(size * sizeof(double));
366 bval = (double *)malloc(size * sizeof(double));
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]);
378 m_fileCurve->setData(tval,fval,size);
379 m_byteCurve->setData(tval,bval,size);
381 for (int year=2000; year<2010; year++) {
382 for (int month=1; month<=12; month++) {
385 QTextStream(&monthBegin) << year << "-" << month << "-01 00:00:00";
387 QTextStream(&monthBegin) << year << "-0" << month << "-01 00:00:00";
389 QDateTime mdt = QDateTime::fromString(monthBegin, mainWin->m_dtformat);
390 double monbeg = mdt.toTime_t();
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);
400 mX->setXValue(monbeg);
401 mX->attach(m_jobPlot);
405 #if !defined(__GNU_C)
413 * slot to respond to the plot type combo changing
415 void JobPlot::setPlotType(QString currentText)
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;
433 m_fileCurve->setStyle(style);
434 m_byteCurve->setStyle(style);
438 void JobPlot::fillSymbolCombo(QComboBox *q)
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);
460 * slot to respond to the symbol type combo changing
462 void JobPlot::setFileSymbolType(int index)
464 setSymbolType(index, 0);
467 void JobPlot::setByteSymbolType(int index)
469 setSymbolType(index, 1);
471 void JobPlot::setSymbolType(int index, int type)
474 sym.setPen(QColor(Qt::black));
479 style = controls->fileSymbolTypeCombo->itemData(index);
480 sym.setStyle( (QwtSymbol::Style)style.toInt() );
481 sym.setBrush(QColor(Qt::yellow));
482 m_fileCurve->setSymbol(sym);
485 style = controls->byteSymbolTypeCombo->itemData(index);
486 sym.setStyle( (QwtSymbol::Style)style.toInt() );
487 sym.setBrush(QColor(Qt::blue));
488 m_byteCurve->setSymbol(sym);
495 * slot to respond to the file check box changing state
497 void JobPlot::fileCheckChanged(int newstate)
499 if (newstate == Qt::Unchecked) {
500 m_fileCurve->detach();
501 m_jobPlot->enableAxis(QwtPlot::yLeft, false);
503 m_fileCurve->attach(m_jobPlot);
504 m_jobPlot->enableAxis(QwtPlot::yLeft);
510 * slot to respond to the byte check box changing state
512 void JobPlot::byteCheckChanged(int newstate)
514 if (newstate == Qt::Unchecked) {
515 m_byteCurve->detach();
516 m_jobPlot->enableAxis(QwtPlot::yRight, false);
518 m_byteCurve->attach(m_jobPlot);
519 m_jobPlot->enableAxis(QwtPlot::yRight);
525 * Save user settings associated with this page
527 void JobPlot::writeSettings()
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());
539 * Read settings values for Controls
541 void JobPlot::readControlSettings()
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);
558 * Read and restore user settings associated with this page
560 void JobPlot::readSplitterSettings()
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());