2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-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.
30 * Version $Id: jobplot.cpp 4230 2007-02-21 20:07:37Z kerns $
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/graph1.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 */
107 * This is called when the page selector has this page selected
109 void JobPlot::currentStackItem()
120 * Slot for the refrehs 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 " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
224 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
225 " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
226 " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
227 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
228 QStringList conditions;
229 int jobIndex = controls->jobComboBox->currentIndex();
230 if ((jobIndex != -1) && (controls->jobComboBox->itemText(jobIndex) != "Any"))
231 conditions.append("Job.Name='" + controls->jobComboBox->itemText(jobIndex) + "'");
232 int clientIndex = controls->clientComboBox->currentIndex();
233 if ((clientIndex != -1) && (controls->clientComboBox->itemText(clientIndex) != "Any"))
234 conditions.append("Client.Name='" + controls->clientComboBox->itemText(clientIndex) + "'");
235 int volumeIndex = controls->volumeComboBox->currentIndex();
236 if ((volumeIndex != -1) && (controls->volumeComboBox->itemText(volumeIndex) != "Any"))
237 conditions.append("Media.VolumeName='" + controls->volumeComboBox->itemText(volumeIndex) + "'");
238 int fileSetIndex = controls->fileSetComboBox->currentIndex();
239 if ((fileSetIndex != -1) && (controls->fileSetComboBox->itemText(fileSetIndex) != "Any"))
240 conditions.append("FileSet.FileSet='" + controls->fileSetComboBox->itemText(fileSetIndex) + "'");
241 int purgedIndex = controls->purgedComboBox->currentIndex();
242 if ((purgedIndex != -1) && (controls->purgedComboBox->itemText(purgedIndex) != "Any"))
243 conditions.append("Job.PurgedFiles='" + controls->purgedComboBox->itemText(purgedIndex) + "'");
244 int levelIndex = controls->levelComboBox->currentIndex();
245 if ((levelIndex != -1) && (controls->levelComboBox->itemText(levelIndex) != "Any"))
246 conditions.append("Job.Level='" + controls->levelComboBox->itemText(levelIndex) + "'");
247 int statusIndex = controls->statusComboBox->currentIndex();
248 if ((statusIndex != -1) && (controls->statusComboBox->itemText(statusIndex) != "Any"))
249 conditions.append("Status.JobStatusLong='" + controls->statusComboBox->itemText(statusIndex) + "'");
250 /* If Limit check box For limit by days is checked */
251 if (controls->daysCheckBox->checkState() == Qt::Checked) {
252 QDateTime stamp = QDateTime::currentDateTime().addDays(-controls->daysSpinBox->value());
253 QString since = stamp.toString(Qt::ISODate);
254 conditions.append("Job.Starttime>'" + since + "'");
257 foreach (QString condition, conditions) {
259 query += " WHERE " + condition;
262 query += " AND " + condition;
266 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
267 /* If Limit check box for limit records returned is checked */
268 if (controls->limitCheckBox->checkState() == Qt::Checked) {
270 limit.setNum(controls->limitSpinBox->value());
271 query += " LIMIT " + limit;
274 if (mainWin->m_sqlDebug) {
275 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
279 if (m_console->sql_cmd(query, results)) {
282 QStringList fieldlist;
285 /* Iterate through the record returned from the query */
286 foreach (resultline, results) {
287 PlotJobData *plotJobData = new PlotJobData();
288 fieldlist = resultline.split("\t");
290 QString statusCode("");
291 /* Iterate through fields in the record */
292 foreach (field, fieldlist) {
293 field = field.trimmed(); /* strip leading & trailing spaces */
295 plotJobData->dt = QDateTime::fromString(field, mainWin->m_dtformat);
296 } else if (column == 1) {
297 plotJobData->files = field.toDouble();
298 } else if (column == 2) {
299 plotJobData->bytes = field.toDouble();
302 m_pjd.prepend(plotJobData);
307 if ((controls->volumeComboBox->itemText(volumeIndex) != "Any") && (results.count() == 0)){
308 /* for context sensitive searches, let the user know if there were no
310 QMessageBox::warning(this, tr("Bat"),
311 tr("The Jobs query returned no results.\n"
312 "Press OK to continue?"), QMessageBox::Ok );
317 * The user interface that used to be in the ui header. I wanted to have a
318 * scroll area which is not in designer.
320 void JobPlot::setupUserInterface()
322 QSizePolicy sizePolicy(static_cast<QSizePolicy::Policy>(1), static_cast<QSizePolicy::Policy>(5));
323 sizePolicy.setHorizontalStretch(0);
324 sizePolicy.setVerticalStretch(0);
325 sizePolicy.setVerticalStretch(0);
326 sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
327 sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
328 m_gridLayout = new QGridLayout(this);
329 m_gridLayout->setSpacing(6);
330 m_gridLayout->setMargin(9);
331 m_gridLayout->setObjectName(QString::fromUtf8("m_gridLayout"));
332 m_splitter = new QSplitter(this);
333 m_splitter->setObjectName(QString::fromUtf8("m_splitter"));
334 m_splitter->setOrientation(Qt::Horizontal);
335 m_jobPlot = new QwtPlot(m_splitter);
336 m_jobPlot->setObjectName(QString::fromUtf8("m_jobPlot"));
337 m_jobPlot->setSizePolicy(sizePolicy);
338 m_jobPlot->setMinimumSize(QSize(0, 0));
339 QScrollArea *area = new QScrollArea(m_splitter);
340 area->setObjectName(QString::fromUtf8("area"));
341 controls = new JobPlotControls();
342 area->setWidget(controls);
344 m_splitter->addWidget(m_jobPlot);
345 m_splitter->addWidget(area);
347 m_gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
351 * Add the curves to the plot
353 void JobPlot::addCurve()
355 m_jobPlot->setTitle("Files and Bytes backed up");
356 m_jobPlot->insertLegend(new QwtLegend(), QwtPlot::RightLegend);
359 m_jobPlot->enableAxis(QwtPlot::yRight);
360 m_jobPlot->setAxisTitle(QwtPlot::yRight, "<-- Bytes Kb");
361 m_jobPlot->setAxisTitle(m_jobPlot->xBottom, "date of backup -->");
362 m_jobPlot->setAxisTitle(m_jobPlot->yLeft, "Number of Files -->");
363 m_jobPlot->setAxisScaleDraw(QwtPlot::xBottom, new DateTimeScaleDraw());
366 m_fileCurve = new QwtPlotCurve("Files");
367 m_fileCurve->setPen(QPen(Qt::red));
368 m_fileCurve->setCurveType(m_fileCurve->Yfx);
369 m_fileCurve->setYAxis(QwtPlot::yLeft);
371 m_byteCurve = new QwtPlotCurve("Bytes");
372 m_byteCurve->setPen(QPen(Qt::blue));
373 m_byteCurve->setCurveType(m_byteCurve->Yfx);
374 m_byteCurve->setYAxis(QwtPlot::yRight);
375 setPlotType(controls->plotTypeCombo->currentText());
376 setFileSymbolType(controls->fileSymbolTypeCombo->currentIndex());
377 setByteSymbolType(controls->byteSymbolTypeCombo->currentIndex());
379 m_fileCurve->attach(m_jobPlot);
380 m_byteCurve->attach(m_jobPlot);
383 int size = m_pjd.count();
388 foreach (PlotJobData* plotJobData, m_pjd) {
389 // printf("%.0f %.0f %s\n", plotJobData->bytes, plotJobData->files,
390 // plotJobData->dt.toString(mainWin->m_dtformat).toUtf8().data());
391 fval[j] = plotJobData->files;
392 bval[j] = plotJobData->bytes / 1024;
393 tval[j] = plotJobData->dt.toTime_t();
394 // printf("%i %.0f %.0f %.0f\n", j, tval[j], fval[j], bval[j]);
397 m_fileCurve->setData(tval,fval,size);
398 m_byteCurve->setData(tval,bval,size);
400 for (int year=2000; year<2010; year++) {
401 for (int month=1; month<=12; month++) {
404 QTextStream(&monthBegin) << year << "-" << month << "-01 00:00:00";
406 QTextStream(&monthBegin) << year << "-0" << month << "-01 00:00:00";
408 QDateTime mdt = QDateTime::fromString(monthBegin, mainWin->m_dtformat);
409 double monbeg = mdt.toTime_t();
411 // ...a vertical line at the first of each month
412 QwtPlotMarker *mX = new QwtPlotMarker();
413 mX->setLabel(mdt.toString("MMM-d"));
414 mX->setLabelAlignment(Qt::AlignRight|Qt::AlignTop);
415 mX->setLineStyle(QwtPlotMarker::VLine);
416 QPen pen(Qt::darkGray);
417 pen.setStyle(Qt::DashDotDotLine);
419 mX->setXValue(monbeg);
420 mX->attach(m_jobPlot);
426 * slot to respond to the plot type combo changing
428 void JobPlot::setPlotType(QString currentText)
430 QwtPlotCurve::CurveStyle style;
431 if (currentText == "Fitted") {
432 style = QwtPlotCurve::Lines;
433 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
434 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
435 } else if (currentText == "Sticks") {
436 style = QwtPlotCurve::Sticks;
437 } else if (currentText == "Lines") {
438 style = QwtPlotCurve::Lines;
439 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
440 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
441 } else if (currentText == "Steps") {
442 style = QwtPlotCurve::Steps;
443 } else if (currentText == "None") {
444 style = QwtPlotCurve::NoCurve;
446 m_fileCurve->setStyle(style);
447 m_byteCurve->setStyle(style);
452 * slot to respond to the symbol type combo changing
454 void JobPlot::setFileSymbolType(int index)
456 setSymbolType(index, 0);
459 void JobPlot::setByteSymbolType(int index)
461 setSymbolType(index, 1);
463 void JobPlot::setSymbolType(int index, int type)
466 sym.setPen(QColor(Qt::black));
469 sym.setStyle(QwtSymbol::Ellipse);
470 } else if (index == 1) {
471 sym.setStyle(QwtSymbol::Rect);
472 } else if (index == 2) {
473 sym.setStyle(QwtSymbol::Diamond);
474 } else if (index == 3) {
475 sym.setStyle(QwtSymbol::Triangle);
476 } else if (index == 4) {
477 sym.setStyle(QwtSymbol::DTriangle);
478 } else if (index == 5) {
479 sym.setStyle(QwtSymbol::UTriangle);
480 } else if (index == 6) {
481 sym.setStyle(QwtSymbol::LTriangle);
482 } else if (index == 7) {
483 sym.setStyle(QwtSymbol::RTriangle);
484 } else if (index == 8) {
485 sym.setStyle(QwtSymbol::Cross);
486 } else if (index == 9) {
487 sym.setStyle(QwtSymbol::XCross);
488 } else if (index == 10) {
489 sym.setStyle(QwtSymbol::HLine);
490 } else if (index == 11) {
491 sym.setStyle(QwtSymbol::VLine);
492 } else if (index == 12) {
493 sym.setStyle(QwtSymbol::Star1);
494 } else if (index == 13) {
495 sym.setStyle(QwtSymbol::Star2);
496 } else if (index == 14) {
497 sym.setStyle(QwtSymbol::Hexagon);
500 sym.setBrush(QColor(Qt::yellow));
501 m_fileCurve->setSymbol(sym);
504 sym.setBrush(QColor(Qt::blue));
505 m_byteCurve->setSymbol(sym);
511 * slot to respond to the file check box changing state
513 void JobPlot::fileCheckChanged(int newstate)
515 if (newstate == Qt::Unchecked) {
516 m_fileCurve->detach();
517 m_jobPlot->enableAxis(QwtPlot::yLeft, false);
519 m_fileCurve->attach(m_jobPlot);
520 m_jobPlot->enableAxis(QwtPlot::yLeft);
526 * slot to respond to the byte check box changing state
528 void JobPlot::byteCheckChanged(int newstate)
530 if (newstate == Qt::Unchecked) {
531 m_byteCurve->detach();
532 m_jobPlot->enableAxis(QwtPlot::yRight, false);
534 m_byteCurve->attach(m_jobPlot);
535 m_jobPlot->enableAxis(QwtPlot::yRight);
541 * Save user settings associated with this page
543 void JobPlot::writeSettings()
545 QSettings settings(m_console->m_dir->name(), "bat");
546 settings.beginGroup("JobPlot");
547 settings.setValue("m_splitterSizes", m_splitter->saveState());
548 settings.setValue("fileSymbolTypeCombo", controls->fileSymbolTypeCombo->currentText());
549 settings.setValue("byteSymbolTypeCombo", controls->byteSymbolTypeCombo->currentText());
550 settings.setValue("plotTypeCombo", controls->plotTypeCombo->currentText());
555 * Read settings values for Controls
557 void JobPlot::readControlSettings()
559 QSettings settings(m_console->m_dir->name(), "bat");
560 settings.beginGroup("JobPlot");
561 int fileSymbolTypeIndex = controls->fileSymbolTypeCombo->findText(settings.value("fileSymbolTypeCombo").toString(), Qt::MatchExactly);
562 if (fileSymbolTypeIndex == -1) fileSymbolTypeIndex = 2;
563 controls->fileSymbolTypeCombo->setCurrentIndex(fileSymbolTypeIndex);
564 int byteSymbolTypeIndex = controls->byteSymbolTypeCombo->findText(settings.value("byteSymbolTypeCombo").toString(), Qt::MatchExactly);
565 if (byteSymbolTypeIndex == -1) byteSymbolTypeIndex = 3;
566 controls->byteSymbolTypeCombo->setCurrentIndex(byteSymbolTypeIndex);
567 int plotTypeIndex = controls->plotTypeCombo->findText(settings.value("plotTypeCombo").toString(), Qt::MatchExactly);
568 if (plotTypeIndex == -1) plotTypeIndex = 2;
569 controls->plotTypeCombo->setCurrentIndex(plotTypeIndex);
574 * Read and restore user settings associated with this page
576 void JobPlot::readSplitterSettings()
578 QSettings settings(m_console->m_dir->name(), "bat");
579 settings.beginGroup("JobPlot");
580 m_splitter->restoreState(settings.value("m_splitterSizes").toByteArray());