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 "WorkflowDesignerPlugin.h"
23
24 #include <QDir>
25 #include <QMenu>
26 #include <QMessageBox>
27
28 #include <U2Core/AppContext.h>
29 #include <U2Core/CMDLineHelpProvider.h>
30 #include <U2Core/CMDLineRegistry.h>
31 #include <U2Core/CMDLineUtils.h>
32 #include <U2Core/FileAndDirectoryUtils.h>
33 #include <U2Core/GAutoDeleteList.h>
34 #include <U2Core/L10n.h>
35 #include <U2Core/ServiceTypes.h>
36 #include <U2Core/Settings.h>
37 #include <U2Core/Task.h>
38 #include <U2Core/TaskStarter.h>
39 #include <U2Core/U2SafePoints.h>
40
41 #include <U2Designer/DashboardInfoRegistry.h>
42
43 #include <U2Gui/ToolsMenu.h>
44
45 #include <U2Lang/IncludedProtoFactory.h>
46 #include <U2Lang/WorkflowEnv.h>
47 #include <U2Lang/WorkflowSettings.h>
48 #include <U2Lang/WorkflowTasksRegistry.h>
49
50 #include <U2Test/GTest.h>
51 #include <U2Test/GTestFrameworkComponents.h>
52
53 #include "WorkflowDocument.h"
54 #include "WorkflowSamples.h"
55 #include "WorkflowSettingsController.h"
56 #include "WorkflowViewController.h"
57 #include "cmdline/GalaxyConfigTask.h"
58 #include "cmdline/WorkflowCMDLineTasks.h"
59 #include "library/CoreLib.h"
60 #include "library/IncludedProtoFactoryImpl.h"
61 #include "tasks/ReadAssemblyTask.h"
62 #include "util/DatasetsCountValidator.h"
63
64 namespace U2 {
65
U2_PLUGIN_INIT_FUNC()66 extern "C" Q_DECL_EXPORT Plugin *U2_PLUGIN_INIT_FUNC() {
67 WorkflowDesignerPlugin *plug = new WorkflowDesignerPlugin();
68 return plug;
69 }
70
71 const QString WorkflowDesignerPlugin::RUN_WORKFLOW = "task";
72 const QString WorkflowDesignerPlugin::PRINT = "print";
73 const QString WorkflowDesignerPlugin::CUSTOM_EL_WITH_SCRIPTS_DIR = "custom-element-script-dir";
74 const QString WorkflowDesignerPlugin::CUSTOM_EXTERNAL_TOOL_DIR = "custom-element-external-tool-dir";
75 const QString WorkflowDesignerPlugin::INCLUDED_ELEMENTS_DIR = "imported-workflow-element-dir";
76 const QString WorkflowDesignerPlugin::WORKFLOW_OUTPUT_DIR = "workflow-output-dir";
77
WorkflowDesignerPlugin()78 WorkflowDesignerPlugin::WorkflowDesignerPlugin()
79 : Plugin(tr("Workflow Designer"), tr("Workflow Designer allows one to create complex computational workflows.")) {
80 if (AppContext::getMainWindow()) {
81 services << new WorkflowDesignerService();
82 AppContext::getAppSettingsGUI()->registerPage(new WorkflowSettingsPageController());
83 AppContext::getObjectViewFactoryRegistry()->registerGObjectViewFactory(new WorkflowViewFactory(this));
84 }
85 IncludedProtoFactory::init(new IncludedProtoFactoryImpl());
86
87 AppContext::getDocumentFormatRegistry()->registerFormat(new WorkflowDocFormat(this));
88
89 // xml workflow tests removed. commented for future uses
90
91 // GTestFormatRegistry* tfr = AppContext::getTestFramework()->getTestFormatRegistry();
92 // XMLTestFormat *xmlTestFormat = qobject_cast<XMLTestFormat*>(tfr->findFormat("XML"));
93 // assert(xmlTestFormat!=NULL);
94
95 // GAutoDeleteList<XMLTestFactory>* l = new GAutoDeleteList<XMLTestFactory>(this);
96 // l->qlist = WorkflowTests::createTestFactories();
97
98 // foreach(XMLTestFactory* f, l->qlist) {
99 // bool res = xmlTestFormat->registerTestFactory(f);
100 // assert(res);
101 // Q_UNUSED(res);
102 // }
103
104 registerCMDLineHelp();
105 processCMDLineOptions();
106 WorkflowEnv::getActorValidatorRegistry()->addValidator(DatasetsCountValidator::ID, new DatasetsCountValidator());
107
108 CHECK(AppContext::getPluginSupport(), );
109 connect(AppContext::getPluginSupport(), SIGNAL(si_allStartUpPluginsLoaded()), SLOT(sl_initWorkers()));
110
111 DashboardInfoRegistry *dashboardsInfoRegistry = AppContext::getDashboardInfoRegistry();
112 SAFE_POINT(nullptr != dashboardsInfoRegistry, "dashboardsInfoRegistry is nullptr", );
113 AppContext::getDashboardInfoRegistry()->scanDashboardsDir();
114 }
115
processCMDLineOptions()116 void WorkflowDesignerPlugin::processCMDLineOptions() {
117 CMDLineRegistry *cmdlineReg = AppContext::getCMDLineRegistry();
118 assert(cmdlineReg != nullptr);
119
120 if (cmdlineReg->hasParameter(CUSTOM_EL_WITH_SCRIPTS_DIR)) {
121 WorkflowSettings::setUserDirectory(FileAndDirectoryUtils::getAbsolutePath(cmdlineReg->getParameterValue(CUSTOM_EL_WITH_SCRIPTS_DIR)));
122 }
123 if (cmdlineReg->hasParameter(CUSTOM_EXTERNAL_TOOL_DIR)) {
124 WorkflowSettings::setExternalToolDirectory(FileAndDirectoryUtils::getAbsolutePath(cmdlineReg->getParameterValue(CUSTOM_EXTERNAL_TOOL_DIR)));
125 }
126 if (cmdlineReg->hasParameter(INCLUDED_ELEMENTS_DIR)) {
127 WorkflowSettings::setIncludedElementsDirectory(FileAndDirectoryUtils::getAbsolutePath(cmdlineReg->getParameterValue(INCLUDED_ELEMENTS_DIR)));
128 }
129 if (cmdlineReg->hasParameter(WORKFLOW_OUTPUT_DIR)) {
130 WorkflowSettings::setWorkflowOutputDirectory(FileAndDirectoryUtils::getAbsolutePath(cmdlineReg->getParameterValue(WORKFLOW_OUTPUT_DIR)));
131 }
132
133 bool consoleMode = !AppContext::isGUIMode(); // only in console mode we run workflows by default. Otherwise we show them
134 if (cmdlineReg->hasParameter(RUN_WORKFLOW) || (consoleMode && !CMDLineRegistryUtils::getPureValues().isEmpty())) {
135 Task *t = new WorkflowRunFromCMDLineTask();
136 connect(AppContext::getTaskScheduler(), SIGNAL(si_ugeneIsReadyToWork()), new TaskStarter(t), SLOT(registerTask()));
137 } else {
138 if (cmdlineReg->hasParameter(GalaxyConfigTask::GALAXY_CONFIG_OPTION) && consoleMode) {
139 const QString schemePath = cmdlineReg->getParameterValue(GalaxyConfigTask::GALAXY_CONFIG_OPTION);
140 const QString ugenePath = cmdlineReg->getParameterValue(GalaxyConfigTask::UGENE_PATH_OPTION);
141 const QString galaxyPath = cmdlineReg->getParameterValue(GalaxyConfigTask::GALAXY_PATH_OPTION);
142 const QString destinationPath = nullptr;
143 Task *t = new GalaxyConfigTask(schemePath, ugenePath, galaxyPath, destinationPath);
144 connect(AppContext::getPluginSupport(), SIGNAL(si_allStartUpPluginsLoaded()), new TaskStarter(t), SLOT(registerTask()));
145 }
146 }
147 }
148
registerWorkflowTasks()149 void WorkflowDesignerPlugin::registerWorkflowTasks() {
150 WorkflowTasksRegistry *registry = WorkflowEnv::getWorkflowTasksRegistry();
151
152 ReadDocumentTaskFactory *readAssemblyFactory = new ReadAssemblyTaskFactory();
153 bool ok = registry->registerReadDocumentTaskFactory(readAssemblyFactory);
154 if (!ok) {
155 coreLog.error("Can not register read assembly task");
156 }
157 }
158
registerCMDLineHelp()159 void WorkflowDesignerPlugin::registerCMDLineHelp() {
160 CMDLineRegistry *cmdLineRegistry = AppContext::getCMDLineRegistry();
161 assert(nullptr != cmdLineRegistry);
162
163 CMDLineHelpProvider *taskSection = new CMDLineHelpProvider(
164 RUN_WORKFLOW,
165 tr("Runs the specified task."),
166 tr("Runs the specified task. A path to a user-defined UGENE workflow"
167 " be used as a task name."),
168 tr("<task_name> [<task_parameter>=value ...]"));
169
170 cmdLineRegistry->registerCMDLineHelpProvider(taskSection);
171
172 CMDLineHelpProvider *printSection = new CMDLineHelpProvider(
173 PRINT,
174 tr("Prints the content of the specified slot."),
175 tr("Prints the content of the specified slot. The incoming/outcoming content of"
176 " specified slot is printed to the standard output."),
177 tr("<actor_name>.<port_name>.<slot_name>"));
178 Q_UNUSED(printSection);
179
180 CMDLineHelpProvider *galaxyConfigSection = new CMDLineHelpProvider(
181 GalaxyConfigTask::GALAXY_CONFIG_OPTION,
182 tr("Creates new Galaxy tool config."),
183 tr("Creates new Galaxy tool config from existing workflow. Paths to UGENE"
184 " and Galaxy can be set"),
185 tr("<uwl-file> [--ugene-path=value] [--galaxy-path=value]"));
186
187 cmdLineRegistry->registerCMDLineHelpProvider(galaxyConfigSection);
188
189 // CMDLineHelpProvider * remoteMachineSectionArguments = new CMDLineHelpProvider( REMOTE_MACHINE, "<path-to-machine-file>");
190 // CMDLineHelpProvider * remoteMachineSection = new CMDLineHelpProvider( REMOTE_MACHINE, tr("run provided tasks on given remote machine") );
191 // TODO: bug UGENE-23
192 // cmdLineRegistry->registerCMDLineHelpProvider( remoteMachineSectionArguments );
193 // cmdLineRegistry->registerCMDLineHelpProvider( remoteMachineSection );
194 }
195
sl_initWorkers()196 void WorkflowDesignerPlugin::sl_initWorkers() {
197 Workflow::CoreLib::init();
198 registerWorkflowTasks();
199 Workflow::CoreLib::initIncludedWorkers();
200 }
201
~WorkflowDesignerPlugin()202 WorkflowDesignerPlugin::~WorkflowDesignerPlugin() {
203 Workflow::CoreLib::cleanup();
204 }
205
206 class CloseDesignerTask : public Task {
207 public:
CloseDesignerTask(WorkflowDesignerService * s)208 CloseDesignerTask(WorkflowDesignerService *s)
209 : Task(U2::WorkflowDesignerPlugin::tr("Close Designer"), TaskFlag_NoRun),
210 service(s) {
211 }
212 virtual void prepare();
213
214 private:
215 WorkflowDesignerService *service;
216 };
217
prepare()218 void CloseDesignerTask::prepare() {
219 if (!service->closeViews()) {
220 stateInfo.setError(U2::WorkflowDesignerPlugin::tr("Close Designer canceled"));
221 }
222 }
223
createServiceDisablingTask()224 Task *WorkflowDesignerService::createServiceDisablingTask() {
225 return new CloseDesignerTask(this);
226 }
227
WorkflowDesignerService()228 WorkflowDesignerService::WorkflowDesignerService()
229 : Service(Service_WorkflowDesigner, tr("Workflow Designer"), ""),
230 designerAction(nullptr), managerAction(nullptr), newWorkflowAction(nullptr) {
231 }
232
serviceStateChangedCallback(ServiceState,bool enabledStateChanged)233 void WorkflowDesignerService::serviceStateChangedCallback(ServiceState, bool enabledStateChanged) {
234 IdRegistry<WelcomePageAction> *welcomePageActions = AppContext::getWelcomePageActionRegistry();
235 SAFE_POINT(nullptr != welcomePageActions, L10N::nullPointerError("Welcome Page Actions"), );
236
237 if (!enabledStateChanged) {
238 return;
239 }
240 if (isEnabled()) {
241 SAFE_POINT(nullptr == designerAction, "Illegal WD service state", );
242 SAFE_POINT(nullptr == newWorkflowAction, "Illegal WD service state", );
243
244 if (!AppContext::getPluginSupport()->isAllPluginsLoaded()) {
245 connect(AppContext::getPluginSupport(), SIGNAL(si_allStartUpPluginsLoaded()), SLOT(sl_startWorkflowPlugin()));
246 } else {
247 sl_startWorkflowPlugin();
248 }
249
250 welcomePageActions->registerEntry(new WorkflowWelcomePageAction(this));
251 } else {
252 welcomePageActions->unregisterEntry(BaseWelcomePageActions::CREATE_WORKFLOW);
253 delete newWorkflowAction;
254 newWorkflowAction = nullptr;
255 delete designerAction;
256 designerAction = nullptr;
257 }
258 }
259
sl_startWorkflowPlugin()260 void WorkflowDesignerService::sl_startWorkflowPlugin() {
261 initDesignerAction();
262 initNewWorkflowAction();
263 initSampleActions();
264 }
265
initDesignerAction()266 void WorkflowDesignerService::initDesignerAction() {
267 designerAction = new QAction(QIcon(":/workflow_designer/images/wd.png"), tr("Workflow Designer..."), this);
268 designerAction->setObjectName(ToolsMenu::WORKFLOW_DESIGNER);
269 #ifdef _DEBUG
270 designerAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_D));
271 #endif
272 connect(designerAction, SIGNAL(triggered()), SLOT(sl_showDesignerWindow()));
273 ToolsMenu::addAction(ToolsMenu::TOOLS, designerAction);
274 }
275
initNewWorkflowAction()276 void WorkflowDesignerService::initNewWorkflowAction() {
277 newWorkflowAction = new QAction(QIcon(":/workflow_designer/images/wd.png"), tr("New workflow..."), this);
278 newWorkflowAction->setObjectName("New workflow");
279 connect(newWorkflowAction, SIGNAL(triggered()), SLOT(sl_showDesignerWindow()));
280
281 QMenu *fileMenu = AppContext::getMainWindow()->getTopLevelMenu(MWMENU_FILE);
282 QAction *beforeAction = nullptr;
283 foreach (QAction *action, fileMenu->actions()) {
284 if (action->objectName() == ACTION_PROJECTSUPPORT__NEW_SECTION_SEPARATOR) {
285 beforeAction = action;
286 break;
287 }
288 }
289 fileMenu->insertAction(beforeAction, newWorkflowAction);
290 }
291
closeViews()292 bool WorkflowDesignerService::closeViews() {
293 MWMDIManager *wm = AppContext::getMainWindow()->getMDIManager();
294 assert(wm);
295 foreach (MWMDIWindow *w, wm->getWindows()) {
296 WorkflowView *view = qobject_cast<WorkflowView *>(w);
297 if (view) {
298 if (!AppContext::getMainWindow()->getMDIManager()->closeMDIWindow(view)) {
299 return false;
300 }
301 }
302 }
303 return true;
304 }
305
checkServiceState() const306 bool WorkflowDesignerService::checkServiceState() const {
307 if (isDisabled()) {
308 QMessageBox::warning(QApplication::activeWindow(), L10N::warningTitle(), L10N::internalError(tr("Can not open Workflow Designer. Please, try to reload UGENE.")));
309 return false;
310 }
311 return true;
312 }
313
sl_showDesignerWindow()314 void WorkflowDesignerService::sl_showDesignerWindow() {
315 CHECK(checkServiceState(), );
316 WorkflowView::openWD(nullptr); // FIXME
317 }
318
sl_sampleActionClicked(const SampleAction & action)319 void WorkflowDesignerService::sl_sampleActionClicked(const SampleAction &action) {
320 CHECK(checkServiceState(), );
321
322 WorkflowView *view = WorkflowView::openWD(nullptr);
323 CHECK(nullptr != view, );
324
325 view->sl_loadScene(QDir("data:workflow_samples").path() + "/" + action.samplePath, false);
326 }
327
createServiceEnablingTask()328 Task *WorkflowDesignerService::createServiceEnablingTask() {
329 QString defaultDir = QDir::searchPaths(PATH_PREFIX_DATA).first() + "/workflow_samples";
330
331 return SampleRegistry::init(QStringList(defaultDir));
332 }
333
initSampleActions()334 void WorkflowDesignerService::initSampleActions() {
335 SampleActionsManager *samples = new SampleActionsManager(this);
336 connect(samples, SIGNAL(si_clicked(const SampleAction &)), SLOT(sl_sampleActionClicked(const SampleAction &)));
337
338 const QString externalToolsPlugin = "external_tool_support";
339
340 SampleAction ngsControl(ToolsMenu::NGS_CONTROL, ToolsMenu::NGS_MENU, "NGS/fastqc.uwl", tr("Reads quality control..."));
341 ngsControl.requiredPlugins << externalToolsPlugin;
342
343 // SPAdes is available only on Linux and Mac.
344 #if defined(Q_OS_LINUX) || defined(Q_OS_DARWIN)
345 SampleAction ngsDenovo(ToolsMenu::NGS_DENOVO, ToolsMenu::NGS_MENU, "NGS/from_tools_menu_only/ngs_assembly.uwl", tr("Reads de novo assembly (with SPAdes)..."));
346 ngsDenovo.requiredPlugins << externalToolsPlugin;
347 samples->registerAction(ngsDenovo);
348 #endif
349 SampleAction ngsScaffold(ToolsMenu::NGS_SCAFFOLD, ToolsMenu::NGS_MENU, "Scenarios/length_filter.uwl", tr("Filter short scaffolds..."));
350 ngsScaffold.requiredPlugins << externalToolsPlugin;
351 SampleAction ngsRawDna(ToolsMenu::NGS_RAW_DNA, ToolsMenu::NGS_MENU, "NGS/raw_dna.uwl", tr("Raw DNA-Seq data processing..."));
352 ngsRawDna.requiredPlugins << externalToolsPlugin;
353 SampleAction ngsVariants(ToolsMenu::NGS_CALL_VARIANTS, ToolsMenu::NGS_MENU, "NGS/ngs_variant_calling.uwl", tr("Variant calling..."));
354 ngsVariants.requiredPlugins << externalToolsPlugin;
355 SampleAction ngsEffect(ToolsMenu::NGS_VARIANT_EFFECT, ToolsMenu::NGS_MENU, "NGS/ngs_variant_annotation.uwl", tr("Annotate variants and predict effects..."));
356 ngsEffect.requiredPlugins << externalToolsPlugin;
357 SampleAction ngsRawRna(ToolsMenu::NGS_RAW_RNA, ToolsMenu::NGS_MENU, "NGS/raw_rna.uwl", tr("Raw RNA-Seq data processing..."));
358 ngsRawRna.requiredPlugins << externalToolsPlugin;
359 SampleAction ngsRna(ToolsMenu::NGS_RNA, ToolsMenu::NGS_MENU, "NGS/ngs_transcriptomics_tophat_stringtie.uwl", tr("RNA-Seq data analysis..."));
360 ngsRna.requiredPlugins << externalToolsPlugin;
361 SampleAction ngsTranscript(ToolsMenu::NGS_TRANSCRIPT, ToolsMenu::NGS_MENU, "NGS/extract_transcript_seq.uwl", tr("Extract transcript sequences..."));
362 ngsTranscript.requiredPlugins << externalToolsPlugin;
363 SampleAction ngsRawChip(ToolsMenu::NGS_RAW_CHIP, ToolsMenu::NGS_MENU, "NGS/raw_chip.uwl", tr("Raw ChIP-Seq data processing..."));
364 ngsRawChip.requiredPlugins << externalToolsPlugin;
365 SampleAction ngsChip(ToolsMenu::NGS_CHIP, ToolsMenu::NGS_MENU, "NGS/cistrome.uwl", tr("ChIP-Seq data analysis..."));
366 ngsChip.requiredPlugins << externalToolsPlugin;
367 SampleAction ngsClassification(ToolsMenu::NGS_CLASSIFICATION, ToolsMenu::NGS_MENU, "NGS/from_tools_menu_only/ngs_classification.uwl", tr("Metagenomics classification..."));
368 ngsChip.requiredPlugins << externalToolsPlugin << "kraken_support"
369 << "clark_support"
370 << "diamond_support"
371 << "wevote_support"
372 << "ngs_reads_classification";
373 SampleAction ngsCoverage(ToolsMenu::NGS_COVERAGE, ToolsMenu::NGS_MENU, "NGS/extract_coverage.uwl", tr("Extract coverage from assemblies..."));
374 ngsCoverage.requiredPlugins << externalToolsPlugin;
375 SampleAction ngsConsensus(ToolsMenu::NGS_CONSENSUS, ToolsMenu::NGS_MENU, "NGS/consensus.uwl", tr("Extract consensus from assemblies..."));
376 ngsConsensus.requiredPlugins << externalToolsPlugin;
377
378 SampleAction blastNcbi(ToolsMenu::BLAST_NCBI, ToolsMenu::BLAST_MENU, "Scenarios/remote_blasting.uwl", tr("Remote NCBI BLAST..."));
379 blastNcbi.requiredPlugins << "remote_blast";
380
381 samples->registerAction(ngsControl);
382 samples->registerAction(ngsScaffold);
383 samples->registerAction(ngsRawDna);
384 samples->registerAction(ngsVariants);
385 samples->registerAction(ngsEffect);
386 samples->registerAction(ngsRawRna);
387 samples->registerAction(ngsRna);
388 samples->registerAction(ngsTranscript);
389 samples->registerAction(ngsRawChip);
390 samples->registerAction(ngsChip);
391 samples->registerAction(ngsClassification);
392 samples->registerAction(ngsCoverage);
393 samples->registerAction(ngsConsensus);
394 samples->registerAction(blastNcbi);
395 }
396
397 /************************************************************************/
398 /* WorkflowWelcomePageAction */
399 /************************************************************************/
WorkflowWelcomePageAction(WorkflowDesignerService * service)400 WorkflowWelcomePageAction::WorkflowWelcomePageAction(WorkflowDesignerService *service)
401 : WelcomePageAction(BaseWelcomePageActions::CREATE_WORKFLOW), service(service) {
402 }
403
perform()404 void WorkflowWelcomePageAction::perform() {
405 SAFE_POINT(!service.isNull(), L10N::nullPointerError("Workflow Service"), );
406 service->sl_showDesignerWindow();
407 }
408
409 } // namespace U2
410