libpappsomspp
Library for mass spectrometry
tandemwrapperrun.cpp
Go to the documentation of this file.
1 /**
2  * \file pappsomspp/processing/tandemwrapper/tandemwrapperrun.cpp
3  * \date 25/01/2020
4  * \author Olivier Langella
5  * \brief actually does really run tandem directly on Bruker's data
6  */
7 
8 /*******************************************************************************
9  * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.fr>.
10  *
11  * This file is part of PAPPSOms-tools.
12  *
13  * PAPPSOms-tools is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation, either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * PAPPSOms-tools is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with PAPPSOms-tools. If not, see <http://www.gnu.org/licenses/>.
25  *
26  ******************************************************************************/
27 
28 #include "tandemwrapperrun.h"
29 #include <QDebug>
30 #include <QFileInfo>
31 #include <QSettings>
32 #include <QThread>
33 #include <QThreadPool>
34 #include "../../exception/exceptioninterrupted.h"
35 #include "../../msfile/msfileaccessor.h"
36 #include "../../msrun/private/timsmsrunreaderms2.h"
37 #include "../../processing/filters/filterpseudocentroid.h"
38 #include "../../processing/filters/filtertriangle.h"
39 #include "../../processing/filters/filterchargedeconvolution.h"
40 #include "../../msrun/output/mzxmloutput.h"
41 #include "xtandeminputsaxhandler.h"
44 
45 namespace pappso
46 {
47 
48 
49 TandemWrapperRun::TandemWrapperRun(const QString &tandem_binary,
50  const QString &tmp_dir)
51 {
52 
53  setTandemBinaryPath(tandem_binary);
54 
55  if(!tmp_dir.isEmpty())
56  {
57  mpa_temporaryDirectory = new QTemporaryDir(tmp_dir + "/xtpwrp");
58  }
59  else
60  {
61  mpa_temporaryDirectory = new QTemporaryDir(QDir::tempPath() + "/xtpwrp");
62  }
63  mpa_temporaryDirectory->setAutoRemove(true);
64  if(!mpa_temporaryDirectory->isValid())
65  {
67  QObject::tr("ERROR: unable to create temporary directory %1\n Please "
68  "check file system permissions")
69  .arg(mpa_temporaryDirectory->path()));
70  }
71 }
72 
74 {
75  if(mpa_temporaryDirectory != nullptr)
76  {
78  }
79 
80  if(m_xtProcess != nullptr)
81  {
82  m_xtProcess->deleteLater();
83  }
84 }
85 
86 void
87 TandemWrapperRun::setTandemBinaryPath(const QString &tandem_binary_path)
88 {
89 
90 
91  m_tandemBinary = tandem_binary_path;
92  QSettings settings;
93  if(m_tandemBinary.isEmpty())
94  {
96  settings.value("path/tandem_binary", "/usr/bin/tandem").toString();
97  }
98  // check for tandem executable
100 
101  qDebug() << m_tandemVersion;
102  settings.setValue("path/tandem_binary", m_tandemBinary);
103 }
104 
105 
106 const QString
107 TandemWrapperRun::checkXtandemVersion(const QString &tandem_bin_path)
108 {
109  qDebug();
110  // check tandem path
111  QFileInfo tandem_exe(tandem_bin_path);
112  if(!tandem_exe.exists())
113  {
114  // dir.path() returns the unique directory path
116  QObject::tr(
117  "X!Tandem software not found at %1.\nPlease check the X!Tandem "
118  "installation on your computer and set tandem.exe path.")
119  .arg(tandem_exe.absoluteFilePath()));
120  }
121  if(!tandem_exe.isReadable())
122  {
123  // dir.path() returns the unique directory path
125  QObject::tr("Please check permissions on X!Tandem software found at %1 "
126  "(file not readable).")
127  .arg(tandem_exe.absoluteFilePath()));
128  }
129  if(!tandem_exe.isExecutable())
130  {
131  // dir.path() returns the unique directory path
133  QObject::tr("Please check permissions on X!Tandem software found at %1 "
134  "(file not executable).")
135  .arg(tandem_exe.absoluteFilePath()));
136  }
137 
138 
139  QString version_return;
140  QStringList arguments;
141 
142  arguments << "-v";
143 
144  QProcess *xt_process = new QProcess();
145  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
146 
147  xt_process->start(tandem_bin_path, arguments);
148 
149  if(!xt_process->waitForStarted())
150  {
152  QObject::tr("X!Tandem %1 process failed to start")
153  .arg(m_tandemVersion));
154  }
155 
156  while(xt_process->waitForReadyRead(1000))
157  {
158  }
159  /*
160  if (!xt_process->waitForFinished(_max_xt_time_ms)) {
161  throw pappso::PappsoException(QObject::tr("can't wait for X!Tandem process
162  to finish : timeout at %1").arg(_max_xt_time_ms));
163  }
164  */
165  QByteArray result = xt_process->readAll();
166 
167 
168  qDebug() << result.constData();
169 
170  // X! TANDEM Jackhammer TPP (2013.06.15.1 - LabKey, Insilicos, ISB)
171 
172  QRegExp parse_version("(.*) TANDEM ([A-Z,a-z, ]+) \\(([^ ,^\\)]*)(.*)");
173  qDebug() << parse_version;
174  // Pattern patt = Pattern.compile("X! TANDEM [A-Z]+ \\‍((.*)\\‍)",
175  // Pattern.CASE_INSENSITIVE);
176 
177  if(parse_version.exactMatch(result.constData()))
178  {
179  version_return = QString("X!Tandem %1 %2")
180  .arg(parse_version.capturedTexts()[2])
181  .arg(parse_version.capturedTexts()[3]); //.join(" ");
182  }
183  else
184  {
186  QObject::tr("This executable %1 may not be a valid X!Tandem software. "
187  "Please check your X!Tandem installation.")
188  .arg(tandem_bin_path));
189  }
190 
191  QProcess::ExitStatus Status = xt_process->exitStatus();
192  delete xt_process;
193  if(Status != 0)
194  {
195  // != QProcess::NormalExit
197  QObject::tr("error executing X!Tandem Status != 0 : %1 %2\n%3")
198  .arg(tandem_bin_path)
199  .arg(arguments.join(" ").arg(result.data())));
200  }
201  qDebug();
202  return version_return;
203 }
204 
205 void
207 {
208  QString message(m_xtProcess->readAllStandardOutput());
209  mp_monitor->appendText(message);
210 
211  if(message.toLower().contains("error"))
212  {
213  throw pappso::XtandemError(message);
214  }
215 
216  if(mp_monitor->shouldIstop())
217  {
218  m_xtProcess->kill();
219  delete m_xtProcess;
220  m_xtProcess = nullptr;
222  QObject::tr("X!Tandem stopped by the user"));
223  }
224 }
225 
226 void
228 {
229  mp_monitor->appendText(m_xtProcess->readAllStandardError());
230  if(mp_monitor->shouldIstop())
231  {
232  m_xtProcess->kill();
233  delete m_xtProcess;
234  m_xtProcess = nullptr;
236  QObject::tr("X!Tandem stopped by the user"));
237  }
238 }
239 
240 void
242  const QString &tmp_tandem_output,
243  const QString &final_tandem_output,
244  const QString &original_msdata_file_name)
245 {
246  mp_monitor->setStatus(QObject::tr("Rewriting X!Tandem XML result file"));
247 
248  XtandemOutputSaxHandler wrap_output(final_tandem_output,
249  original_msdata_file_name);
250 
251  wrap_output.setInputParameters("spectrum, timstof MS2 filters",
253 
255  {
256  wrap_output.setInputParameters("output, spectrum index", "true");
257  }
258  else
259  {
260  }
261 
262  QFile qfile(tmp_tandem_output);
263  QXmlInputSource xmlInputSource(&qfile);
264  QXmlSimpleReader simplereader;
265  simplereader.setContentHandler(&wrap_output);
266  simplereader.setErrorHandler(&wrap_output);
267 
268  if(simplereader.parse(xmlInputSource))
269  {
270  }
271  else
272  {
274  QObject::tr("Error reading %1 X!Tandem output file :\n %2")
275  .arg(tmp_tandem_output)
276  .arg(wrap_output.errorString()));
277  }
278 }
279 
280 void
281 TandemWrapperRun::readTandemPresetFile(const QString &tandem_preset_file)
282 {
283  // get number of threads and centroid parameters from tandem preset
284 
285  XtandemPresetSaxHandler preset_handler;
286 
287  QFile qfile(tandem_preset_file);
288  QXmlInputSource xmlInputSource(&qfile);
289  QXmlSimpleReader simplereader;
290  simplereader.setContentHandler(&preset_handler);
291  simplereader.setErrorHandler(&preset_handler);
292 
293  if(simplereader.parse(xmlInputSource))
294  {
295 
296  int ideal_number_of_thread = QThread::idealThreadCount();
297  int cpu_number = preset_handler.getNumberOfThreads();
298  qDebug() << " cpu_number=" << cpu_number;
299  // QThreadPool::globalInstance()->setMaxThreadCount(1);
300  if(cpu_number > ideal_number_of_thread)
301  {
302  cpu_number = ideal_number_of_thread;
303  }
304  else
305  {
306  if(cpu_number > 0)
307  {
308  QThreadPool::globalInstance()->setMaxThreadCount(cpu_number);
309 
310  qDebug() << " maxThreadCount="
311  << QThreadPool::globalInstance()->maxThreadCount();
312  }
313  }
314 
315  QString ms2_filters_str = preset_handler.getMs2FiltersOptions();
316  if(!ms2_filters_str.isEmpty())
317  {
319  std::make_shared<pappso::FilterSuiteString>(ms2_filters_str);
320  }
321  else
322  {
324  std::make_shared<pappso::FilterSuiteString>(
325  "chargeDeconvolution|0.02dalton mzExclusion|0.01dalton");
326  }
327  }
328  else
329  {
331  QObject::tr("Error reading %1 X!Tandem preset file :\n %2")
332  .arg(tandem_preset_file)
333  .arg(preset_handler.errorString()));
334  }
335 }
336 
337 
338 void
339 TandemWrapperRun::wrapTandemInputFile(const QString &tandem_input_file)
340 {
341  // read original tandem input file
342  // store original ms data file name
343  // create new mzXML data file in temporary directory
344  // create new tandem input file based on new mzXML file
345  QString mzxml_data_file_name =
346  mpa_temporaryDirectory->filePath("msdata.mzxml");
347  QString wrapped_tandem_input =
348  mpa_temporaryDirectory->filePath("input_tandem.xml");
349  QString wrapped_tandem_output =
350  mpa_temporaryDirectory->filePath("output_tandem.xml");
351  XtandemInputSaxHandler wrap_input(
352  mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
353 
354  QFile qfile(tandem_input_file);
355  if(!qfile.exists())
356  {
358  QObject::tr("Tandem input file %1 does not exists")
359  .arg(QFileInfo(tandem_input_file).absoluteFilePath()));
360  }
361  QXmlInputSource xmlInputSource(&qfile);
362  QXmlSimpleReader simplereader;
363  simplereader.setContentHandler(&wrap_input);
364  simplereader.setErrorHandler(&wrap_input);
365 
366  if(simplereader.parse(xmlInputSource))
367  {
368  }
369  else
370  {
372  QObject::tr("Error reading %1 X!Tandem input file :\n %2")
373  .arg(tandem_input_file)
374  .arg(wrap_input.errorString()));
375  }
376 
377  // get number of threads and centroid parameters from tandem preset
379 
380 
381  // convert to mzXML
382  QString original_msdata_file_name = wrap_input.getOriginalMsDataFileName();
383  if(convertOrginalMsData2mzXmlData(original_msdata_file_name,
384  mzxml_data_file_name))
385  {
386 
387 
388  // launch tandem
389  runTandem(wrapped_tandem_input);
390 
391  // rewrite tandem result file
392  writeFinalTandemOutput(wrapped_tandem_output,
393  wrap_input.getOriginalTandemOutputFileName(),
394  original_msdata_file_name);
395  }
396  else
397  {
398  // launch tandem on original file
399  runTandem(tandem_input_file);
400  }
401 }
402 
403 bool
405  const QString &target)
406 {
407  qDebug();
408  pappso::MsFileAccessor origin_access(origin, "runa1");
411  origin_access.getMsRunIds();
412 
413  if(origin_access.getFileFormat() == pappso::MzFormat::brukerTims)
414  {
416  }
417 
418  if((origin_access.getFileFormat() == pappso::MzFormat::mzML) ||
419  (origin_access.getFileFormat() == pappso::MzFormat::brukerTims))
420  {
422  QObject::tr("Converting %1 to mzXML %2").arg(origin).arg(target));
423  pappso::MsRunReaderSPtr p_reader;
424  p_reader =
425  origin_access.msRunReaderSp(origin_access.getMsRunIds().front());
426 
427  pappso::TimsMsRunReaderMs2 *tims2_reader =
428  dynamic_cast<pappso::TimsMsRunReaderMs2 *>(p_reader.get());
429  if(tims2_reader != nullptr)
430  {
431  qDebug();
432  tims2_reader->setMs2BuiltinCentroid(true);
433 
434  if(msp_ms2FilterSuiteString != nullptr)
435  {
437  }
438  qDebug();
439  }
440 
441 
442  pappso::MzxmlOutput *p_mzxml_output;
443  QFile output_file(target);
444  // qDebug() << " TsvDirectoryWriter::writeSheet " <<
445  // QFileInfo(*_p_ofile).absoluteFilePath();
446  if(output_file.open(QIODevice::WriteOnly))
447  {
448  p_mzxml_output = new pappso::MzxmlOutput(
449  *mp_monitor, QTextStream(&output_file).device());
450 
451  p_mzxml_output->maskMs1(true);
452 
453  p_mzxml_output->setReadAhead(true);
454 
455  p_mzxml_output->write(p_reader.get());
456 
457  p_mzxml_output->close();
458 
459  delete p_mzxml_output;
460  }
461  else
462  {
464  QObject::tr("unable to write into %1 mzXML output file")
465  .arg(target));
466  }
467 
468  qDebug();
469  return true;
470  }
471  else
472  { // other mz data formats
473  return false;
474  }
475  return true;
476 }
477 
478 void
480  const QString &tandem_input_file)
481 {
482  mp_monitor = &monitor;
483 
484  wrapTandemInputFile(tandem_input_file);
485  mp_monitor = nullptr;
486 }
487 void
488 TandemWrapperRun::runTandem(const QString &tandem_input_file)
489 {
490  if(mp_monitor->shouldIstop())
491  {
493  QObject::tr("X!Tandem stopped by the user processing on file %1")
494  .arg(tandem_input_file));
495  }
496  m_xtProcess = new QProcess();
497  QStringList arguments;
498 
499  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
500 
501  arguments << tandem_input_file;
502  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
503  m_xtProcess->start(m_tandemBinary, arguments);
504 
505  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
506 
507  connect(m_xtProcess,
508  &QProcess::readyReadStandardOutput,
509  this,
511  connect(m_xtProcess,
512  &QProcess::readyReadStandardError,
513  this,
515 
516 
517  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
518 
519  mp_monitor->setStatus(QObject::tr("Running X!Tandem"));
520 
521  if(!m_xtProcess->waitForStarted())
522  {
524  QObject::tr("X!Tandem process failed to start"));
525  }
526 
527  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
528  while(m_xtProcess->waitForFinished(m_maxTandemRunTimeMs) == false)
529  {
530  //_p_monitor->appendText(xt_process->readAll().data());
531  // data.append(xt_process->readAll());
532  if(mp_monitor->shouldIstop())
533  {
534  m_xtProcess->kill();
535  delete m_xtProcess;
536  m_xtProcess = nullptr;
538  QObject::tr("X!Tandem stopped by the user processing on file %1")
539  .arg(tandem_input_file));
540  }
541  }
542 
543  QProcess::ExitStatus Status = m_xtProcess->exitStatus();
544 
545  delete m_xtProcess;
546  if(Status != QProcess::ExitStatus::NormalExit)
547  {
548  // != QProcess::NormalExit
550  QObject::tr("error executing X!Tandem Status != 0 : %1")
551  .arg(m_tandemBinary));
552  }
553  m_xtProcess = nullptr;
554 }
555 
556 QString
558 {
559  if(msp_ms2FilterSuiteString == nullptr)
560  return "";
561  return msp_ms2FilterSuiteString.get()->toString();
562 }
563 
564 } // namespace pappso
MzFormat getFileFormat() const
get the raw format of mz data
MsRunReaderSPtr msRunReaderSp(MsRunIdCstSPtr ms_run_id)
void setPreferedFileReaderType(MzFormat format, FileReaderType reader_type)
given an mz format, explicitly set the prefered reader
std::vector< MsRunIdCstSPtr > getMsRunIds()
void setReadAhead(bool read_ahead)
Definition: mzxmloutput.cpp:93
void write(MsRunReader *p_msrunreader)
Definition: mzxmloutput.cpp:98
void maskMs1(bool mask_ms1)
QTemporaryDir * mpa_temporaryDirectory
void run(UiMonitorInterface &monitor, const QString &tandem_input_file)
run a tandem job
void setTandemBinaryPath(const QString &tandem_binary_path)
UiMonitorInterface * mp_monitor
bool convertOrginalMsData2mzXmlData(const QString &origin, const QString &target)
void readTandemPresetFile(const QString &tandem_preset_file)
std::shared_ptr< FilterSuiteString > msp_ms2FilterSuiteString
void wrapTandemInputFile(const QString &tandem_input_file)
void writeFinalTandemOutput(const QString &tmp_tandem_output, const QString &final_tandem_output, const QString &original_msdata_file_name)
tandem output modification tandem output is modified to contain the Bruker's file as input and centro...
TandemWrapperRun(const QString &tandem_binary, const QString &tmp_dir)
prepare a tandem run
QString getMs2FilterSuiteString() const
gets the list of filters used on MS2 spectrum
void runTandem(const QString &tandem_input_file)
run a tandem job
const QString checkXtandemVersion(const QString &tandem_bin_path)
void setMs2FilterCstSPtr(pappso::FilterInterfaceCstSPtr filter)
void setMs2BuiltinCentroid(bool centroid)
enable or disable simple centroid filter on raw tims data for MS2
virtual void setStatus(const QString &status)=0
current status of the process
virtual void appendText(const QString &text)=0
append a text to a long report
virtual bool shouldIstop()=0
should the procces be stopped ? If true, then cancel process Use this function at strategic point of ...
const QString & getOriginalTandemOutputFileName() const
const QString & getOriginalTandemPresetFileName() const
const QString & getOriginalMsDataFileName() const
void setInputParameters(const QString &label_name_attribute, const QString &input_value)
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition: aa.cpp:39
std::shared_ptr< MsRunReader > MsRunReaderSPtr
Definition: msrunreader.h:166
actually does really run tandem directly on Bruker's data
rewrites tandem xml input file with temporary files
rewrites tandem xml output file with temporary files
read tandem preset file to get centroid parameters and number of threads