1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include "Metaphlan2Worker.h"
23
24 #include <U2Core/AppContext.h>
25 #include <U2Core/AppSettings.h>
26 #include <U2Core/FailTask.h>
27 #include <U2Core/FileAndDirectoryUtils.h>
28 #include <U2Core/GUrlUtils.h>
29 #include <U2Core/TaskSignalMapper.h>
30 #include <U2Core/U2OpStatusUtils.h>
31 #include <U2Core/UserApplicationsSettings.h>
32
33 #include <U2Lang/WorkflowContext.h>
34 #include <U2Lang/WorkflowMonitor.h>
35 #include <U2Lang/WorkflowUtils.h>
36
37 #include "../ngs_reads_classification/src/NgsReadsClassificationUtils.h"
38 #include "Metaphlan2Support.h"
39 #include "Metaphlan2WorkerFactory.h"
40
41 namespace U2 {
42 namespace LocalWorkflow {
43
44 const QString Metaphlan2Worker::METAPHLAN2_ROOT_DIR = "MetaPhlAn2";
45 const QString Metaphlan2Worker::BOWTIE2OUT_DIR = "bowtie2out";
46 const QString Metaphlan2Worker::BOWTIE2OUT_SUFFIX = "bowtie2out";
47 const QString Metaphlan2Worker::PROFILE_DIR = "profiles";
48 const QString Metaphlan2Worker::PROFILE_SUFFIX = "profile";
49
Metaphlan2Worker(Actor * actor)50 Metaphlan2Worker::Metaphlan2Worker(Actor *actor)
51 : BaseWorker(actor),
52 input(nullptr) {
53 }
54
init()55 void Metaphlan2Worker::init() {
56 input = ports.value(Metaphlan2WorkerFactory::INPUT_PORT_ID);
57 SAFE_POINT(nullptr != input, QString("Port with id '%1' is nullptr").arg(Metaphlan2WorkerFactory::INPUT_PORT_ID), );
58 }
59
tick()60 Task *Metaphlan2Worker::tick() {
61 if (isReadyToRun()) {
62 U2OpStatus2Log os;
63 Metaphlan2TaskSettings settings = getSettings(os);
64 CHECK(!os.hasError(), new FailTask(os.getError()));
65
66 Metaphlan2ClassifyTask *task = new Metaphlan2ClassifyTask(settings);
67 task->addListeners(createLogListeners());
68 connect(new TaskSignalMapper(task), SIGNAL(si_taskFinished(Task *)), SLOT(sl_taskFinished(Task *)));
69 return task;
70 }
71
72 if (dataFinished()) {
73 setDone();
74 }
75
76 return nullptr;
77 }
78
cleanup()79 void Metaphlan2Worker::cleanup() {
80 }
81
sl_taskFinished(Task * task)82 void Metaphlan2Worker::sl_taskFinished(Task *task) {
83 Metaphlan2ClassifyTask *metaphlan2Task = qobject_cast<Metaphlan2ClassifyTask *>(task);
84 if (!metaphlan2Task->isFinished() || metaphlan2Task->hasError() || metaphlan2Task->isCanceled()) {
85 return;
86 }
87
88 addOutputToDashboard(metaphlan2Task->getBowtie2OutputUrl(), "Bowtie2");
89 addOutputToDashboard(metaphlan2Task->getOutputUrl(), "MetaPhlAn2");
90 }
91
isReadyToRun() const92 bool Metaphlan2Worker::isReadyToRun() const {
93 return input->hasMessage();
94 }
95
dataFinished() const96 bool Metaphlan2Worker::dataFinished() const {
97 return input->isEnded();
98 }
99
getSettings(U2OpStatus & os)100 Metaphlan2TaskSettings Metaphlan2Worker::getSettings(U2OpStatus &os) {
101 Metaphlan2TaskSettings settings;
102 settings.isPairedEnd = getValue<QString>(Metaphlan2WorkerFactory::SEQUENCING_READS) == Metaphlan2WorkerFactory::PAIRED_END;
103
104 const Message message = getMessageAndSetupScriptValues(input);
105 settings.readsUrl = message.getData().toMap()[Metaphlan2WorkerFactory::INPUT_SLOT].toString();
106 if (settings.isPairedEnd) {
107 settings.pairedReadsUrl = message.getData().toMap()[Metaphlan2WorkerFactory::PAIRED_INPUT_SLOT].toString();
108 }
109
110 settings.databaseUrl = getValue<QString>(Metaphlan2WorkerFactory::DB_URL);
111 settings.numberOfThreads = getValue<int>(Metaphlan2WorkerFactory::NUM_THREADS);
112 settings.analysisType = getValue<QString>(Metaphlan2WorkerFactory::ANALYSIS_TYPE);
113 settings.taxLevel = getValue<QString>(Metaphlan2WorkerFactory::TAX_LEVEL);
114 settings.normalizeByMetagenomeSize = getValue<QString>(Metaphlan2WorkerFactory::NORMALIZE) == Metaphlan2WorkerFactory::NOT_SKIP_NORMILIZE_BY_SIZE;
115 settings.presenceThreshold = getValue<int>(Metaphlan2WorkerFactory::PRESENCE_THRESHOLD);
116
117 const QString defaultOutputDirectory = getDefaultOutputDir();
118
119 settings.bowtie2OutputFile = getValue<QString>(Metaphlan2WorkerFactory::BOWTIE2_OUTPUT_URL);
120 if (settings.bowtie2OutputFile.isEmpty()) {
121 settings.bowtie2OutputFile = createOutputToolDirectory(defaultOutputDirectory, message, settings.isPairedEnd, Bowtie2);
122 } else {
123 const bool dirCreated = QDir().mkpath(QFileInfo(settings.bowtie2OutputFile).absoluteDir().path());
124 CHECK_EXT(dirCreated, os.setError(tr("Can't create a folder for the output file: %1").arg(settings.bowtie2OutputFile)), settings);
125 }
126 settings.bowtie2OutputFile = GUrlUtils::rollFileName(settings.bowtie2OutputFile, "_");
127
128 settings.outputFile = getValue<QString>(Metaphlan2WorkerFactory::OUTPUT_URL);
129 if (settings.outputFile.isEmpty()) {
130 settings.outputFile = createOutputToolDirectory(defaultOutputDirectory, message, settings.isPairedEnd, MetaPhlAn2);
131 } else {
132 const bool dirCreated = QDir().mkpath(QFileInfo(settings.outputFile).absoluteDir().path());
133 CHECK_EXT(dirCreated, os.setError(tr("Can't create a folder for the output file: %1").arg(settings.outputFile)), settings);
134 }
135 settings.outputFile = GUrlUtils::rollFileName(settings.outputFile, "_");
136
137 QString bowtie2AlignerPath = WorkflowUtils::getExternalToolPath(Metaphlan2Support::ET_BOWTIE_2_ALIGNER_ID);
138 CHECK_EXT(!bowtie2AlignerPath.isEmpty(), os.setError("Bowtie2 aligner isn't found"), settings);
139
140 settings.bowtie2ExternalToolPath = QFileInfo(bowtie2AlignerPath).dir().path();
141 QString pythonPath = WorkflowUtils::getExternalToolPath(Metaphlan2Support::ET_PYTHON_BIO_ID);
142 CHECK_EXT(!pythonPath.isEmpty(), os.setError("Python isn't found"), settings);
143
144 settings.pythonExternalToolPath = QFileInfo(pythonPath).dir().path();
145 settings.tmpDir = AppContext::getAppSettings()->getUserAppsSettings()->createCurrentProcessTemporarySubDir(os, METAPHLAN2_ROOT_DIR);
146 CHECK_OP(os, settings);
147
148 return settings;
149 }
150
getDefaultOutputDir() const151 QString Metaphlan2Worker::getDefaultOutputDir() const {
152 QString defaultOutputDirectory = FileAndDirectoryUtils::getWorkingDir(context->workingDir(), FileAndDirectoryUtils::WORKFLOW_INTERNAL, "", context->workingDir());
153 defaultOutputDirectory += METAPHLAN2_ROOT_DIR;
154 return GUrlUtils::rollFileName(defaultOutputDirectory, "_");
155 }
156
createOutputToolDirectory(const QString & outputDirectory,const Message & message,const bool isPairedEnd,const Output out) const157 QString Metaphlan2Worker::createOutputToolDirectory(const QString &outputDirectory, const Message &message, const bool isPairedEnd, const Output out) const {
158 QStringList suffix;
159 QString folder;
160 switch (out) {
161 case Bowtie2:
162 suffix << BOWTIE2OUT_SUFFIX;
163 folder = BOWTIE2OUT_DIR;
164 break;
165 case MetaPhlAn2:
166 suffix << PROFILE_SUFFIX;
167 folder = PROFILE_DIR;
168 break;
169 }
170 QString outputToolDirectory = QString("%1/%2").arg(outputDirectory).arg(folder);
171 createDirectory(outputToolDirectory);
172
173 const MessageMetadata metadata = context->getMetadataStorage().get(message.getMetadataId());
174 QString result = QString("%1/%2").arg(outputToolDirectory).arg(NgsReadsClassificationUtils::getBaseFileNameWithSuffixes(metadata.getFileUrl(), suffix, "txt", isPairedEnd));
175
176 return result;
177 }
178
createDirectory(QString & dir) const179 void Metaphlan2Worker::createDirectory(QString &dir) const {
180 dir = GUrlUtils::rollFileName(dir, "_");
181 QDir outDir(dir);
182 outDir.mkpath(dir);
183 }
184
addOutputToDashboard(const QString & outputUrl,const QString & outputName) const185 void Metaphlan2Worker::addOutputToDashboard(const QString &outputUrl, const QString &outputName) const {
186 if (QFileInfo::exists(outputUrl)) {
187 context->getMonitor()->addOutputFile(outputUrl, getActor()->getId());
188 } else {
189 coreLog.error(tr("%1 output file doesn't exist").arg(outputName));
190 }
191 }
192
193 } // namespace LocalWorkflow
194 } // namespace U2
195