2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
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
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.
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
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.
34 * Dirk Bartley, March 2007
40 #include "jobgraphs/jobplot.h"
43 JobPlotPass::JobPlotPass()
48 JobPlotPass& JobPlotPass::operator=(const JobPlotPass &cp)
51 recordLimitCheck = cp.recordLimitCheck;
52 daysLimitCheck = cp.daysLimitCheck;
53 recordLimitSpin = cp.recordLimitSpin;
54 daysLimitSpin = cp.daysLimitSpin;
55 jobCombo = cp.jobCombo;
56 clientCombo = cp.clientCombo;
57 volumeCombo = cp.volumeCombo;
58 fileSetCombo = cp.fileSetCombo;
59 purgedCombo = cp.purgedCombo;
60 levelCombo = cp.levelCombo;
61 statusCombo = cp.statusCombo;
66 * Constructor for the controls class which inherits QScrollArea and a ui header
68 JobPlotControls::JobPlotControls()
74 * Constructor, this class does not inherit anything but pages.
76 JobPlot::JobPlot(QTreeWidgetItem *parentTreeWidgetItem, JobPlotPass &passVals)
80 pgInitialize(parentTreeWidgetItem);
81 readSplitterSettings();
82 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
83 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/applications-graphics.png")));
86 /* this invokes the pass values = operator function */
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 */
108 * This is called when the page selector has this page selected
110 void JobPlot::currentStackItem()
120 * Slot for the refresh push button, also called from constructor.
122 void JobPlot::reGraph()
133 * Setup the control widgets for the graph, this are the objects from JobPlotControls
135 void JobPlot::setupControls()
137 QStringList graphType = QStringList() << /* "Fitted" <<*/ "Sticks" << "Lines" << "Steps" << "None";
138 controls->plotTypeCombo->addItems(graphType);
139 QStringList symbolType = QStringList() << "Ellipse" << "Rect" << "Diamond" << "Triangle"
140 << "DTrianle" << "UTriangle" << "LTriangle" << "RTriangle" << "Cross" << "XCross"
141 << "HLine" << "Vline" << "Star1" << "Star2" << "Hexagon" << "None";
142 controls->fileSymbolTypeCombo->addItems(symbolType);
143 controls->byteSymbolTypeCombo->addItems(symbolType);
144 readControlSettings();
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()));
155 controls->clientComboBox->addItem("Any");
156 controls->clientComboBox->addItems(m_console->client_list);
158 QStringList volumeList;
159 m_console->getVolumeList(volumeList);
160 controls->volumeComboBox->addItem("Any");
161 controls->volumeComboBox->addItems(volumeList);
162 controls->jobComboBox->addItem("Any");
163 controls->jobComboBox->addItems(m_console->job_list);
164 controls->levelComboBox->addItem("Any");
165 controls->levelComboBox->addItems( QStringList() << "F" << "D" << "I");
166 controls->purgedComboBox->addItem("Any");
167 controls->purgedComboBox->addItems( QStringList() << "0" << "1");
168 controls->fileSetComboBox->addItem("Any");
169 controls->fileSetComboBox->addItems(m_console->fileset_list);
170 QStringList statusLongList;
171 m_console->getStatusList(statusLongList);
172 controls->statusComboBox->addItem("Any");
173 controls->statusComboBox->addItems(statusLongList);
176 controls->limitCheckBox->setCheckState(m_pass.recordLimitCheck);
177 controls->limitSpinBox->setValue(m_pass.recordLimitSpin);
178 controls->daysCheckBox->setCheckState(m_pass.daysLimitCheck);
179 controls->daysSpinBox->setValue(m_pass.daysLimitSpin);
180 int jobIndex = controls->jobComboBox->findText(m_pass.jobCombo, Qt::MatchExactly);
182 controls->jobComboBox->setCurrentIndex(jobIndex);
183 int clientIndex = controls->clientComboBox->findText(m_pass.clientCombo, Qt::MatchExactly);
184 if (clientIndex != -1)
185 controls->clientComboBox->setCurrentIndex(clientIndex);
186 int volumeIndex = controls->volumeComboBox->findText(m_pass.volumeCombo, Qt::MatchExactly);
187 if (volumeIndex != -1)
188 controls->volumeComboBox->setCurrentIndex(volumeIndex);
189 int filesetIndex = controls->fileSetComboBox->findText(m_pass.fileSetCombo, Qt::MatchExactly);
190 if (filesetIndex != -1)
191 controls->fileSetComboBox->setCurrentIndex(filesetIndex);
192 int purgedIndex = controls->purgedComboBox->findText(m_pass.purgedCombo, Qt::MatchExactly);
193 if (purgedIndex != -1)
194 controls->purgedComboBox->setCurrentIndex(purgedIndex);
195 int levelIndex = controls->levelComboBox->findText(m_pass.levelCombo, Qt::MatchExactly);
196 if (levelIndex != -1)
197 controls->levelComboBox->setCurrentIndex(levelIndex);
198 int statusIndex = controls->statusComboBox->findText(m_pass.statusCombo, Qt::MatchExactly);
199 if (statusIndex != -1)
200 controls->statusComboBox->setCurrentIndex(statusIndex);
202 /* Set Defaults for check and spin for limits */
203 controls->limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
204 controls->limitSpinBox->setValue(mainWin->m_recordLimitVal);
205 controls->daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
206 controls->daysSpinBox->setValue(mainWin->m_daysLimitVal);
211 * Setup the control widgets for the graph, this are the objects from JobPlotControls
213 void JobPlot::runQuery()
217 query += "SELECT DISTINCT "
218 " Job.Starttime AS JobStart,"
219 " Job.Jobfiles AS FileCount,"
220 " Job.JobBytes AS Bytes,"
221 " Job.JobId AS JobId"
223 " JOIN Client ON (Client.ClientId=Job.ClientId)"
224 " JOIN Status ON (Job.JobStatus=Status.JobStatus)"
225 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)";
227 QStringList conditions;
228 int jobIndex = controls->jobComboBox->currentIndex();
229 if ((jobIndex != -1) && (controls->jobComboBox->itemText(jobIndex) != "Any"))
230 conditions.append("Job.Name='" + controls->jobComboBox->itemText(jobIndex) + "'");
231 int clientIndex = controls->clientComboBox->currentIndex();
232 if ((clientIndex != -1) && (controls->clientComboBox->itemText(clientIndex) != "Any"))
233 conditions.append("Client.Name='" + controls->clientComboBox->itemText(clientIndex) + "'");
234 int volumeIndex = controls->volumeComboBox->currentIndex();
235 if ((volumeIndex != -1) && (controls->volumeComboBox->itemText(volumeIndex) != "Any")) {
236 query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
237 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
238 conditions.append("Media.VolumeName='" + controls->volumeComboBox->itemText(volumeIndex) + "'");
240 int fileSetIndex = controls->fileSetComboBox->currentIndex();
241 if ((fileSetIndex != -1) && (controls->fileSetComboBox->itemText(fileSetIndex) != "Any"))
242 conditions.append("FileSet.FileSet='" + controls->fileSetComboBox->itemText(fileSetIndex) + "'");
243 int purgedIndex = controls->purgedComboBox->currentIndex();
244 if ((purgedIndex != -1) && (controls->purgedComboBox->itemText(purgedIndex) != "Any"))
245 conditions.append("Job.PurgedFiles='" + controls->purgedComboBox->itemText(purgedIndex) + "'");
246 int levelIndex = controls->levelComboBox->currentIndex();
247 if ((levelIndex != -1) && (controls->levelComboBox->itemText(levelIndex) != "Any"))
248 conditions.append("Job.Level='" + controls->levelComboBox->itemText(levelIndex) + "'");
249 int statusIndex = controls->statusComboBox->currentIndex();
250 if ((statusIndex != -1) && (controls->statusComboBox->itemText(statusIndex) != "Any"))
251 conditions.append("Status.JobStatusLong='" + controls->statusComboBox->itemText(statusIndex) + "'");
252 /* If Limit check box For limit by days is checked */
253 if (controls->daysCheckBox->checkState() == Qt::Checked) {
254 QDateTime stamp = QDateTime::currentDateTime().addDays(-controls->daysSpinBox->value());
255 QString since = stamp.toString(Qt::ISODate);
256 conditions.append("Job.Starttime>'" + since + "'");
259 foreach (QString condition, conditions) {
261 query += " WHERE " + condition;
264 query += " AND " + condition;
268 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
269 /* If Limit check box for limit records returned is checked */
270 if (controls->limitCheckBox->checkState() == Qt::Checked) {
272 limit.setNum(controls->limitSpinBox->value());
273 query += " LIMIT " + limit;
276 if (mainWin->m_sqlDebug) {
277 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
281 if (m_console->sql_cmd(query, results)) {
284 QStringList fieldlist;
287 /* Iterate through the record returned from the query */
288 foreach (resultline, results) {
289 PlotJobData *plotJobData = new PlotJobData();
290 fieldlist = resultline.split("\t");
292 QString statusCode("");
293 /* Iterate through fields in the record */
294 foreach (field, fieldlist) {
295 field = field.trimmed(); /* strip leading & trailing spaces */
297 plotJobData->dt = QDateTime::fromString(field, mainWin->m_dtformat);
298 } else if (column == 1) {
299 plotJobData->files = field.toDouble();
300 } else if (column == 2) {
301 plotJobData->bytes = field.toDouble();
304 m_pjd.prepend(plotJobData);
309 if ((controls->volumeComboBox->itemText(volumeIndex) != "Any") && (results.count() == 0)){
310 /* for context sensitive searches, let the user know if there were no
312 QMessageBox::warning(this, tr("Bat"),
313 tr("The Jobs query returned no results.\n"
314 "Press OK to continue?"), QMessageBox::Ok );
319 * The user interface that used to be in the ui header. I wanted to have a
320 * scroll area which is not in designer.
322 void JobPlot::setupUserInterface()
324 QSizePolicy sizePolicy(static_cast<QSizePolicy::Policy>(1), static_cast<QSizePolicy::Policy>(5));
325 sizePolicy.setHorizontalStretch(0);
326 sizePolicy.setVerticalStretch(0);
327 sizePolicy.setVerticalStretch(0);
328 sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
329 sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
330 m_gridLayout = new QGridLayout(this);
331 m_gridLayout->setSpacing(6);
332 m_gridLayout->setMargin(9);
333 m_gridLayout->setObjectName(QString::fromUtf8("m_gridLayout"));
334 m_splitter = new QSplitter(this);
335 m_splitter->setObjectName(QString::fromUtf8("m_splitter"));
336 m_splitter->setOrientation(Qt::Horizontal);
337 m_jobPlot = new QwtPlot(m_splitter);
338 m_jobPlot->setObjectName(QString::fromUtf8("m_jobPlot"));
339 m_jobPlot->setSizePolicy(sizePolicy);
340 m_jobPlot->setMinimumSize(QSize(0, 0));
341 QScrollArea *area = new QScrollArea(m_splitter);
342 area->setObjectName(QString::fromUtf8("area"));
343 controls = new JobPlotControls();
344 area->setWidget(controls);
346 m_splitter->addWidget(m_jobPlot);
347 m_splitter->addWidget(area);
349 m_gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
353 * Add the curves to the plot
355 void JobPlot::addCurve()
357 m_jobPlot->setTitle("Files and Bytes backed up");
358 m_jobPlot->insertLegend(new QwtLegend(), QwtPlot::RightLegend);
361 m_jobPlot->enableAxis(QwtPlot::yRight);
362 m_jobPlot->setAxisTitle(QwtPlot::yRight, "<-- Bytes Kb");
363 m_jobPlot->setAxisTitle(m_jobPlot->xBottom, "date of backup -->");
364 m_jobPlot->setAxisTitle(m_jobPlot->yLeft, "Number of Files -->");
365 m_jobPlot->setAxisScaleDraw(QwtPlot::xBottom, new DateTimeScaleDraw());
368 m_fileCurve = new QwtPlotCurve("Files");
369 m_fileCurve->setPen(QPen(Qt::red));
370 m_fileCurve->setCurveType(m_fileCurve->Yfx);
371 m_fileCurve->setYAxis(QwtPlot::yLeft);
373 m_byteCurve = new QwtPlotCurve("Bytes");
374 m_byteCurve->setPen(QPen(Qt::blue));
375 m_byteCurve->setCurveType(m_byteCurve->Yfx);
376 m_byteCurve->setYAxis(QwtPlot::yRight);
377 setPlotType(controls->plotTypeCombo->currentText());
378 setFileSymbolType(controls->fileSymbolTypeCombo->currentIndex());
379 setByteSymbolType(controls->byteSymbolTypeCombo->currentIndex());
381 m_fileCurve->attach(m_jobPlot);
382 m_byteCurve->attach(m_jobPlot);
385 int size = m_pjd.count();
390 foreach (PlotJobData* plotJobData, m_pjd) {
391 // printf("%.0f %.0f %s\n", plotJobData->bytes, plotJobData->files,
392 // plotJobData->dt.toString(mainWin->m_dtformat).toUtf8().data());
393 fval[j] = plotJobData->files;
394 bval[j] = plotJobData->bytes / 1024;
395 tval[j] = plotJobData->dt.toTime_t();
396 // printf("%i %.0f %.0f %.0f\n", j, tval[j], fval[j], bval[j]);
399 m_fileCurve->setData(tval,fval,size);
400 m_byteCurve->setData(tval,bval,size);
402 for (int year=2000; year<2010; year++) {
403 for (int month=1; month<=12; month++) {
406 QTextStream(&monthBegin) << year << "-" << month << "-01 00:00:00";
408 QTextStream(&monthBegin) << year << "-0" << month << "-01 00:00:00";
410 QDateTime mdt = QDateTime::fromString(monthBegin, mainWin->m_dtformat);
411 double monbeg = mdt.toTime_t();
413 // ...a vertical line at the first of each month
414 QwtPlotMarker *mX = new QwtPlotMarker();
415 mX->setLabel(mdt.toString("MMM-d"));
416 mX->setLabelAlignment(Qt::AlignRight|Qt::AlignTop);
417 mX->setLineStyle(QwtPlotMarker::VLine);
418 QPen pen(Qt::darkGray);
419 pen.setStyle(Qt::DashDotDotLine);
421 mX->setXValue(monbeg);
422 mX->attach(m_jobPlot);
428 * slot to respond to the plot type combo changing
430 void JobPlot::setPlotType(QString currentText)
432 QwtPlotCurve::CurveStyle style = QwtPlotCurve::NoCurve;
433 if (currentText == "Fitted") {
434 style = QwtPlotCurve::Lines;
435 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
436 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
437 } else if (currentText == "Sticks") {
438 style = QwtPlotCurve::Sticks;
439 } else if (currentText == "Lines") {
440 style = QwtPlotCurve::Lines;
441 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
442 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
443 } else if (currentText == "Steps") {
444 style = QwtPlotCurve::Steps;
445 } else if (currentText == "None") {
446 style = QwtPlotCurve::NoCurve;
448 m_fileCurve->setStyle(style);
449 m_byteCurve->setStyle(style);
454 * slot to respond to the symbol type combo changing
456 void JobPlot::setFileSymbolType(int index)
458 setSymbolType(index, 0);
461 void JobPlot::setByteSymbolType(int index)
463 setSymbolType(index, 1);
465 void JobPlot::setSymbolType(int index, int type)
468 sym.setPen(QColor(Qt::black));
471 sym.setStyle(QwtSymbol::Ellipse);
472 } else if (index == 1) {
473 sym.setStyle(QwtSymbol::Rect);
474 } else if (index == 2) {
475 sym.setStyle(QwtSymbol::Diamond);
476 } else if (index == 3) {
477 sym.setStyle(QwtSymbol::Triangle);
478 } else if (index == 4) {
479 sym.setStyle(QwtSymbol::DTriangle);
480 } else if (index == 5) {
481 sym.setStyle(QwtSymbol::UTriangle);
482 } else if (index == 6) {
483 sym.setStyle(QwtSymbol::LTriangle);
484 } else if (index == 7) {
485 sym.setStyle(QwtSymbol::RTriangle);
486 } else if (index == 8) {
487 sym.setStyle(QwtSymbol::Cross);
488 } else if (index == 9) {
489 sym.setStyle(QwtSymbol::XCross);
490 } else if (index == 10) {
491 sym.setStyle(QwtSymbol::HLine);
492 } else if (index == 11) {
493 sym.setStyle(QwtSymbol::VLine);
494 } else if (index == 12) {
495 sym.setStyle(QwtSymbol::Star1);
496 } else if (index == 13) {
497 sym.setStyle(QwtSymbol::Star2);
498 } else if (index == 14) {
499 sym.setStyle(QwtSymbol::Hexagon);
502 sym.setBrush(QColor(Qt::yellow));
503 m_fileCurve->setSymbol(sym);
506 sym.setBrush(QColor(Qt::blue));
507 m_byteCurve->setSymbol(sym);
513 * slot to respond to the file check box changing state
515 void JobPlot::fileCheckChanged(int newstate)
517 if (newstate == Qt::Unchecked) {
518 m_fileCurve->detach();
519 m_jobPlot->enableAxis(QwtPlot::yLeft, false);
521 m_fileCurve->attach(m_jobPlot);
522 m_jobPlot->enableAxis(QwtPlot::yLeft);
528 * slot to respond to the byte check box changing state
530 void JobPlot::byteCheckChanged(int newstate)
532 if (newstate == Qt::Unchecked) {
533 m_byteCurve->detach();
534 m_jobPlot->enableAxis(QwtPlot::yRight, false);
536 m_byteCurve->attach(m_jobPlot);
537 m_jobPlot->enableAxis(QwtPlot::yRight);
543 * Save user settings associated with this page
545 void JobPlot::writeSettings()
547 QSettings settings(m_console->m_dir->name(), "bat");
548 settings.beginGroup("JobPlot");
549 settings.setValue("m_splitterSizes", m_splitter->saveState());
550 settings.setValue("fileSymbolTypeCombo", controls->fileSymbolTypeCombo->currentText());
551 settings.setValue("byteSymbolTypeCombo", controls->byteSymbolTypeCombo->currentText());
552 settings.setValue("plotTypeCombo", controls->plotTypeCombo->currentText());
557 * Read settings values for Controls
559 void JobPlot::readControlSettings()
561 QSettings settings(m_console->m_dir->name(), "bat");
562 settings.beginGroup("JobPlot");
563 int fileSymbolTypeIndex = controls->fileSymbolTypeCombo->findText(settings.value("fileSymbolTypeCombo").toString(), Qt::MatchExactly);
564 if (fileSymbolTypeIndex == -1) fileSymbolTypeIndex = 2;
565 controls->fileSymbolTypeCombo->setCurrentIndex(fileSymbolTypeIndex);
566 int byteSymbolTypeIndex = controls->byteSymbolTypeCombo->findText(settings.value("byteSymbolTypeCombo").toString(), Qt::MatchExactly);
567 if (byteSymbolTypeIndex == -1) byteSymbolTypeIndex = 3;
568 controls->byteSymbolTypeCombo->setCurrentIndex(byteSymbolTypeIndex);
569 int plotTypeIndex = controls->plotTypeCombo->findText(settings.value("plotTypeCombo").toString(), Qt::MatchExactly);
570 if (plotTypeIndex == -1) plotTypeIndex = 2;
571 controls->plotTypeCombo->setCurrentIndex(plotTypeIndex);
576 * Read and restore user settings associated with this page
578 void JobPlot::readSplitterSettings()
580 QSettings settings(m_console->m_dir->name(), "bat");
581 settings.beginGroup("JobPlot");
582 m_splitter->restoreState(settings.value("m_splitterSizes").toByteArray());