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();
374 tval = (double *)malloc(size * sizeof(double));
375 fval = (double *)malloc(size * sizeof(double));
376 bval = (double *)malloc(size * sizeof(double));
379 foreach (PlotJobData* plotJobData, m_pjd) {
380 // printf("%.0f %.0f %s\n", plotJobData->bytes, plotJobData->files,
381 // plotJobData->dt.toString(mainWin->m_dtformat).toUtf8().data());
382 fval[j] = plotJobData->files;
383 bval[j] = plotJobData->bytes / 1024;
384 tval[j] = plotJobData->dt.toTime_t();
385 // printf("%i %.0f %.0f %.0f\n", j, tval[j], fval[j], bval[j]);
388 m_fileCurve->setData(tval,fval,size);
389 m_byteCurve->setData(tval,bval,size);
391 for (int year=2000; year<2010; year++) {
392 for (int month=1; month<=12; month++) {
395 QTextStream(&monthBegin) << year << "-" << month << "-01 00:00:00";
397 QTextStream(&monthBegin) << year << "-0" << month << "-01 00:00:00";
399 QDateTime mdt = QDateTime::fromString(monthBegin, mainWin->m_dtformat);
400 double monbeg = mdt.toTime_t();
402 // ...a vertical line at the first of each month
403 QwtPlotMarker *mX = new QwtPlotMarker();
404 mX->setLabel(mdt.toString("MMM-d"));
405 mX->setLabelAlignment(Qt::AlignRight|Qt::AlignTop);
406 mX->setLineStyle(QwtPlotMarker::VLine);
407 QPen pen(Qt::darkGray);
408 pen.setStyle(Qt::DashDotDotLine);
410 mX->setXValue(monbeg);
411 mX->attach(m_jobPlot);
415 #if !defined(__GNU_C)
423 * slot to respond to the plot type combo changing
425 void JobPlot::setPlotType(QString currentText)
427 QwtPlotCurve::CurveStyle style = QwtPlotCurve::NoCurve;
428 if (currentText == tr("Fitted")) {
429 style = QwtPlotCurve::Lines;
430 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
431 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
432 } else if (currentText == tr("Sticks")) {
433 style = QwtPlotCurve::Sticks;
434 } else if (currentText == tr("Lines")) {
435 style = QwtPlotCurve::Lines;
436 m_fileCurve->setCurveAttribute(QwtPlotCurve::Fitted);
437 m_byteCurve->setCurveAttribute(QwtPlotCurve::Fitted);
438 } else if (currentText == tr("Steps")) {
439 style = QwtPlotCurve::Steps;
440 } else if (currentText == tr("None")) {
441 style = QwtPlotCurve::NoCurve;
443 m_fileCurve->setStyle(style);
444 m_byteCurve->setStyle(style);
448 void JobPlot::fillSymbolCombo(QComboBox *q)
450 q->addItem( tr("Ellipse"), (int)QwtSymbol::Ellipse);
451 q->addItem( tr("Rect"), (int)QwtSymbol::Rect);
452 q->addItem( tr("Diamond"), (int)QwtSymbol::Diamond);
453 q->addItem( tr("Triangle"), (int)QwtSymbol::Triangle);
454 q->addItem( tr("DTrianle"), (int)QwtSymbol::DTriangle);
455 q->addItem( tr("UTriangle"), (int)QwtSymbol::UTriangle);
456 q->addItem( tr("LTriangle"), (int)QwtSymbol::LTriangle);
457 q->addItem( tr("RTriangle"), (int)QwtSymbol::RTriangle);
458 q->addItem( tr("Cross"), (int)QwtSymbol::Cross);
459 q->addItem( tr("XCross"), (int)QwtSymbol::XCross);
460 q->addItem( tr("HLine"), (int)QwtSymbol::HLine);
461 q->addItem( tr("Vline"), (int)QwtSymbol::VLine);
462 q->addItem( tr("Star1"), (int)QwtSymbol::Star1);
463 q->addItem( tr("Star2"), (int)QwtSymbol::Star2);
464 q->addItem( tr("Hexagon"), (int)QwtSymbol::Hexagon);
465 q->addItem( tr("None"), (int)QwtSymbol::NoSymbol);
470 * slot to respond to the symbol type combo changing
472 void JobPlot::setFileSymbolType(int index)
474 setSymbolType(index, 0);
477 void JobPlot::setByteSymbolType(int index)
479 setSymbolType(index, 1);
481 void JobPlot::setSymbolType(int index, int type)
484 sym.setPen(QColor(Qt::black));
489 style = controls->fileSymbolTypeCombo->itemData(index);
490 sym.setStyle( (QwtSymbol::Style)style.toInt() );
491 sym.setBrush(QColor(Qt::yellow));
492 m_fileCurve->setSymbol(sym);
495 style = controls->byteSymbolTypeCombo->itemData(index);
496 sym.setStyle( (QwtSymbol::Style)style.toInt() );
497 sym.setBrush(QColor(Qt::blue));
498 m_byteCurve->setSymbol(sym);
505 * slot to respond to the file check box changing state
507 void JobPlot::fileCheckChanged(int newstate)
509 if (newstate == Qt::Unchecked) {
510 m_fileCurve->detach();
511 m_jobPlot->enableAxis(QwtPlot::yLeft, false);
513 m_fileCurve->attach(m_jobPlot);
514 m_jobPlot->enableAxis(QwtPlot::yLeft);
520 * slot to respond to the byte check box changing state
522 void JobPlot::byteCheckChanged(int newstate)
524 if (newstate == Qt::Unchecked) {
525 m_byteCurve->detach();
526 m_jobPlot->enableAxis(QwtPlot::yRight, false);
528 m_byteCurve->attach(m_jobPlot);
529 m_jobPlot->enableAxis(QwtPlot::yRight);
535 * Save user settings associated with this page
537 void JobPlot::writeSettings()
539 QSettings settings(m_console->m_dir->name(), "bat");
540 settings.beginGroup("JobPlot");
541 settings.setValue("m_splitterSizes", m_splitter->saveState());
542 settings.setValue("fileSymbolTypeCombo", controls->fileSymbolTypeCombo->currentText());
543 settings.setValue("byteSymbolTypeCombo", controls->byteSymbolTypeCombo->currentText());
544 settings.setValue("plotTypeCombo", controls->plotTypeCombo->currentText());
549 * Read settings values for Controls
551 void JobPlot::readControlSettings()
553 QSettings settings(m_console->m_dir->name(), "bat");
554 settings.beginGroup("JobPlot");
555 int fileSymbolTypeIndex = controls->fileSymbolTypeCombo->findText(settings.value("fileSymbolTypeCombo").toString(), Qt::MatchExactly);
556 if (fileSymbolTypeIndex == -1) fileSymbolTypeIndex = 2;
557 controls->fileSymbolTypeCombo->setCurrentIndex(fileSymbolTypeIndex);
558 int byteSymbolTypeIndex = controls->byteSymbolTypeCombo->findText(settings.value("byteSymbolTypeCombo").toString(), Qt::MatchExactly);
559 if (byteSymbolTypeIndex == -1) byteSymbolTypeIndex = 3;
560 controls->byteSymbolTypeCombo->setCurrentIndex(byteSymbolTypeIndex);
561 int plotTypeIndex = controls->plotTypeCombo->findText(settings.value("plotTypeCombo").toString(), Qt::MatchExactly);
562 if (plotTypeIndex == -1) plotTypeIndex = 2;
563 controls->plotTypeCombo->setCurrentIndex(plotTypeIndex);
568 * Read and restore user settings associated with this page
570 void JobPlot::readSplitterSettings()
572 QSettings settings(m_console->m_dir->name(), "bat");
573 settings.beginGroup("JobPlot");
574 m_splitter->restoreState(settings.value("m_splitterSizes").toByteArray());