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 Kern Sibbald.
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 "util/comboutil.h"
41 #include "jobgraphs/jobplot.h"
44 JobPlotPass::JobPlotPass()
49 JobPlotPass& JobPlotPass::operator=(const JobPlotPass &cp)
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;
67 * Constructor for the controls class which inherits QScrollArea and a ui header
69 JobPlotControls::JobPlotControls()
75 * Constructor, this class does not inherit anything but pages.
77 JobPlot::JobPlot(QTreeWidgetItem *parentTreeWidgetItem, JobPlotPass &passVals)
80 pgInitialize(tr("JobPlot"), 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() << /* tr("Fitted") <<*/ tr("Sticks")
138 << tr("Lines") << tr("Steps") << tr("None");
139 controls->plotTypeCombo->addItems(graphType);
141 fillSymbolCombo(controls->fileSymbolTypeCombo);
142 fillSymbolCombo(controls->byteSymbolTypeCombo);
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(tr("Any"));
156 controls->clientComboBox->addItems(m_console->client_list);
158 QStringList volumeList;
159 m_console->getVolumeList(volumeList);
160 controls->volumeComboBox->addItem(tr("Any"));
161 controls->volumeComboBox->addItems(volumeList);
162 controls->jobComboBox->addItem(tr("Any"));
163 controls->jobComboBox->addItems(m_console->job_list);
165 levelComboFill(controls->levelComboBox);
167 boolComboFill(controls->purgedComboBox);
169 controls->fileSetComboBox->addItem(tr("Any"));
170 controls->fileSetComboBox->addItems(m_console->fileset_list);
171 QStringList statusLongList;
172 m_console->getStatusList(statusLongList);
173 controls->statusComboBox->addItem(tr("Any"));
174 controls->statusComboBox->addItems(statusLongList);
177 controls->limitCheckBox->setCheckState(m_pass.recordLimitCheck);
178 controls->limitSpinBox->setValue(m_pass.recordLimitSpin);
179 controls->daysCheckBox->setCheckState(m_pass.daysLimitCheck);
180 controls->daysSpinBox->setValue(m_pass.daysLimitSpin);
182 comboSel(controls->jobComboBox, m_pass.jobCombo);
183 comboSel(controls->clientComboBox, m_pass.clientCombo);
184 comboSel(controls->volumeComboBox, m_pass.volumeCombo);
185 comboSel(controls->fileSetComboBox, m_pass.fileSetCombo);
186 comboSel(controls->purgedComboBox, m_pass.purgedCombo);
187 comboSel(controls->levelComboBox, m_pass.levelCombo);
188 comboSel(controls->statusComboBox, m_pass.statusCombo);
191 /* Set Defaults for check and spin for limits */
192 controls->limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
193 controls->limitSpinBox->setValue(mainWin->m_recordLimitVal);
194 controls->daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
195 controls->daysSpinBox->setValue(mainWin->m_daysLimitVal);
200 * Setup the control widgets for the graph, this are the objects from JobPlotControls
202 void JobPlot::runQuery()
206 query += "SELECT DISTINCT "
207 " Job.Starttime AS JobStart,"
208 " Job.Jobfiles AS FileCount,"
209 " Job.JobBytes AS Bytes,"
210 " Job.JobId AS JobId"
212 " JOIN Client ON (Client.ClientId=Job.ClientId)"
213 " JOIN Status ON (Job.JobStatus=Status.JobStatus)"
214 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)";
216 QStringList conditions;
217 comboCond(conditions, controls->jobComboBox, "Job.Name");
218 comboCond(conditions, controls->clientComboBox, "Client.Name");
219 int volumeIndex = controls->volumeComboBox->currentIndex();
220 if ((volumeIndex != -1) && (controls->volumeComboBox->itemText(volumeIndex) != tr("Any"))) {
221 query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
222 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
223 conditions.append("Media.VolumeName='" + controls->volumeComboBox->itemText(volumeIndex) + "'");
225 comboCond(conditions, controls->fileSetComboBox, "FileSet.FileSet");
226 boolComboCond(conditions, controls->purgedComboBox, "Job.PurgedFiles");
227 levelComboCond(conditions, controls->levelComboBox, "Job.Level");
228 comboCond(conditions, controls->statusComboBox, "Status.JobStatusLong");
230 /* If Limit check box For limit by days is checked */
231 if (controls->daysCheckBox->checkState() == Qt::Checked) {
232 QDateTime stamp = QDateTime::currentDateTime().addDays(-controls->daysSpinBox->value());
233 QString since = stamp.toString(Qt::ISODate);
234 conditions.append("Job.Starttime>'" + since + "'");
237 foreach (QString condition, conditions) {
239 query += " WHERE " + condition;
242 query += " AND " + condition;
246 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
247 /* If Limit check box for limit records returned is checked */
248 if (controls->limitCheckBox->checkState() == Qt::Checked) {
250 limit.setNum(controls->limitSpinBox->value());
251 query += " LIMIT " + limit;
254 if (mainWin->m_sqlDebug) {
255 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
259 if (m_console->sql_cmd(query, results)) {
262 QStringList fieldlist;
265 /* Iterate through the record returned from the query */
266 foreach (resultline, results) {
267 PlotJobData *plotJobData = new PlotJobData();
268 fieldlist = resultline.split("\t");
270 QString statusCode("");
271 /* Iterate through fields in the record */
272 foreach (field, fieldlist) {
273 field = field.trimmed(); /* strip leading & trailing spaces */
275 plotJobData->dt = QDateTime::fromString(field, mainWin->m_dtformat);
276 } else if (column == 1) {
277 plotJobData->files = field.toDouble();
278 } else if (column == 2) {
279 plotJobData->bytes = field.toDouble();
282 m_pjd.prepend(plotJobData);
287 if ((controls->volumeComboBox->itemText(volumeIndex) != tr("Any")) && (results.count() == 0)){
288 /* for context sensitive searches, let the user know if there were no
290 QMessageBox::warning(this, "Bat",
291 tr("The Jobs query returned no results.\n"
292 "Press OK to continue?"), QMessageBox::Ok );
297 * The user interface that used to be in the ui header. I wanted to have a
298 * scroll area which is not in designer.
300 void JobPlot::setupUserInterface()
302 QSizePolicy sizePolicy(static_cast<QSizePolicy::Policy>(1), static_cast<QSizePolicy::Policy>(5));
303 sizePolicy.setHorizontalStretch(0);
304 sizePolicy.setVerticalStretch(0);
305 sizePolicy.setVerticalStretch(0);
306 sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
307 sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
308 m_gridLayout = new QGridLayout(this);
309 m_gridLayout->setSpacing(6);
310 m_gridLayout->setMargin(9);
311 m_gridLayout->setObjectName(QString::fromUtf8("m_gridLayout"));
312 m_splitter = new QSplitter(this);
313 m_splitter->setObjectName(QString::fromUtf8("m_splitter"));
314 m_splitter->setOrientation(Qt::Horizontal);
315 m_jobPlot = new QwtPlot(m_splitter);
316 m_jobPlot->setObjectName(QString::fromUtf8("m_jobPlot"));
317 m_jobPlot->setSizePolicy(sizePolicy);
318 m_jobPlot->setMinimumSize(QSize(0, 0));
319 QScrollArea *area = new QScrollArea(m_splitter);
320 area->setObjectName(QString::fromUtf8("area"));
321 controls = new JobPlotControls();
322 area->setWidget(controls);
324 m_splitter->addWidget(m_jobPlot);
325 m_splitter->addWidget(area);
327 m_gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
331 * Add the curves to the plot
333 void JobPlot::addCurve()
335 m_jobPlot->setTitle(tr("Files and Bytes backed up"));
336 m_jobPlot->insertLegend(new QwtLegend(), QwtPlot::RightLegend);
339 m_jobPlot->enableAxis(QwtPlot::yRight);
340 m_jobPlot->setAxisTitle(QwtPlot::yRight, tr("<-- Bytes Kb"));
341 m_jobPlot->setAxisTitle(m_jobPlot->xBottom, tr("date of backup -->"));
342 m_jobPlot->setAxisTitle(m_jobPlot->yLeft, tr("Number of Files -->"));
343 m_jobPlot->setAxisScaleDraw(QwtPlot::xBottom, new DateTimeScaleDraw());
346 m_fileCurve = new QwtPlotCurve( tr("Files") );
347 m_fileCurve->setPen(QPen(Qt::red));
348 m_fileCurve->setCurveType(m_fileCurve->Yfx);
349 m_fileCurve->setYAxis(QwtPlot::yLeft);
351 m_byteCurve = new QwtPlotCurve(tr("Bytes"));
352 m_byteCurve->setPen(QPen(Qt::blue));
353 m_byteCurve->setCurveType(m_byteCurve->Yfx);
354 m_byteCurve->setYAxis(QwtPlot::yRight);
355 setPlotType(controls->plotTypeCombo->currentText());
356 setFileSymbolType(controls->fileSymbolTypeCombo->currentIndex());
357 setByteSymbolType(controls->byteSymbolTypeCombo->currentIndex());
359 m_fileCurve->attach(m_jobPlot);
360 m_byteCurve->attach(m_jobPlot);
363 int size = m_pjd.count();
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);
406 * slot to respond to the plot type combo changing
408 void JobPlot::setPlotType(QString currentText)
410 QwtPlotCurve::CurveStyle style = QwtPlotCurve::NoCurve;
411 if (currentText == tr("Fitted")) {
412 style = QwtPlotCurve::Lines;
413 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
414 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
415 } else if (currentText == tr("Sticks")) {
416 style = QwtPlotCurve::Sticks;
417 } else if (currentText == tr("Lines")) {
418 style = QwtPlotCurve::Lines;
419 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
420 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
421 } else if (currentText == tr("Steps")) {
422 style = QwtPlotCurve::Steps;
423 } else if (currentText == tr("None")) {
424 style = QwtPlotCurve::NoCurve;
426 m_fileCurve->setStyle(style);
427 m_byteCurve->setStyle(style);
431 void JobPlot::fillSymbolCombo(QComboBox *q)
433 q->addItem( tr("Ellipse"), (int)QwtSymbol::Ellipse);
434 q->addItem( tr("Rect"), (int)QwtSymbol::Rect);
435 q->addItem( tr("Diamond"), (int)QwtSymbol::Diamond);
436 q->addItem( tr("Triangle"), (int)QwtSymbol::Triangle);
437 q->addItem( tr("DTrianle"), (int)QwtSymbol::DTriangle);
438 q->addItem( tr("UTriangle"), (int)QwtSymbol::UTriangle);
439 q->addItem( tr("LTriangle"), (int)QwtSymbol::LTriangle);
440 q->addItem( tr("RTriangle"), (int)QwtSymbol::RTriangle);
441 q->addItem( tr("Cross"), (int)QwtSymbol::Cross);
442 q->addItem( tr("XCross"), (int)QwtSymbol::XCross);
443 q->addItem( tr("HLine"), (int)QwtSymbol::HLine);
444 q->addItem( tr("Vline"), (int)QwtSymbol::VLine);
445 q->addItem( tr("Star1"), (int)QwtSymbol::Star1);
446 q->addItem( tr("Star2"), (int)QwtSymbol::Star2);
447 q->addItem( tr("Hexagon"), (int)QwtSymbol::Hexagon);
448 q->addItem( tr("None"), (int)QwtSymbol::NoSymbol);
453 * slot to respond to the symbol type combo changing
455 void JobPlot::setFileSymbolType(int index)
457 setSymbolType(index, 0);
460 void JobPlot::setByteSymbolType(int index)
462 setSymbolType(index, 1);
464 void JobPlot::setSymbolType(int index, int type)
467 sym.setPen(QColor(Qt::black));
472 style = controls->fileSymbolTypeCombo->itemData(index);
473 sym.setStyle( (QwtSymbol::Style)style.toInt() );
474 sym.setBrush(QColor(Qt::yellow));
475 m_fileCurve->setSymbol(sym);
478 style = controls->byteSymbolTypeCombo->itemData(index);
479 sym.setStyle( (QwtSymbol::Style)style.toInt() );
480 sym.setBrush(QColor(Qt::blue));
481 m_byteCurve->setSymbol(sym);
488 * slot to respond to the file check box changing state
490 void JobPlot::fileCheckChanged(int newstate)
492 if (newstate == Qt::Unchecked) {
493 m_fileCurve->detach();
494 m_jobPlot->enableAxis(QwtPlot::yLeft, false);
496 m_fileCurve->attach(m_jobPlot);
497 m_jobPlot->enableAxis(QwtPlot::yLeft);
503 * slot to respond to the byte check box changing state
505 void JobPlot::byteCheckChanged(int newstate)
507 if (newstate == Qt::Unchecked) {
508 m_byteCurve->detach();
509 m_jobPlot->enableAxis(QwtPlot::yRight, false);
511 m_byteCurve->attach(m_jobPlot);
512 m_jobPlot->enableAxis(QwtPlot::yRight);
518 * Save user settings associated with this page
520 void JobPlot::writeSettings()
522 QSettings settings(m_console->m_dir->name(), "bat");
523 settings.beginGroup("JobPlot");
524 settings.setValue("m_splitterSizes", m_splitter->saveState());
525 settings.setValue("fileSymbolTypeCombo", controls->fileSymbolTypeCombo->currentText());
526 settings.setValue("byteSymbolTypeCombo", controls->byteSymbolTypeCombo->currentText());
527 settings.setValue("plotTypeCombo", controls->plotTypeCombo->currentText());
532 * Read settings values for Controls
534 void JobPlot::readControlSettings()
536 QSettings settings(m_console->m_dir->name(), "bat");
537 settings.beginGroup("JobPlot");
538 int fileSymbolTypeIndex = controls->fileSymbolTypeCombo->findText(settings.value("fileSymbolTypeCombo").toString(), Qt::MatchExactly);
539 if (fileSymbolTypeIndex == -1) fileSymbolTypeIndex = 2;
540 controls->fileSymbolTypeCombo->setCurrentIndex(fileSymbolTypeIndex);
541 int byteSymbolTypeIndex = controls->byteSymbolTypeCombo->findText(settings.value("byteSymbolTypeCombo").toString(), Qt::MatchExactly);
542 if (byteSymbolTypeIndex == -1) byteSymbolTypeIndex = 3;
543 controls->byteSymbolTypeCombo->setCurrentIndex(byteSymbolTypeIndex);
544 int plotTypeIndex = controls->plotTypeCombo->findText(settings.value("plotTypeCombo").toString(), Qt::MatchExactly);
545 if (plotTypeIndex == -1) plotTypeIndex = 2;
546 controls->plotTypeCombo->setCurrentIndex(plotTypeIndex);
551 * Read and restore user settings associated with this page
553 void JobPlot::readSplitterSettings()
555 QSettings settings(m_console->m_dir->name(), "bat");
556 settings.beginGroup("JobPlot");
557 m_splitter->restoreState(settings.value("m_splitterSizes").toByteArray());