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 "CuffmergeSupportTask.h"
23 
24 #include <QDir>
25 
26 #include <U2Core/AnnotationTableObject.h>
27 #include <U2Core/AppContext.h>
28 #include <U2Core/BaseDocumentFormats.h>
29 #include <U2Core/ExternalToolRegistry.h>
30 #include <U2Core/ExternalToolRunTask.h>
31 #include <U2Core/GUrlUtils.h>
32 #include <U2Core/IOAdapter.h>
33 #include <U2Core/L10n.h>
34 #include <U2Core/LoadDocumentTask.h>
35 #include <U2Core/U2SafePoints.h>
36 #include <U2Core/U2Type.h>
37 
38 #include <U2Lang/DbiDataStorage.h>
39 
40 #include "CufflinksSupport.h"
41 #include "CufflinksSupportTask.h"
42 #include "tophat/TopHatSettings.h"
43 
44 namespace U2 {
45 
46 const QString CuffmergeSupportTask::outSubDirBaseName("cuffmerge_out");
47 
CuffmergeSupportTask(const CuffmergeSettings & _settings)48 CuffmergeSupportTask::CuffmergeSupportTask(const CuffmergeSettings &_settings)
49     : ExternalToolSupportTask(tr("Running Cuffmerge task"), TaskFlags_FOSE_COSC), settings(_settings),
50       fileNum(0),
51       mergeTask(nullptr),
52       loadResultTask(nullptr) {
53     SAFE_POINT_EXT(nullptr != settings.storage, setError(tr("Workflow data storage is NULL")), );
54     SAFE_POINT_EXT(!settings.annotationTables.isEmpty(), setError(tr("There are no annotations to process")), );
55 }
56 
~CuffmergeSupportTask()57 CuffmergeSupportTask::~CuffmergeSupportTask() {
58     qDeleteAll(docs);
59     qDeleteAll(result);
60 }
61 
prepare()62 void CuffmergeSupportTask::prepare() {
63     setupWorkingDirPath();
64 
65     settings.outDir = GUrlUtils::createDirectory(
66         settings.outDir + "/" + outSubDirBaseName,
67         "_",
68         stateInfo);
69     CHECK_OP(stateInfo, );
70 
71     foreach (const Workflow::SharedDbiDataHandler &annTableHandler, settings.annotationTables) {
72         Task *task = createWriteTask(annTableHandler, getAnnsFilePath());
73         CHECK_OP(stateInfo, );
74         addSubTask(task);
75     }
76 }
77 
onSubTaskFinished(Task * subTask)78 QList<Task *> CuffmergeSupportTask::onSubTaskFinished(Task *subTask) {
79     if (writeTasks.contains(subTask)) {
80         writeTasks.removeOne(subTask);
81     }
82 
83     QList<Task *> newSubTasks;
84     if (writeTasks.isEmpty() && nullptr == mergeTask) {
85         newSubTasks << createCuffmergeTask();
86     }
87 
88     else if (subTask == mergeTask) {
89         loadResultTask = createLoadResultDocumentTask("merged.gtf");
90         CHECK_OP(stateInfo, newSubTasks);
91         newSubTasks << loadResultTask;
92     }
93 
94     else if (subTask == loadResultTask) {
95         QScopedPointer<Document> doc(loadResultTask->takeDocument());
96         SAFE_POINT_EXT(nullptr != doc, setError(L10N::nullPointerError("document with annotations")), newSubTasks);
97         doc->setDocumentOwnsDbiResources(false);
98         foreach (GObject *object, doc->findGObjectByType(GObjectTypes::ANNOTATION_TABLE)) {
99             doc->removeObject(object, DocumentObjectRemovalMode_Release);
100             result << qobject_cast<AnnotationTableObject *>(object);
101         }
102     }
103 
104     return newSubTasks;
105 }
106 
run()107 void CuffmergeSupportTask::run() {
108     ExternalToolSupportUtils::appendExistingFile(settings.outDir + "/merged.gtf", outputFiles);
109 }
110 
setupWorkingDirPath()111 void CuffmergeSupportTask::setupWorkingDirPath() {
112     if (0 == QString::compare(settings.workingDir, "default", Qt::CaseInsensitive)) {
113         workingDir = ExternalToolSupportUtils::createTmpDir(CufflinksSupport::CUFFMERGE_TMP_DIR, stateInfo);
114     } else {
115         workingDir = ExternalToolSupportUtils::createTmpDir(settings.workingDir, CufflinksSupport::CUFFMERGE_TMP_DIR, stateInfo);
116     }
117 }
118 
getAnnsFilePath()119 QString CuffmergeSupportTask::getAnnsFilePath() {
120     QString filePath = workingDir + QString("/tmp_%1.gtf").arg(fileNum);
121     fileNum++;
122     return filePath;
123 }
124 
createWriteTask(const Workflow::SharedDbiDataHandler & annTableHandler,const QString & filePath)125 Task *CuffmergeSupportTask::createWriteTask(const Workflow::SharedDbiDataHandler &annTableHandler, const QString &filePath) {
126     Document *doc = prepareDocument(annTableHandler, filePath);
127     CHECK_OP(stateInfo, nullptr);
128 
129     docs << doc;
130     SaveDocumentTask *task = new SaveDocumentTask(doc, doc->getIOAdapterFactory(), filePath);
131     writeTasks << task;
132 
133     return task;
134 }
135 
writeFileList()136 void CuffmergeSupportTask::writeFileList() {
137     listFilePath = workingDir + "/gtf_list.txt";
138     QFile file(listFilePath);
139     bool res = file.open(QIODevice::WriteOnly);
140     if (!res) {
141         stateInfo.setError(tr("Can not create a file: %1").arg(listFilePath));
142         return;
143     }
144 
145     QString data;
146     foreach (Document *doc, docs) {
147         data += doc->getURLString() + "\n";
148     }
149     file.write(data.toLatin1());
150     file.close();
151 }
152 
createCuffmergeTask()153 Task *CuffmergeSupportTask::createCuffmergeTask() {
154     writeFileList();
155     CHECK_OP(stateInfo, nullptr);
156 
157     QStringList args;
158     {
159         args << "-p" << QString::number(TopHatSettings::getThreadsCount());
160         if (!settings.refAnnsUrl.isEmpty()) {
161             args << "--ref-gtf" << settings.refAnnsUrl;
162         }
163         if (!settings.refSeqUrl.isEmpty()) {
164             args << "--ref-sequence" << settings.refSeqUrl;
165         }
166         args << "-o" << settings.outDir;
167         args << "--min-isoform-fraction" << QString::number(settings.minIsoformFraction);
168         args << listFilePath;
169     }
170 
171     QStringList addPaths;
172     {
173         ExternalToolRegistry *registry = AppContext::getExternalToolRegistry();
174 
175         ExternalTool *cm = registry->getById(CufflinksSupport::ET_CUFFMERGE_ID);
176         ExternalTool *cc = registry->getById(CufflinksSupport::ET_CUFFCOMPARE_ID);
177         QFileInfo cmInfo(cm->getPath());
178         QFileInfo ccInfo(cc->getPath());
179 
180         addPaths << cmInfo.dir().absolutePath();
181         addPaths << ccInfo.dir().absolutePath();
182     }
183 
184     mergeTask = new ExternalToolRunTask(CufflinksSupport::ET_CUFFMERGE_ID, args, new ExternalToolLogParser(), workingDir, addPaths);
185     setListenerForTask(mergeTask);
186     return mergeTask;
187 }
188 
createLoadResultDocumentTask(const QString & fileName)189 LoadDocumentTask *CuffmergeSupportTask::createLoadResultDocumentTask(const QString &fileName) {
190     const QString filePath = settings.outDir + "/" + fileName;
191 
192     IOAdapterFactory *iof = AppContext::getIOAdapterRegistry()->getIOAdapterFactoryById(BaseIOAdapters::LOCAL_FILE);
193     SAFE_POINT_EXT(nullptr != iof, setError(tr("An internal error occurred during getting annotations from a %1 output file!").arg(CufflinksSupport::ET_CUFFMERGE)), nullptr);
194 
195     QVariantMap hints;
196     hints[DocumentFormat::DBI_REF_HINT] = QVariant::fromValue(settings.storage->getDbiRef());
197 
198     return new LoadDocumentTask(BaseDocumentFormats::GTF, filePath, iof, hints);
199 }
200 
takeResult()201 QList<AnnotationTableObject *> CuffmergeSupportTask::takeResult() {
202     QList<AnnotationTableObject *> ret = result;
203     result.clear();
204     return ret;
205 }
206 
prepareDocument(const Workflow::SharedDbiDataHandler & annTableHandler,const QString & filePath)207 Document *CuffmergeSupportTask::prepareDocument(const Workflow::SharedDbiDataHandler &annTableHandler, const QString &filePath) {
208     DocumentFormat *format = AppContext::getDocumentFormatRegistry()->getFormatById(BaseDocumentFormats::GTF);
209     SAFE_POINT_EXT(nullptr != format, setError(L10N::nullPointerError("GTF format")), nullptr);
210 
211     IOAdapterFactory *iof = AppContext::getIOAdapterRegistry()->getIOAdapterFactoryById(BaseIOAdapters::LOCAL_FILE);
212     SAFE_POINT_EXT(nullptr != iof, setError(L10N::nullPointerError("I/O adapter factory")), nullptr);
213 
214     AnnotationTableObject *annTable = Workflow::StorageUtils::getAnnotationTableObject(settings.storage, annTableHandler);
215     SAFE_POINT_EXT(nullptr != annTable, setError(L10N::nullPointerError("source annotation data")), nullptr);
216 
217     Document *doc = format->createNewLoadedDocument(iof, filePath, stateInfo);
218     CHECK_OP(stateInfo, nullptr);
219     doc->setDocumentOwnsDbiResources(false);
220     doc->addObject(annTable);
221 
222     return doc;
223 }
224 
getOutputFiles() const225 QStringList CuffmergeSupportTask::getOutputFiles() const {
226     return outputFiles;
227 }
228 
229 /************************************************************************/
230 /* CuffmergeSettings */
231 /************************************************************************/
CuffmergeSettings()232 CuffmergeSettings::CuffmergeSettings()
233     : minIsoformFraction(0.05),
234       storage(nullptr) {
235 }
236 
237 }    // namespace U2
238