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 <QRegularExpression>
35 #include "../../exception/exceptioninterrupted.h"
36 #include "../../msfile/msfileaccessor.h"
37 #include "../../msrun/private/timsmsrunreaderms2.h"
38 #include "../../processing/filters/filtertriangle.h"
39 #include "../../processing/filters/filterchargedeconvolution.h"
40 #include "../../msrun/output/mzxmloutput.h"
41 #include "wraptandemresults.h"
42 #include "xtandempresetreader.h"
43 #include "wraptandeminput.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  QRegularExpression parse_version(
173  "(.*) TANDEM ([A-Z,a-z, ]+) \\(([^ ,^\\)]*)(.*)");
174  qDebug() << parse_version;
175  // Pattern patt = Pattern.compile("X! TANDEM [A-Z]+ \\‍((.*)\\‍)",
176  // Pattern.CASE_INSENSITIVE);
177  QRegularExpressionMatch match_parse_version =
178  parse_version.match(result.constData());
179  if(match_parse_version.hasMatch())
180  {
181  version_return = QString("X!Tandem %1 %2")
182  .arg(match_parse_version.captured(2))
183  .arg(match_parse_version.captured(3)); //.join(" ");
184  }
185  else
186  {
188  QObject::tr("This executable %1 may not be a valid X!Tandem software. "
189  "Please check your X!Tandem installation.")
190  .arg(tandem_bin_path));
191  }
192 
193  QProcess::ExitStatus Status = xt_process->exitStatus();
194  delete xt_process;
195  if(Status != 0)
196  {
197  // != QProcess::NormalExit
199  QObject::tr("error executing X!Tandem Status != 0 : %1 %2\n%3")
200  .arg(tandem_bin_path)
201  .arg(arguments.join(" ").arg(result.data())));
202  }
203  qDebug();
204  return version_return;
205 }
206 
207 void
209 {
210  QString message(m_xtProcess->readAllStandardOutput());
211  mp_monitor->appendText(message);
212 
213  if(message.toLower().contains("error"))
214  {
215  throw pappso::XtandemError(message);
216  }
217 
218  if(mp_monitor->shouldIstop())
219  {
220  m_xtProcess->kill();
221  delete m_xtProcess;
222  m_xtProcess = nullptr;
224  QObject::tr("X!Tandem stopped by the user"));
225  }
226 }
227 
228 void
230 {
231  mp_monitor->appendText(m_xtProcess->readAllStandardError());
232  if(mp_monitor->shouldIstop())
233  {
234  m_xtProcess->kill();
235  delete m_xtProcess;
236  m_xtProcess = nullptr;
238  QObject::tr("X!Tandem stopped by the user"));
239  }
240 }
241 
242 void
244  const QString &tmp_tandem_output,
245  const QString &final_tandem_output,
246  const QString &original_msdata_file_name)
247 {
248  mp_monitor->setStatus(QObject::tr("Rewriting X!Tandem XML result file"));
249 
250  WrapTandemResults wrap_output(final_tandem_output, original_msdata_file_name);
251 
252  wrap_output.setInputParameters("spectrum, timstof MS2 filters",
254  wrap_output.setInputParameters("spectrum, mzFormat",
255  QString("%1").arg((int)m_mzFormat));
256 
258  {
259  wrap_output.setInputParameters("output, spectrum index", "true");
260  }
261  else
262  {
263  }
264 
265  if(m_conversionTime != 0)
266  {
267  wrap_output.setInputParameters(
268  "timing, tandemwrapper conversion time (sec)",
269  QString("%1").arg(m_conversionTime / 1000));
270  }
271 
272  if(wrap_output.readFile(tmp_tandem_output))
273  {
274  }
275  else
276  {
278  QObject::tr("Error reading %1 X!Tandem output file :\n %2")
279  .arg(tmp_tandem_output)
280  .arg(wrap_output.errorString()));
281  }
282 }
283 
284 void
285 TandemWrapperRun::readTandemPresetFile(const QString &tandem_preset_file)
286 {
287  // get number of threads and centroid parameters from tandem preset
288 
289  XtandemPresetReader preset_handler;
290 
291 
292  if(preset_handler.readFile(tandem_preset_file))
293  {
294 
295  int ideal_number_of_thread = QThread::idealThreadCount();
296  int cpu_number = preset_handler.getNumberOfThreads();
297  qDebug() << " cpu_number=" << cpu_number;
298  // QThreadPool::globalInstance()->setMaxThreadCount(1);
299  if(cpu_number > ideal_number_of_thread)
300  {
301  cpu_number = ideal_number_of_thread;
302  }
303  else
304  {
305  if(cpu_number > 0)
306  {
307  QThreadPool::globalInstance()->setMaxThreadCount(cpu_number);
308 
309  qDebug() << " maxThreadCount="
310  << QThreadPool::globalInstance()->maxThreadCount();
311  }
312  }
313 
314  QString ms2_filters_str = preset_handler.getMs2FiltersOptions();
315  if(!ms2_filters_str.isEmpty())
316  {
318  std::make_shared<pappso::FilterSuiteString>(ms2_filters_str);
319  }
320  else
321  {
323  std::make_shared<pappso::FilterSuiteString>(
324  "chargeDeconvolution|0.02dalton mzExclusion|0.01dalton");
325  }
326  }
327  else
328  {
330  QObject::tr("Error reading %1 X!Tandem preset file :\n %2")
331  .arg(tandem_preset_file)
332  .arg(preset_handler.errorString()));
333  }
334 }
335 
336 
337 void
338 TandemWrapperRun::wrapTandemInputFile(const QString &tandem_input_file)
339 {
340  // read original tandem input file
341  // store original ms data file name
342  // create new mzXML data file in temporary directory
343  // create new tandem input file based on new mzXML file
344 
345 
346  QString mzxml_data_file_name =
347  mpa_temporaryDirectory->filePath("msdata.mzxml");
348  QString wrapped_tandem_input =
349  mpa_temporaryDirectory->filePath("input_tandem.xml");
350  QString wrapped_tandem_output =
351  mpa_temporaryDirectory->filePath("output_tandem.xml");
352 
353  WrapTandemInput wrap_tandem_input(
354  mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
355 
356 
357  if(wrap_tandem_input.readFile(tandem_input_file))
358  {
359  }
360  else
361  {
363  QObject::tr("Error reading %1 X!Tandem input file :\n %2")
364  .arg(tandem_input_file)
365  .arg(wrap_tandem_input.errorString()));
366  }
367 
368 
369  /*
370  *
371  XtandemInputSaxHandler wrap_input(
372  mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
373  QFile qfile(tandem_input_file);
374  if(!qfile.exists())
375  {
376  throw pappso::PappsoException(
377  QObject::tr("Tandem input file %1 does not exists")
378  .arg(QFileInfo(tandem_input_file).absoluteFilePath()));
379  }
380  QXmlInputSource xmlInputSource(&qfile);
381  QXmlSimpleReader simplereader;
382  simplereader.setContentHandler(&wrap_input);
383  simplereader.setErrorHandler(&wrap_input);
384 
385  if(simplereader.parse(xmlInputSource))
386  {
387  }
388  else
389  {
390  throw pappso::PappsoException(
391  QObject::tr("Error reading %1 X!Tandem input file :\n %2")
392  .arg(tandem_input_file)
393  .arg(wrap_input.errorString()));
394  }
395 */
396  // get number of threads and centroid parameters from tandem preset
398 
399 
400  // convert to mzXML
401  QString original_msdata_file_name =
402  wrap_tandem_input.getOriginalMsDataFileName();
403  if(convertOrginalMsData2mzXmlData(original_msdata_file_name,
404  mzxml_data_file_name))
405  {
406 
407 
408  // launch tandem
409  runTandem(wrapped_tandem_input);
410 
411  // rewrite tandem result file
413  wrapped_tandem_output,
414  wrap_tandem_input.getOriginalTandemOutputFileName(),
415  original_msdata_file_name);
416  }
417  else
418  {
419  // launch tandem on original file
420  runTandem(tandem_input_file);
421  }
422 }
423 
424 bool
426  const QString &target)
427 {
428  qDebug();
429  pappso::MsFileAccessor origin_access(origin, "runa1");
432  origin_access.getMsRunIds();
433  m_mzFormat = origin_access.getFileFormat();
435  {
437  QObject::tr("%1 file format not known").arg(origin));
438  }
439 
440  if(origin_access.getFileFormat() == pappso::MzFormat::brukerTims)
441  {
443  }
444 
445  if((origin_access.getFileFormat() == pappso::MzFormat::mzML) ||
446  (origin_access.getFileFormat() == pappso::MzFormat::brukerTims))
447  {
449  QObject::tr("Converting %1 to mzXML %2").arg(origin).arg(target));
450  pappso::MsRunReaderSPtr p_reader;
451  p_reader =
452  origin_access.msRunReaderSp(origin_access.getMsRunIds().front());
453 
454  pappso::TimsMsRunReaderMs2 *tims2_reader =
455  dynamic_cast<pappso::TimsMsRunReaderMs2 *>(p_reader.get());
456  if(tims2_reader != nullptr)
457  {
458  qDebug();
459  tims2_reader->setMs2BuiltinCentroid(true);
460 
461  if(msp_ms2FilterSuiteString != nullptr)
462  {
464  }
465  qDebug();
466  }
467 
468 
469  pappso::MzxmlOutput *p_mzxml_output;
470  QFile output_file(target);
471  // qDebug() << " TsvDirectoryWriter::writeSheet " <<
472  // QFileInfo(*_p_ofile).absoluteFilePath();
473  if(output_file.open(QIODevice::WriteOnly))
474  {
475  QElapsedTimer timer;
476  m_conversionTime = 0;
477  timer.start();
478  p_mzxml_output = new pappso::MzxmlOutput(
479  *mp_monitor, QTextStream(&output_file).device());
480 
481  p_mzxml_output->maskMs1(true);
482 
483  p_mzxml_output->setReadAhead(true);
484 
485  p_mzxml_output->write(p_reader.get());
486 
487  p_mzxml_output->close();
488 
489  delete p_mzxml_output;
490  m_conversionTime = timer.elapsed();
491 
492  mp_monitor->setStatus(QObject::tr("Conversion finished in %1 seconds")
493  .arg(m_conversionTime / 1000));
494  }
495  else
496  {
498  QObject::tr("unable to write into %1 mzXML output file")
499  .arg(target));
500  }
501 
502  qDebug();
503  return true;
504  }
505  else
506  { // other mz data formats
507  return false;
508  }
509  return true;
510 }
511 
512 void
514  const QString &tandem_input_file)
515 {
516  mp_monitor = &monitor;
517 
518  wrapTandemInputFile(tandem_input_file);
519  mp_monitor = nullptr;
520 }
521 void
522 TandemWrapperRun::runTandem(const QString &tandem_input_file)
523 {
524  if(mp_monitor->shouldIstop())
525  {
527  QObject::tr("X!Tandem stopped by the user processing on file %1")
528  .arg(tandem_input_file));
529  }
530  m_xtProcess = new QProcess();
531  QStringList arguments;
532 
533  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
534 
535  arguments << tandem_input_file;
536  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
537  m_xtProcess->start(m_tandemBinary, arguments);
538 
539  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
540 
541  connect(m_xtProcess,
542  &QProcess::readyReadStandardOutput,
543  this,
545  connect(m_xtProcess,
546  &QProcess::readyReadStandardError,
547  this,
549 
550 
551  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
552 
553  mp_monitor->setStatus(QObject::tr("Running X!Tandem"));
554 
555  if(!m_xtProcess->waitForStarted())
556  {
558  QObject::tr("X!Tandem process failed to start"));
559  }
560 
561  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
562  while(m_xtProcess->waitForFinished(m_maxTandemRunTimeMs) == false)
563  {
564  //_p_monitor->appendText(xt_process->readAll().data());
565  // data.append(xt_process->readAll());
566  if(mp_monitor->shouldIstop())
567  {
568  m_xtProcess->kill();
569  delete m_xtProcess;
570  m_xtProcess = nullptr;
572  QObject::tr("X!Tandem stopped by the user processing on file %1")
573  .arg(tandem_input_file));
574  }
575  }
576 
577  QProcess::ExitStatus Status = m_xtProcess->exitStatus();
578 
579  delete m_xtProcess;
580  if(Status != QProcess::ExitStatus::NormalExit)
581  {
582  // != QProcess::NormalExit
584  QObject::tr("error executing X!Tandem Status != 0 : %1")
585  .arg(m_tandemBinary));
586  }
587  m_xtProcess = nullptr;
588 }
589 
590 QString
592 {
593  if(msp_ms2FilterSuiteString == nullptr)
594  return "";
595  return msp_ms2FilterSuiteString.get()->toString();
596 }
597 
598 } // 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
pappso::MzFormat m_mzFormat
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)
virtual bool readFile(const QString &fileName)
const QString getMs2FiltersOptions() const
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:185
@ unknown
unknown format
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