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 three of the GNU Affero 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 Affero 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 */
89 /* If the values of the controls are predetermined (from joblist), then set
90 * 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()
119 * Slot for the refresh push button, also called from constructor.
121 void JobPlot::reGraph()
132 * Setup the control widgets for the graph, this are the objects from JobPlotControls
134 void JobPlot::setupControls()
136 QStringList graphType = QStringList() << /* tr("Fitted") <<*/ tr("Sticks")
137 << tr("Lines") << tr("Steps") << tr("None");
138 controls->plotTypeCombo->addItems(graphType);
140 fillSymbolCombo(controls->fileSymbolTypeCombo);
141 fillSymbolCombo(controls->byteSymbolTypeCombo);
143 readControlSettings();
145 controls->fileCheck->setCheckState(Qt::Checked);
146 controls->byteCheck->setCheckState(Qt::Checked);
147 connect(controls->plotTypeCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(setPlotType(QString)));
148 connect(controls->fileSymbolTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setFileSymbolType(int)));
149 connect(controls->byteSymbolTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setByteSymbolType(int)));
150 connect(controls->fileCheck, SIGNAL(stateChanged(int)), this, SLOT(fileCheckChanged(int)));
151 connect(controls->byteCheck, SIGNAL(stateChanged(int)), this, SLOT(byteCheckChanged(int)));
152 connect(controls->refreshButton, SIGNAL(pressed()), this, SLOT(reGraph()));
154 controls->clientComboBox->addItem(tr("Any"));
155 controls->clientComboBox->addItems(m_console->client_list);
157 QStringList volumeList;
158 getVolumeList(volumeList);
159 controls->volumeComboBox->addItem(tr("Any"));
160 controls->volumeComboBox->addItems(volumeList);
161 controls->jobComboBox->addItem(tr("Any"));
162 controls->jobComboBox->addItems(m_console->job_list);
164 levelComboFill(controls->levelComboBox);
166 boolComboFill(controls->purgedComboBox);
168 controls->fileSetComboBox->addItem(tr("Any"));
169 controls->fileSetComboBox->addItems(m_console->fileset_list);
170 QStringList statusLongList;
171 getStatusList(statusLongList);
172 controls->statusComboBox->addItem(tr("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);
181 comboSel(controls->jobComboBox, m_pass.jobCombo);
182 comboSel(controls->clientComboBox, m_pass.clientCombo);
183 comboSel(controls->volumeComboBox, m_pass.volumeCombo);
184 comboSel(controls->fileSetComboBox, m_pass.fileSetCombo);
185 comboSel(controls->purgedComboBox, m_pass.purgedCombo);
186 comboSel(controls->levelComboBox, m_pass.levelCombo);
187 comboSel(controls->statusComboBox, m_pass.statusCombo);
190 /* Set Defaults for check and spin for limits */
191 controls->limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
192 controls->limitSpinBox->setValue(mainWin->m_recordLimitVal);
193 controls->daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
194 controls->daysSpinBox->setValue(mainWin->m_daysLimitVal);
199 * Setup the control widgets for the graph, this are the objects from JobPlotControls
201 void JobPlot::runQuery()
205 query += "SELECT DISTINCT "
206 " Job.Starttime AS JobStart,"
207 " Job.Jobfiles AS FileCount,"
208 " Job.JobBytes AS Bytes,"
209 " Job.JobId AS JobId"
211 " JOIN Client ON (Client.ClientId=Job.ClientId)"
212 " JOIN Status ON (Job.JobStatus=Status.JobStatus)"
213 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)";
215 QStringList conditions;
216 comboCond(conditions, controls->jobComboBox, "Job.Name");
217 comboCond(conditions, controls->clientComboBox, "Client.Name");
218 int volumeIndex = controls->volumeComboBox->currentIndex();
219 if ((volumeIndex != -1) && (controls->volumeComboBox->itemText(volumeIndex) != tr("Any"))) {
220 query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
221 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
222 conditions.append("Media.VolumeName='" + controls->volumeComboBox->itemText(volumeIndex) + "'");
224 comboCond(conditions, controls->fileSetComboBox, "FileSet.FileSet");
225 boolComboCond(conditions, controls->purgedComboBox, "Job.PurgedFiles");
226 levelComboCond(conditions, controls->levelComboBox, "Job.Level");
227 comboCond(conditions, controls->statusComboBox, "Status.JobStatusLong");
229 /* If Limit check box For limit by days is checked */
230 if (controls->daysCheckBox->checkState() == Qt::Checked) {
231 QDateTime stamp = QDateTime::currentDateTime().addDays(-controls->daysSpinBox->value());
232 QString since = stamp.toString(Qt::ISODate);
233 conditions.append("Job.Starttime>'" + since + "'");
236 foreach (QString condition, conditions) {
238 query += " WHERE " + condition;
241 query += " AND " + condition;
245 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
246 /* If Limit check box for limit records returned is checked */
247 if (controls->limitCheckBox->checkState() == Qt::Checked) {
249 limit.setNum(controls->limitSpinBox->value());
250 query += " LIMIT " + limit;
253 if (mainWin->m_sqlDebug) {
254 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
258 if (m_console->sql_cmd(query, results)) {
261 QStringList fieldlist;
264 /* Iterate through the record returned from the query */
265 foreach (resultline, results) {
266 PlotJobData *plotJobData = new PlotJobData();
267 fieldlist = resultline.split("\t");
269 QString statusCode("");
270 /* Iterate through fields in the record */
271 foreach (field, fieldlist) {
272 field = field.trimmed(); /* strip leading & trailing spaces */
274 plotJobData->dt = QDateTime::fromString(field, mainWin->m_dtformat);
275 } else if (column == 1) {
276 plotJobData->files = field.toDouble();
277 } else if (column == 2) {
278 plotJobData->bytes = field.toDouble();
281 m_pjd.prepend(plotJobData);
286 if ((controls->volumeComboBox->itemText(volumeIndex) != tr("Any")) && (results.count() == 0)){
287 /* for context sensitive searches, let the user know if there were no
289 QMessageBox::warning(this, "Bat",
290 tr("The Jobs query returned no results.\n"
291 "Press OK to continue?"), QMessageBox::Ok );
296 * The user interface that used to be in the ui header. I wanted to have a
297 * scroll area which is not in designer.
299 void JobPlot::setupUserInterface()
301 QSizePolicy sizePolicy(static_cast<QSizePolicy::Policy>(1), static_cast<QSizePolicy::Policy>(5));
302 sizePolicy.setHorizontalStretch(0);
303 sizePolicy.setVerticalStretch(0);
304 sizePolicy.setVerticalStretch(0);
305 sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
306 sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
307 m_gridLayout = new QGridLayout(this);
308 m_gridLayout->setSpacing(6);
309 m_gridLayout->setMargin(9);
310 m_gridLayout->setObjectName(QString::fromUtf8("m_gridLayout"));
311 m_splitter = new QSplitter(this);
312 m_splitter->setObjectName(QString::fromUtf8("m_splitter"));
313 m_splitter->setOrientation(Qt::Horizontal);
314 m_jobPlot = new QwtPlot(m_splitter);
315 m_jobPlot->setObjectName(QString::fromUtf8("m_jobPlot"));
316 m_jobPlot->setSizePolicy(sizePolicy);
317 m_jobPlot->setMinimumSize(QSize(0, 0));
318 QScrollArea *area = new QScrollArea(m_splitter);
319 area->setObjectName(QString::fromUtf8("area"));
320 controls = new JobPlotControls();
321 area->setWidget(controls);
323 m_splitter->addWidget(m_jobPlot);
324 m_splitter->addWidget(area);
326 m_gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
330 * Add the curves to the plot
332 void JobPlot::addCurve()
334 m_jobPlot->setTitle(tr("Files and Bytes backed up"));
335 m_jobPlot->insertLegend(new QwtLegend(), QwtPlot::RightLegend);
338 m_jobPlot->enableAxis(QwtPlot::yRight);
339 m_jobPlot->setAxisTitle(QwtPlot::yRight, tr("<-- Bytes Kb"));
340 m_jobPlot->setAxisTitle(m_jobPlot->xBottom, tr("date of backup -->"));
341 m_jobPlot->setAxisTitle(m_jobPlot->yLeft, tr("Number of Files -->"));
342 m_jobPlot->setAxisScaleDraw(QwtPlot::xBottom, new DateTimeScaleDraw());
345 m_fileCurve = new QwtPlotCurve( tr("Files") );
346 m_fileCurve->setPen(QPen(Qt::red));
347 m_fileCurve->setCurveType(m_fileCurve->Yfx);
348 m_fileCurve->setYAxis(QwtPlot::yLeft);
350 m_byteCurve = new QwtPlotCurve(tr("Bytes"));
351 m_byteCurve->setPen(QPen(Qt::blue));
352 m_byteCurve->setCurveType(m_byteCurve->Yfx);
353 m_byteCurve->setYAxis(QwtPlot::yRight);
354 setPlotType(controls->plotTypeCombo->currentText());
355 setFileSymbolType(controls->fileSymbolTypeCombo->currentIndex());
356 setByteSymbolType(controls->byteSymbolTypeCombo->currentIndex());
358 m_fileCurve->attach(m_jobPlot);
359 m_byteCurve->attach(m_jobPlot);
362 int size = m_pjd.count();
373 tval = (double *)malloc(size * sizeof(double));
374 fval = (double *)malloc(size * sizeof(double));
375 bval = (double *)malloc(size * sizeof(double));
378 foreach (PlotJobData* plotJobData, m_pjd) {
379 // printf("%.0f %.0f %s\n", plotJobData->bytes, plotJobData->files,
380 // plotJobData->dt.toString(mainWin->m_dtformat).toUtf8().data());
381 fval[j] = plotJobData->files;
382 bval[j] = plotJobData->bytes / 1024;
383 tval[j] = plotJobData->dt.toTime_t();
384 // printf("%i %.0f %.0f %.0f\n", j, tval[j], fval[j], bval[j]);
387 m_fileCurve->setData(tval,fval,size);
388 m_byteCurve->setData(tval,bval,size);
390 for (int year=2000; year<2010; year++) {
391 for (int month=1; month<=12; month++) {
394 QTextStream(&monthBegin) << year << "-" << month << "-01 00:00:00";
396 QTextStream(&monthBegin) << year << "-0" << month << "-01 00:00:00";
398 QDateTime mdt = QDateTime::fromString(monthBegin, mainWin->m_dtformat);
399 double monbeg = mdt.toTime_t();
401 // ...a vertical line at the first of each month
402 QwtPlotMarker *mX = new QwtPlotMarker();
403 mX->setLabel(mdt.toString("MMM-d"));
404 mX->setLabelAlignment(Qt::AlignRight|Qt::AlignTop);
405 mX->setLineStyle(QwtPlotMarker::VLine);
406 QPen pen(Qt::darkGray);
407 pen.setStyle(Qt::DashDotDotLine);
409 mX->setXValue(monbeg);
410 mX->attach(m_jobPlot);
414 #if !defined(__GNU_C)
422 * slot to respond to the plot type combo changing
424 void JobPlot::setPlotType(QString currentText)
426 QwtPlotCurve::CurveStyle style = QwtPlotCurve::NoCurve;
427 if (currentText == tr("Fitted")) {
428 style = QwtPlotCurve::Lines;
429 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
430 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
431 } else if (currentText == tr("Sticks")) {
432 style = QwtPlotCurve::Sticks;
433 } else if (currentText == tr("Lines")) {
434 style = QwtPlotCurve::Lines;
435 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
436 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
437 } else if (currentText == tr("Steps")) {
438 style = QwtPlotCurve::Steps;
439 } else if (currentText == tr("None")) {
440 style = QwtPlotCurve::NoCurve;
442 m_fileCurve->setStyle(style);
443 m_byteCurve->setStyle(style);
447 void JobPlot::fillSymbolCombo(QComboBox *q)
449 q->addItem( tr("Ellipse"), (int)QwtSymbol::Ellipse);
450 q->addItem( tr("Rect"), (int)QwtSymbol::Rect);
451 q->addItem( tr("Diamond"), (int)QwtSymbol::Diamond);
452 q->addItem( tr("Triangle"), (int)QwtSymbol::Triangle);
453 q->addItem( tr("DTrianle"), (int)QwtSymbol::DTriangle);
454 q->addItem( tr("UTriangle"), (int)QwtSymbol::UTriangle);
455 q->addItem( tr("LTriangle"), (int)QwtSymbol::LTriangle);
456 q->addItem( tr("RTriangle"), (int)QwtSymbol::RTriangle);
457 q->addItem( tr("Cross"), (int)QwtSymbol::Cross);
458 q->addItem( tr("XCross"), (int)QwtSymbol::XCross);
459 q->addItem( tr("HLine"), (int)QwtSymbol::HLine);
460 q->addItem( tr("Vline"), (int)QwtSymbol::VLine);
461 q->addItem( tr("Star1"), (int)QwtSymbol::Star1);
462 q->addItem( tr("Star2"), (int)QwtSymbol::Star2);
463 q->addItem( tr("Hexagon"), (int)QwtSymbol::Hexagon);
464 q->addItem( tr("None"), (int)QwtSymbol::NoSymbol);
469 * slot to respond to the symbol type combo changing
471 void JobPlot::setFileSymbolType(int index)
473 setSymbolType(index, 0);
476 void JobPlot::setByteSymbolType(int index)
478 setSymbolType(index, 1);
480 void JobPlot::setSymbolType(int index, int type)
483 sym.setPen(QColor(Qt::black));
488 style = controls->fileSymbolTypeCombo->itemData(index);
489 sym.setStyle( (QwtSymbol::Style)style.toInt() );
490 sym.setBrush(QColor(Qt::yellow));
491 m_fileCurve->setSymbol(sym);
494 style = controls->byteSymbolTypeCombo->itemData(index);
495 sym.setStyle( (QwtSymbol::Style)style.toInt() );
496 sym.setBrush(QColor(Qt::blue));
497 m_byteCurve->setSymbol(sym);
504 * slot to respond to the file check box changing state
506 void JobPlot::fileCheckChanged(int newstate)
508 if (newstate == Qt::Unchecked) {
509 m_fileCurve->detach();
510 m_jobPlot->enableAxis(QwtPlot::yLeft, false);
512 m_fileCurve->attach(m_jobPlot);
513 m_jobPlot->enableAxis(QwtPlot::yLeft);
519 * slot to respond to the byte check box changing state
521 void JobPlot::byteCheckChanged(int newstate)
523 if (newstate == Qt::Unchecked) {
524 m_byteCurve->detach();
525 m_jobPlot->enableAxis(QwtPlot::yRight, false);
527 m_byteCurve->attach(m_jobPlot);
528 m_jobPlot->enableAxis(QwtPlot::yRight);
534 * Save user settings associated with this page
536 void JobPlot::writeSettings()
538 QSettings settings(m_console->m_dir->name(), "bat");
539 settings.beginGroup("JobPlot");
540 settings.setValue("m_splitterSizes", m_splitter->saveState());
541 settings.setValue("fileSymbolTypeCombo", controls->fileSymbolTypeCombo->currentText());
542 settings.setValue("byteSymbolTypeCombo", controls->byteSymbolTypeCombo->currentText());
543 settings.setValue("plotTypeCombo", controls->plotTypeCombo->currentText());
548 * Read settings values for Controls
550 void JobPlot::readControlSettings()
552 QSettings settings(m_console->m_dir->name(), "bat");
553 settings.beginGroup("JobPlot");
554 int fileSymbolTypeIndex = controls->fileSymbolTypeCombo->findText(settings.value("fileSymbolTypeCombo").toString(), Qt::MatchExactly);
555 if (fileSymbolTypeIndex == -1) fileSymbolTypeIndex = 2;
556 controls->fileSymbolTypeCombo->setCurrentIndex(fileSymbolTypeIndex);
557 int byteSymbolTypeIndex = controls->byteSymbolTypeCombo->findText(settings.value("byteSymbolTypeCombo").toString(), Qt::MatchExactly);
558 if (byteSymbolTypeIndex == -1) byteSymbolTypeIndex = 3;
559 controls->byteSymbolTypeCombo->setCurrentIndex(byteSymbolTypeIndex);
560 int plotTypeIndex = controls->plotTypeCombo->findText(settings.value("plotTypeCombo").toString(), Qt::MatchExactly);
561 if (plotTypeIndex == -1) plotTypeIndex = 2;
562 controls->plotTypeCombo->setCurrentIndex(plotTypeIndex);
567 * Read and restore user settings associated with this page
569 void JobPlot::readSplitterSettings()
571 QSettings settings(m_console->m_dir->name(), "bat");
572 settings.beginGroup("JobPlot");
573 if (settings.contains("m_splitterSizes")) {
574 m_splitter->restoreState(settings.value("m_splitterSizes").toByteArray());