]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/job/job.cpp
Ensure that Job duration is not negative
[bacula/bacula] / bacula / src / qt-console / job / job.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28  
29 #include "bat.h"
30 #include "job.h"
31 #include "util/fmtwidgetitem.h"
32 #include "mediainfo/mediainfo.h"
33 #include "run/run.h"
34
35 Job::Job(QString &jobId, QTreeWidgetItem *parentTreeWidgetItem)
36 {
37    setupUi(this);
38    pgInitialize(tr("Job"), parentTreeWidgetItem);
39    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
40    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/joblog.png")));
41    m_cursor = new QTextCursor(textJobLog->document());
42
43    m_jobId = jobId;
44    getFont();
45
46    connect(pbRefresh, SIGNAL(clicked()), this, SLOT(populateAll()));
47    connect(pbDelete, SIGNAL(clicked()), this, SLOT(deleteJob()));
48    connect(pbRun, SIGNAL(clicked()), this, SLOT(rerun()));
49    connect(list_Volume, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(showInfoVolume(QListWidgetItem *)));
50
51    populateAll();
52    dockPage();
53    setCurrent();
54 }
55
56 void Job::rerun()
57 {
58    new runPage(label_Name->text(),
59                label_Level->text(),
60                label_Pool->text(),
61                QString(""),              // storage
62                label_Client->text(),
63                label_FileSet->text());
64 }
65
66 void Job::showInfoVolume(QListWidgetItem *item)
67 {
68    QString s= item->text();
69    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
70
71    MediaInfo *m = new MediaInfo(pageSelectorTreeWidgetItem, s);
72    connect(m, SIGNAL(destroyed()), this, SLOT(populateTree()));
73 }
74
75 void Job::deleteJob()
76 {
77    if (QMessageBox::warning(this, "Bat",
78       tr("Are you sure you want to delete??  !!!.\n"
79 "This delete command is used to delete a Job record and all associated catalog"
80 " records that were created. This command operates only on the Catalog"
81 " database and has no effect on the actual data written to a Volume. This"
82 " command can be dangerous and we strongly recommend that you do not use"
83 " it unless you know what you are doing.  The Job and all its associated"
84 " records (File and JobMedia) will be deleted from the catalog."
85       "Press OK to proceed with delete operation.?"),
86       QMessageBox::Ok | QMessageBox::Cancel)
87       == QMessageBox::Cancel) { return; }
88
89    QString cmd("delete job jobid=");
90    cmd += m_jobId;
91    consoleCommand(cmd, false);
92    closeStackPage();
93 }
94
95 void Job::getFont()
96 {
97    QFont font = textJobLog->font();
98
99    QString dirname;
100    m_console->getDirResName(dirname);
101    QSettings settings(dirname, "bat");
102    settings.beginGroup("Console");
103    font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
104    font.setPointSize(settings.value("consolePointSize", 10).toInt());
105    font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
106    settings.endGroup();
107    textJobLog->setFont(font);
108 }
109
110 void Job::populateAll()
111 {
112 // Pmsg0(50, "populateAll()\n");
113    populateText();
114    populateForm();
115    populateVolumes();
116 }
117
118 /*
119  * Populate the text in the window
120  */
121 void Job::populateText()
122 {
123    textJobLog->clear();
124    QString query;
125    query = "SELECT Time, LogText FROM Log WHERE JobId='" + m_jobId + "' order by Time";
126
127    /* This could be a log item */
128    if (mainWin->m_sqlDebug) {
129       Pmsg1(000, "Log query cmd : %s\n", query.toUtf8().data());
130    }
131   
132    QStringList results;
133    if (m_console->sql_cmd(query, results)) {
134
135       if (!results.size()) {
136          QMessageBox::warning(this, tr("Bat"),
137             tr("There were no results!\n"
138                "It is possible you may need to add \"catalog = all\" "
139                "to the Messages resource for this job.\n"), QMessageBox::Ok);
140          return;
141       } 
142
143       QString jobstr("JobId "); /* FIXME: should this be translated ? */
144       jobstr += m_jobId;
145
146       QString htmlbuf("<html><body><pre>");
147   
148       /* Iterate through the lines of results. */
149       QString field;
150       QStringList fieldlist;
151       QString lastTime;
152       QString lastSvc;
153       foreach (QString resultline, results) {
154          fieldlist = resultline.split("\t");
155          
156          if (fieldlist.size() < 2)
157             continue;
158
159          QString curTime = fieldlist[0].trimmed();
160
161          field = fieldlist[1].trimmed();
162          int colon = field.indexOf(":");
163          if (colon > 0) {
164             /* string is like <service> <jobId xxxx>: ..." 
165              * we split at ':' then remove the jobId xxxx string (always the same) */ 
166             QString curSvc(field.left(colon).replace(jobstr,"").trimmed());
167             if (curSvc == lastSvc  && curTime == lastTime) {
168                curTime.clear();
169                curSvc.clear(); 
170             } else {
171                lastTime = curTime;
172                lastSvc = curSvc;
173             }
174 //          htmlbuf += "<td>" + curTime + "</td>";
175             htmlbuf += "\n" + curSvc + " ";
176
177             /* rest of string is marked as pre-formatted (here trimming should
178              * be avoided, to preserve original formatting) */
179             QString msg(field.mid(colon+2));
180             if (msg.startsWith( tr("Error:")) ) { /* FIXME: should really be translated ? */
181                /* error msg, use a specific class */
182                htmlbuf += "</pre><pre class=err>" + msg + "</pre><pre>";
183             } else {
184                htmlbuf += msg ;
185             }
186          } else {
187             /* non standard string, place as-is */
188             if (curTime == lastTime) {
189                curTime.clear();
190             } else {
191                lastTime = curTime;
192             }
193 //          htmlbuf += "<td>" + curTime + "</td>";
194             htmlbuf += "\n" + field ;
195          }
196   
197       } /* foreach resultline */
198
199       htmlbuf += "</pre></body></html>";
200
201       /* full text ready. Here a custom sheet is used to align columns */
202       QString logSheet(".err {color:#FF0000;}");
203       textJobLog->document()->setDefaultStyleSheet(logSheet);
204       textJobLog->document()->setHtml(htmlbuf); 
205       textJobLog->moveCursor(QTextCursor::Start);
206
207    } /* if results from query */
208   
209 }
210
211 /*
212  * Populate the text in the window
213  */
214 void Job::populateForm()
215 {
216    QString stat, err;
217    char buf[256];
218    QString query = 
219       "SELECT JobId, Job.Name, Level, Client.Name, Pool.Name, FileSet, SchedTime, StartTime, EndTime, "
220       "EndTime-StartTime AS Duration, JobBytes, JobFiles, JobErrors, JobStatus, PurgedFiles "
221       "FROM Job JOIN Client USING (ClientId) LEFT JOIN Pool ON (Job.PoolId = Pool.PoolId) "
222       "LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)"
223       "WHERE JobId=" + m_jobId; 
224    QStringList results;
225    if (m_console->sql_cmd(query, results)) {
226       QString resultline, duration;
227       QStringList fieldlist;
228
229       foreach (resultline, results) { // should have only one result
230          fieldlist = resultline.split("\t");
231          QStringListIterator fld(fieldlist);
232          label_JobId->setText(fld.next());
233          label_Name->setText(fld.next());
234          
235          label_Level->setText(job_level_to_str(fld.next()[0].toAscii()));
236
237          label_Client->setText(fld.next());
238          label_Pool->setText(fld.next());
239          label_FileSet->setText(fld.next());
240          label_SchedTime->setText(fld.next());
241          label_StartTime->setText(fld.next());
242          label_EndTime->setText(fld.next());
243          duration = fld.next();
244          /* 
245           * Note: if we have a negative duration, it is because the EndTime
246           *  is zero (i.e. the Job is still running).  We should use 
247           *  duration = StartTime - current_time
248           */
249          if (duration.left(1) == "-") {
250             duration = "0.0";
251          }
252          label_Duration->setText(duration);
253
254          label_JobBytes->setText(convertBytesSI(fld.next().toULongLong()));
255          label_JobFiles->setText(fld.next());
256          err = fld.next();
257          label_JobErrors->setText(err);
258
259          stat = fld.next();
260          if (stat == "T" && err.toInt() > 0) {
261             stat = "W";
262          }
263          label_JobStatus->setPixmap(QPixmap(":/images/" + stat + ".png"));
264          jobstatus_to_ascii_gui(stat[0].toAscii(), buf, sizeof(buf));
265          stat = buf;
266          label_JobStatus->setToolTip(stat);
267
268          chkbox_PurgedFiles->setCheckState(fld.next().toInt()?Qt::Checked:Qt::Unchecked);
269       }
270    }
271 }
272   
273 void Job::populateVolumes()
274 {
275
276    QString query = 
277       "SELECT DISTINCT VolumeName, InChanger, Slot "
278       "FROM Job JOIN JobMedia USING (JobId) JOIN Media USING (MediaId) "
279       "WHERE JobId=" + m_jobId + " ORDER BY VolumeName "; 
280    if (mainWin->m_sqlDebug) Pmsg1(0, "Query cmd : %s\n",query.toUtf8().data());
281          
282
283    QStringList results;
284    if (m_console->sql_cmd(query, results)) {
285       QString resultline;
286       QStringList fieldlist;
287       list_Volume->clear();
288       foreach (resultline, results) { // should have only one result
289          fieldlist = resultline.split("\t");
290          QStringListIterator fld(fieldlist);
291 //         QListWidgetItem(QIcon(":/images/inchanger" + fld.next() + ".png"), 
292 //                         fld.next(), list_Volume);
293          list_Volume->addItem(fld.next());
294       }
295    }
296 }
297
298 //QListWidgetItem ( const QIcon & icon, const QString & text, QListWidget * parent = 0, int type = Type )