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/filtertriangle.h"
38 #include "../../processing/filters/filterchargedeconvolution.h"
39 #include "../../msrun/output/mzxmloutput.h"
40 #include "wraptandemresults.h"
41 #include "xtandempresetreader.h"
42 #include "wraptandeminput.h"
43 
44 namespace pappso
45 {
46 
47 
48 TandemWrapperRun::TandemWrapperRun(const QString &tandem_binary,
49  const QString &tmp_dir)
50 {
51 
52  setTandemBinaryPath(tandem_binary);
53 
54  if(!tmp_dir.isEmpty())
55  {
56  mpa_temporaryDirectory = new QTemporaryDir(tmp_dir + "/xtpwrp");
57  }
58  else
59  {
60  mpa_temporaryDirectory = new QTemporaryDir(QDir::tempPath() + "/xtpwrp");
61  }
62  mpa_temporaryDirectory->setAutoRemove(true);
63  if(!mpa_temporaryDirectory->isValid())
64  {
66  QObject::tr("ERROR: unable to create temporary directory %1\n Please "
67  "check file system permissions")
68  .arg(mpa_temporaryDirectory->path()));
69  }
70 }
71 
73 {
74  if(mpa_temporaryDirectory != nullptr)
75  {
77  }
78 
79  if(m_xtProcess != nullptr)
80  {
81  m_xtProcess->deleteLater();
82  }
83 }
84 
85 void
86 TandemWrapperRun::setTandemBinaryPath(const QString &tandem_binary_path)
87 {
88 
89 
90  m_tandemBinary = tandem_binary_path;
91  QSettings settings;
92  if(m_tandemBinary.isEmpty())
93  {
95  settings.value("path/tandem_binary", "/usr/bin/tandem").toString();
96  }
97  // check for tandem executable
99 
100  qDebug() << m_tandemVersion;
101  settings.setValue("path/tandem_binary", m_tandemBinary);
102 }
103 
104 
105 const QString
106 TandemWrapperRun::checkXtandemVersion(const QString &tandem_bin_path)
107 {
108  qDebug();
109  // check tandem path
110  QFileInfo tandem_exe(tandem_bin_path);
111  if(!tandem_exe.exists())
112  {
113  // dir.path() returns the unique directory path
115  QObject::tr(
116  "X!Tandem software not found at %1.\nPlease check the X!Tandem "
117  "installation on your computer and set tandem.exe path.")
118  .arg(tandem_exe.absoluteFilePath()));
119  }
120  if(!tandem_exe.isReadable())
121  {
122  // dir.path() returns the unique directory path
124  QObject::tr("Please check permissions on X!Tandem software found at %1 "
125  "(file not readable).")
126  .arg(tandem_exe.absoluteFilePath()));
127  }
128  if(!tandem_exe.isExecutable())
129  {
130  // dir.path() returns the unique directory path
132  QObject::tr("Please check permissions on X!Tandem software found at %1 "
133  "(file not executable).")
134  .arg(tandem_exe.absoluteFilePath()));
135  }
136 
137 
138  QString version_return;
139  QStringList arguments;
140 
141  arguments << "-v";
142 
143  QProcess *xt_process = new QProcess();
144  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
145 
146  xt_process->start(tandem_bin_path, arguments);
147 
148  if(!xt_process->waitForStarted())
149  {
151  QObject::tr("X!Tandem %1 process failed to start")
152  .arg(m_tandemVersion));
153  }
154 
155  while(xt_process->waitForReadyRead(1000))
156  {
157  }
158  /*
159  if (!xt_process->waitForFinished(_max_xt_time_ms)) {
160  throw pappso::PappsoException(QObject::tr("can't wait for X!Tandem process
161  to finish : timeout at %1").arg(_max_xt_time_ms));
162  }
163  */
164  QByteArray result = xt_process->readAll();
165 
166 
167  qDebug() << result.constData();
168 
169  // X! TANDEM Jackhammer TPP (2013.06.15.1 - LabKey, Insilicos, ISB)
170 
171  QRegExp parse_version("(.*) TANDEM ([A-Z,a-z, ]+) \\(([^ ,^\\)]*)(.*)");
172  qDebug() << parse_version;
173  // Pattern patt = Pattern.compile("X! TANDEM [A-Z]+ \\‍((.*)\\‍)",
174  // Pattern.CASE_INSENSITIVE);
175 
176  if(parse_version.exactMatch(result.constData()))
177  {
178  version_return = QString("X!Tandem %1 %2")
179  .arg(parse_version.capturedTexts()[2])
180  .arg(parse_version.capturedTexts()[3]); //.join(" ");
181  }
182  else
183  {
185  QObject::tr("This executable %1 may not be a valid X!Tandem software. "
186  "Please check your X!Tandem installation.")
187  .arg(tandem_bin_path));
188  }
189 
190  QProcess::ExitStatus Status = xt_process->exitStatus();
191  delete xt_process;
192  if(Status != 0)
193  {
194  // != QProcess::NormalExit
196  QObject::tr("error executing X!Tandem Status != 0 : %1 %2\n%3")
197  .arg(tandem_bin_path)
198  .arg(arguments.join(" ").arg(result.data())));
199  }
200  qDebug();
201  return version_return;
202 }
203 
204 void
206 {
207  QString message(m_xtProcess->readAllStandardOutput());
208  mp_monitor->appendText(message);
209 
210  if(message.toLower().contains("error"))
211  {
212  throw pappso::XtandemError(message);
213  }
214 
215  if(mp_monitor->shouldIstop())
216  {
217  m_xtProcess->kill();
218  delete m_xtProcess;
219  m_xtProcess = nullptr;
221  QObject::tr("X!Tandem stopped by the user"));
222  }
223 }
224 
225 void
227 {
228  mp_monitor->appendText(m_xtProcess->readAllStandardError());
229  if(mp_monitor->shouldIstop())
230  {
231  m_xtProcess->kill();
232  delete m_xtProcess;
233  m_xtProcess = nullptr;
235  QObject::tr("X!Tandem stopped by the user"));
236  }
237 }
238 
239 void
241  const QString &tmp_tandem_output,
242  const QString &final_tandem_output,
243  const QString &original_msdata_file_name)
244 {
245  mp_monitor->setStatus(QObject::tr("Rewriting X!Tandem XML result file"));
246 
247  WrapTandemResults wrap_output(final_tandem_output, original_msdata_file_name);
248 
249  wrap_output.setInputParameters("spectrum, timstof MS2 filters",
251  wrap_output.setInputParameters("spectrum, mzFormat",
252  QString("%1").arg((int)m_mzFormat));
253 
255  {
256  wrap_output.setInputParameters("output, spectrum index", "true");
257  }
258  else
259  {
260  }
261 
262  if(m_conversionTime != 0)
263  {
264  wrap_output.setInputParameters(
265  "timing, tandemwrapper conversion time (sec)",
266  QString("%1").arg(m_conversionTime / 1000));
267  }
268 
269  if(wrap_output.readFile(tmp_tandem_output))
270  {
271  }
272  else
273  {
275  QObject::tr("Error reading %1 X!Tandem output file :\n %2")
276  .arg(tmp_tandem_output)
277  .arg(wrap_output.errorString()));
278  }
279 }
280 
281 void
282 TandemWrapperRun::readTandemPresetFile(const QString &tandem_preset_file)
283 {
284  // get number of threads and centroid parameters from tandem preset
285 
286  XtandemPresetReader preset_handler;
287 
288 
289  if(preset_handler.readFile(tandem_preset_file))
290  {
291 
292  int ideal_number_of_thread = QThread::idealThreadCount();
293  int cpu_number = preset_handler.getNumberOfThreads();
294  qDebug() << " cpu_number=" << cpu_number;
295  // QThreadPool::globalInstance()->setMaxThreadCount(1);
296  if(cpu_number > ideal_number_of_thread)
297  {
298  cpu_number = ideal_number_of_thread;
299  }
300  else
301  {
302  if(cpu_number > 0)
303  {
304  QThreadPool::globalInstance()->setMaxThreadCount(cpu_number);
305 
306  qDebug() << " maxThreadCount="
307  << QThreadPool::globalInstance()->maxThreadCount();
308  }
309  }
310 
311  QString ms2_filters_str = preset_handler.getMs2FiltersOptions();
312  if(!ms2_filters_str.isEmpty())
313  {
315  std::make_shared<pappso::FilterSuiteString>(ms2_filters_str);
316  }
317  else
318  {
320  std::make_shared<pappso::FilterSuiteString>(
321  "chargeDeconvolution|0.02dalton mzExclusion|0.01dalton");
322  }
323  }
324  else
325  {
327  QObject::tr("Error reading %1 X!Tandem preset file :\n %2")
328  .arg(tandem_preset_file)
329  .arg(preset_handler.errorString()));
330  }
331 }
332 
333 
334 void
335 TandemWrapperRun::wrapTandemInputFile(const QString &tandem_input_file)
336 {
337  // read original tandem input file
338  // store original ms data file name
339  // create new mzXML data file in temporary directory
340  // create new tandem input file based on new mzXML file
341 
342 
343  QString mzxml_data_file_name =
344  mpa_temporaryDirectory->filePath("msdata.mzxml");
345  QString wrapped_tandem_input =
346  mpa_temporaryDirectory->filePath("input_tandem.xml");
347  QString wrapped_tandem_output =
348  mpa_temporaryDirectory->filePath("output_tandem.xml");
349 
350  WrapTandemInput wrap_tandem_input(
351  mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
352 
353 
354  if(wrap_tandem_input.readFile(tandem_input_file))
355  {
356  }
357  else
358  {
360  QObject::tr("Error reading %1 X!Tandem input file :\n %2")
361  .arg(tandem_input_file)
362  .arg(wrap_tandem_input.errorString()));
363  }
364 
365 
366  /*
367  *
368  XtandemInputSaxHandler wrap_input(
369  mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
370  QFile qfile(tandem_input_file);
371  if(!qfile.exists())
372  {
373  throw pappso::PappsoException(
374  QObject::tr("Tandem input file %1 does not exists")
375  .arg(QFileInfo(tandem_input_file).absoluteFilePath()));
376  }
377  QXmlInputSource xmlInputSource(&qfile);
378  QXmlSimpleReader simplereader;
379  simplereader.setContentHandler(&wrap_input);
380  simplereader.setErrorHandler(&wrap_input);
381 
382  if(simplereader.parse(xmlInputSource))
383  {
384  }
385  else
386  {
387  throw pappso::PappsoException(
388  QObject::tr("Error reading %1 X!Tandem input file :\n %2")
389  .arg(tandem_input_file)
390  .arg(wrap_input.errorString()));
391  }
392 */
393  // get number of threads and centroid parameters from tandem preset
395 
396 
397  // convert to mzXML
398  QString original_msdata_file_name =
399  wrap_tandem_input.getOriginalMsDataFileName();
400  if(convertOrginalMsData2mzXmlData(original_msdata_file_name,
401  mzxml_data_file_name))
402  {
403 
404 
405  // launch tandem
406  runTandem(wrapped_tandem_input);
407 
408  // rewrite tandem result file
410  wrapped_tandem_output,
411  wrap_tandem_input.getOriginalTandemOutputFileName(),
412  original_msdata_file_name);
413  }
414  else
415  {
416  // launch tandem on original file
417  runTandem(tandem_input_file);
418  }
419 }
420 
421 bool
423  const QString &target)
424 {
425  qDebug();
426  pappso::MsFileAccessor origin_access(origin, "runa1");
429  origin_access.getMsRunIds();
430  m_mzFormat = origin_access.getFileFormat();
431 
432  if(origin_access.getFileFormat() == pappso::MzFormat::brukerTims)
433  {
435  }
436 
437  if((origin_access.getFileFormat() == pappso::MzFormat::mzML) ||
438  (origin_access.getFileFormat() == pappso::MzFormat::brukerTims))
439  {
441  QObject::tr("Converting %1 to mzXML %2").arg(origin).arg(target));
442  pappso::MsRunReaderSPtr p_reader;
443  p_reader =
444  origin_access.msRunReaderSp(origin_access.getMsRunIds().front());
445 
446  pappso::TimsMsRunReaderMs2 *tims2_reader =
447  dynamic_cast<pappso::TimsMsRunReaderMs2 *>(p_reader.get());
448  if(tims2_reader != nullptr)
449  {
450  qDebug();
451  tims2_reader->setMs2BuiltinCentroid(true);
452 
453  if(msp_ms2FilterSuiteString != nullptr)
454  {
456  }
457  qDebug();
458  }
459 
460 
461  pappso::MzxmlOutput *p_mzxml_output;
462  QFile output_file(target);
463  // qDebug() << " TsvDirectoryWriter::writeSheet " <<
464  // QFileInfo(*_p_ofile).absoluteFilePath();
465  if(output_file.open(QIODevice::WriteOnly))
466  {
467  QElapsedTimer timer;
468  m_conversionTime = 0;
469  timer.start();
470  p_mzxml_output = new pappso::MzxmlOutput(
471  *mp_monitor, QTextStream(&output_file).device());
472 
473  p_mzxml_output->maskMs1(true);
474 
475  p_mzxml_output->setReadAhead(true);
476 
477  p_mzxml_output->write(p_reader.get());
478 
479  p_mzxml_output->close();
480 
481  delete p_mzxml_output;
482  m_conversionTime = timer.elapsed();
483 
484  mp_monitor->setStatus(QObject::tr("Conversion finished in %1 seconds")
485  .arg(m_conversionTime / 1000));
486  }
487  else
488  {
490  QObject::tr("unable to write into %1 mzXML output file")
491  .arg(target));
492  }
493 
494  qDebug();
495  return true;
496  }
497  else
498  { // other mz data formats
499  return false;
500  }
501  return true;
502 }
503 
504 void
506  const QString &tandem_input_file)
507 {
508  mp_monitor = &monitor;
509 
510  wrapTandemInputFile(tandem_input_file);
511  mp_monitor = nullptr;
512 }
513 void
514 TandemWrapperRun::runTandem(const QString &tandem_input_file)
515 {
516  if(mp_monitor->shouldIstop())
517  {
519  QObject::tr("X!Tandem stopped by the user processing on file %1")
520  .arg(tandem_input_file));
521  }
522  m_xtProcess = new QProcess();
523  QStringList arguments;
524 
525  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
526 
527  arguments << tandem_input_file;
528  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
529  m_xtProcess->start(m_tandemBinary, arguments);
530 
531  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
532 
533  connect(m_xtProcess,
534  &QProcess::readyReadStandardOutput,
535  this,
537  connect(m_xtProcess,
538  &QProcess::readyReadStandardError,
539  this,
541 
542 
543  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
544 
545  mp_monitor->setStatus(QObject::tr("Running X!Tandem"));
546 
547  if(!m_xtProcess->waitForStarted())
548  {
550  QObject::tr("X!Tandem process failed to start"));
551  }
552 
553  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
554  while(m_xtProcess->waitForFinished(m_maxTandemRunTimeMs) == false)
555  {
556  //_p_monitor->appendText(xt_process->readAll().data());
557  // data.append(xt_process->readAll());
558  if(mp_monitor->shouldIstop())
559  {
560  m_xtProcess->kill();
561  delete m_xtProcess;
562  m_xtProcess = nullptr;
564  QObject::tr("X!Tandem stopped by the user processing on file %1")
565  .arg(tandem_input_file));
566  }
567  }
568 
569  QProcess::ExitStatus Status = m_xtProcess->exitStatus();
570 
571  delete m_xtProcess;
572  if(Status != QProcess::ExitStatus::NormalExit)
573  {
574  // != QProcess::NormalExit
576  QObject::tr("error executing X!Tandem Status != 0 : %1")
577  .arg(m_tandemBinary));
578  }
579  m_xtProcess = nullptr;
580 }
581 
582 QString
584 {
585  if(msp_ms2FilterSuiteString == nullptr)
586  return "";
587  return msp_ms2FilterSuiteString.get()->toString();
588 }
589 
590 } // 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:184
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