Skip to content

Commit 16d19f6

Browse files
Merge 24.12 to develop
2 parents d29daa1 + ceaa623 commit 16d19f6

File tree

9 files changed

+359
-98
lines changed

9 files changed

+359
-98
lines changed

nextflow/build.gradle

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,10 @@ plugins {
66

77
dependencies {
88
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: BuildUtils.getPlatformModuleProjectPath(project.gradle, "pipeline"), depProjectConfig: "published", depExtension: "module")
9-
}
109

10+
compileOnly "org.projectlombok:lombok:${lombokVersion}"
11+
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
12+
13+
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: BuildUtils.getPlatformModuleProjectPath(project.gradle, "experiment"), depProjectConfig: "published", depExtension: "module")
14+
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: BuildUtils.getPlatformModuleProjectPath(project.gradle, "pipeline"), depProjectConfig: "published", depExtension: "module")
15+
}

nextflow/src/org/labkey/nextflow/NextFlowController.java

Lines changed: 103 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package org.labkey.nextflow;
22

3+
import lombok.Getter;
4+
import lombok.Setter;
35
import org.apache.commons.lang3.StringUtils;
46
import org.apache.logging.log4j.Logger;
57
import org.labkey.api.action.ApiResponse;
68
import org.labkey.api.action.ApiSimpleResponse;
79
import org.labkey.api.action.FormViewAction;
810
import org.labkey.api.action.MutatingApiAction;
9-
import org.labkey.api.action.SimpleViewAction;
1011
import org.labkey.api.action.SpringActionController;
1112
import org.labkey.api.admin.AdminUrls;
1213
import org.labkey.api.data.PropertyManager;
@@ -15,17 +16,21 @@
1516
import org.labkey.api.pipeline.PipelineJob;
1617
import org.labkey.api.pipeline.PipelineService;
1718
import org.labkey.api.pipeline.PipelineStatusUrls;
19+
import org.labkey.api.pipeline.browse.PipelinePathForm;
1820
import org.labkey.api.security.AdminConsoleAction;
1921
import org.labkey.api.security.RequiresPermission;
2022
import org.labkey.api.security.permissions.AdminOperationsPermission;
21-
import org.labkey.api.security.permissions.InsertPermission;
2223
import org.labkey.api.security.permissions.ReadPermission;
2324
import org.labkey.api.security.permissions.SiteAdminPermission;
2425
import org.labkey.api.util.Button;
26+
import org.labkey.api.util.DOM;
27+
import org.labkey.api.util.FileUtil;
28+
import org.labkey.api.util.HtmlString;
2529
import org.labkey.api.util.PageFlowUtil;
30+
import org.labkey.api.util.Path;
2631
import org.labkey.api.util.URLHelper;
32+
import org.labkey.api.util.element.Select;
2733
import org.labkey.api.util.logging.LogHelper;
28-
import org.labkey.api.view.ActionURL;
2934
import org.labkey.api.view.HtmlView;
3035
import org.labkey.api.view.JspView;
3136
import org.labkey.api.view.NavTree;
@@ -36,14 +41,20 @@
3641
import org.springframework.validation.Errors;
3742
import org.springframework.web.servlet.ModelAndView;
3843

44+
import java.io.File;
45+
import java.util.Arrays;
46+
import java.util.List;
47+
3948
import static org.labkey.api.util.DOM.Attribute.checked;
49+
import static org.labkey.api.util.DOM.Attribute.hidden;
4050
import static org.labkey.api.util.DOM.Attribute.method;
4151
import static org.labkey.api.util.DOM.Attribute.name;
4252
import static org.labkey.api.util.DOM.Attribute.type;
4353
import static org.labkey.api.util.DOM.Attribute.value;
4454
import static org.labkey.api.util.DOM.DIV;
4555
import static org.labkey.api.util.DOM.INPUT;
4656
import static org.labkey.api.util.DOM.LK.FORM;
57+
import static org.labkey.api.util.DOM.UL;
4758
import static org.labkey.api.util.DOM.at;
4859
import static org.labkey.nextflow.NextFlowManager.NEXTFLOW_CONFIG;
4960

@@ -59,32 +70,6 @@ public NextFlowController()
5970
setActionResolver(_actionResolver);
6071
}
6172

62-
@RequiresPermission(ReadPermission.class)
63-
public static class BeginAction extends SimpleViewAction<Object>
64-
{
65-
@Override
66-
public ModelAndView getView(Object o, BindException errors)
67-
{
68-
boolean enabled = NextFlowManager.get().isEnabled(getContainer());
69-
return new HtmlView("NextFlow",
70-
DIV(
71-
DIV("NextFlow integration is " + (enabled ? "enabled" : "disabled") + " in this " + (getContainer().isProject() ? "project" : "folder") + "."),
72-
DIV(
73-
getContainer().hasPermission(getUser(), SiteAdminPermission.class) ?
74-
new Button.ButtonBuilder("Enable/Disable").href(new ActionURL(NextFlowEnableAction.class, getContainer())).build() : null,
75-
" ",
76-
enabled && getContainer().hasPermission(getUser(), InsertPermission.class) ?
77-
new Button.ButtonBuilder("Run NextFlow Analysis").href(new ActionURL(NextFlowRunAction.class, getContainer())).build() : null)));
78-
}
79-
80-
@Override
81-
public void addNavTrail(NavTree root)
82-
{
83-
root.addChild("NextFlow");
84-
}
85-
}
86-
87-
8873
@RequiresPermission(SiteAdminPermission.class)
8974
public static class DeleteNextFlowConfigurationAction extends MutatingApiAction<Object>
9075
{
@@ -196,8 +181,8 @@ public void setEnabled(Boolean enabled)
196181
}
197182
}
198183

199-
@RequiresPermission(SiteAdminPermission.class)
200-
public static class NextFlowEnableAction extends FormViewAction<EnabledForm>
184+
@RequiresPermission(ReadPermission.class)
185+
public static class BeginAction extends FormViewAction<EnabledForm>
201186
{
202187
@Override
203188
public void validateCommand(EnabledForm target, Errors errors)
@@ -208,21 +193,30 @@ public void validateCommand(EnabledForm target, Errors errors)
208193
@Override
209194
public ModelAndView getView(EnabledForm form, boolean reshow, BindException errors)
210195
{
211-
Boolean status = NextFlowManager.get().getEnabledState(getContainer());
212-
boolean inheritedStatus = NextFlowManager.get().isEnabled(getContainer().getParent());
213-
214-
return new HtmlView("Enable/Disable NextFlow",
215-
FORM(at(method, "POST"),
216-
DIV(INPUT(at(type, "radio", name, "enabled", value, Boolean.TRUE.toString(), (status == Boolean.TRUE ? checked : null), null)),
217-
"Enabled"),
218-
DIV(INPUT(at(type, "radio", name, "enabled", value, Boolean.FALSE.toString(), (status == Boolean.FALSE ? checked : null), null)),
219-
"Disabled"),
220-
DIV(INPUT(at(type, "radio", name, "enabled", value, "", (status == null ? checked : null), null)),
221-
getContainer().isRoot() ?
222-
"Unset" :
223-
"Inherited from " + getContainer().getParent().getPath() + " (currently " + (inheritedStatus ? "enabled" : "disabled") + ")"),
224-
new Button.ButtonBuilder("Save").submit(true).build(), " ",
225-
new Button.ButtonBuilder("Cancel").href(getContainer().getStartURL(getUser())).build()));
196+
if (getUser().hasSiteAdminPermission())
197+
{
198+
Boolean status = NextFlowManager.get().getEnabledState(getContainer());
199+
boolean inheritedStatus = NextFlowManager.get().isEnabled(getContainer().getParent());
200+
201+
return new HtmlView("Enable or Disable NextFlow",
202+
FORM(at(method, "POST"),
203+
DIV(INPUT(at(type, "radio", name, "enabled", value, Boolean.TRUE.toString(), (status == Boolean.TRUE ? checked : null), null)),
204+
"Enabled"),
205+
DIV(INPUT(at(type, "radio", name, "enabled", value, Boolean.FALSE.toString(), (status == Boolean.FALSE ? checked : null), null)),
206+
"Disabled"),
207+
DIV(INPUT(at(type, "radio", name, "enabled", value, "", (status == null ? checked : null), null)),
208+
getContainer().isRoot() ?
209+
"Unset" :
210+
"Inherited from " + getContainer().getParent().getPath() + " (currently " + (inheritedStatus ? "enabled" : "disabled") + ")"),
211+
new Button.ButtonBuilder("Save").submit(true).build(), " ",
212+
new Button.ButtonBuilder("Cancel").href(getContainer().getStartURL(getUser())).build()));
213+
}
214+
else
215+
{
216+
return new HtmlView("NextFlow Integration Status",
217+
DIV("NextFlow integration is " + (NextFlowManager.get().isEnabled(getContainer()) ? "enabled" : "disabled") + " in this " + (getContainer().isProject() ? "project" : "folder") + ".")
218+
);
219+
}
226220
}
227221

228222
@Override
@@ -235,7 +229,7 @@ public boolean handlePost(EnabledForm form, BindException errors)
235229
@Override
236230
public void addNavTrail(NavTree root)
237231
{
238-
root.addChild("Enable/Disable NextFlow");
232+
root.addChild("NextFlow Integration Status");
239233
}
240234

241235
@Override
@@ -245,11 +239,18 @@ public URLHelper getSuccessURL(EnabledForm o)
245239
}
246240
}
247241

242+
@Getter @Setter
243+
public static class AnalyzeForm extends PipelinePathForm
244+
{
245+
private boolean launch = false;
246+
private String configFile;
247+
}
248+
248249
@RequiresPermission(AdminOperationsPermission.class)
249-
public class NextFlowRunAction extends FormViewAction<Object>
250+
public class NextFlowRunAction extends FormViewAction<AnalyzeForm>
250251
{
251252
@Override
252-
public void validateCommand(Object o, Errors errors)
253+
public void validateCommand(AnalyzeForm o, Errors errors)
253254
{
254255
if (!NextFlowManager.get().isEnabled(getContainer()))
255256
{
@@ -258,26 +259,69 @@ public void validateCommand(Object o, Errors errors)
258259
}
259260

260261
@Override
261-
public ModelAndView getView(Object o, boolean b, BindException errors)
262+
public ModelAndView getView(AnalyzeForm o, boolean b, BindException errors)
262263
{
263-
return new HtmlView("NextFlow Runner", DIV("Run NextFlow Pipeline",
264-
FORM(at(method, "POST"),
265-
new Button.ButtonBuilder("Start NextFlow").submit(true).build())));
264+
NextFlowConfiguration config = NextFlowManager.get().getConfiguration();
265+
if (config.getNextFlowConfigFilePath() != null)
266+
{
267+
File configDir = new File(config.getNextFlowConfigFilePath());
268+
if (configDir.isDirectory())
269+
{
270+
File[] files = configDir.listFiles();
271+
if (files != null && files.length > 0)
272+
{
273+
List<File> configFiles = Arrays.asList(files);
274+
return new HtmlView("NextFlow Runner", DIV(
275+
FORM(at(method, "POST"),
276+
INPUT(at(hidden, true, name, "launch", value, true)),
277+
Arrays.stream(o.getFile()).map(f -> INPUT(at(hidden, true, name, "file", value, f))).toList(),
278+
"Files: ",
279+
UL(Arrays.stream(o.getFile()).map(DOM::LI)),
280+
"Config: ",
281+
new Select.SelectBuilder().name("configFile").addOptions(configFiles.stream().filter(f -> f.isFile() && f.getName().toLowerCase().endsWith(".config")).map(File::getName).sorted(String.CASE_INSENSITIVE_ORDER).toList()).build(),
282+
new Button.ButtonBuilder("Start NextFlow").submit(true).build())));
283+
}
284+
}
285+
}
286+
return new HtmlView(HtmlString.of("Couldn't find NextFlow config file(s)"));
266287
}
267288

268289
@Override
269-
public boolean handlePost(Object o, BindException errors) throws Exception
290+
public boolean handlePost(AnalyzeForm form, BindException errors) throws Exception
270291
{
271-
ViewBackgroundInfo info = getViewBackgroundInfo();
272-
PipeRoot root = PipelineService.get().findPipelineRoot(info.getContainer());
273-
PipelineJob job = new NextFlowPipelineJob(info, root);
274-
PipelineService.get().queueJob(job);
292+
if (!form.isLaunch())
293+
{
294+
return false;
295+
}
296+
297+
NextFlowConfiguration config = NextFlowManager.get().getConfiguration();
298+
File configDir = new File(config.getNextFlowConfigFilePath());
299+
File configFile = FileUtil.appendPath(configDir, Path.parse(form.getConfigFile()));
300+
if (!configFile.exists())
301+
{
302+
errors.reject(ERROR_MSG, "Config file does not exist");
303+
}
304+
else
305+
{
306+
List<File> inputFiles = form.getValidatedFiles(getContainer());
307+
if (inputFiles.isEmpty())
308+
{
309+
errors.reject(ERROR_MSG, "No input files");
310+
}
311+
else
312+
{
313+
ViewBackgroundInfo info = getViewBackgroundInfo();
314+
PipeRoot root = PipelineService.get().findPipelineRoot(info.getContainer());
315+
PipelineJob job = NextFlowPipelineJob.create(info, root, configFile.toPath(), inputFiles.stream().map(File::toPath).toList());
316+
PipelineService.get().queueJob(job);
317+
}
318+
}
275319

276320
return !errors.hasErrors();
277321
}
278322

279323
@Override
280-
public URLHelper getSuccessURL(Object o)
324+
public URLHelper getSuccessURL(AnalyzeForm o)
281325
{
282326
return PageFlowUtil.urlProvider(PipelineStatusUrls.class).urlBegin(getContainer());
283327
}

nextflow/src/org/labkey/nextflow/NextFlowManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import org.labkey.api.data.PropertyManager;
88
import org.springframework.validation.BindException;
99

10+
import java.nio.file.Files;
11+
import java.nio.file.Path;
12+
import java.nio.file.Paths;
1013
import java.util.HashMap;
1114
import java.util.Map;
1215

@@ -44,6 +47,12 @@ private void checkArgs(NextFlowConfiguration config, BindException errors)
4447
if (StringUtils.isEmpty(config.getNextFlowConfigFilePath()))
4548
errors.rejectValue("nextFlowConfigFilePath", ERROR_MSG, "NextFlow config file path is required");
4649

50+
Path configPath = Paths.get(config.getNextFlowConfigFilePath());
51+
if (!Files.isDirectory(configPath))
52+
{
53+
errors.rejectValue("nextFlowConfigFilePath", ERROR_MSG, "NextFlow config file path must be a directory");
54+
}
55+
4756
// Not yet used
4857
// if (StringUtils.isEmpty(config.getAccountName()))
4958
// errors.rejectValue("accountName", ERROR_MSG, "AWS account name is required");

nextflow/src/org/labkey/nextflow/NextFlowModule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext)
2121
{
2222
ActionURL adminUrl = new ActionURL(NextFlowController.NextFlowConfigurationAction.class, ContainerManager.getRoot());
2323
AdminConsole.addLink(AdminConsole.SettingsLinkType.Configuration, "NextFlow Configuration", adminUrl, AdminPermission.class);
24+
25+
PipelineService.get().registerPipelineProvider(new NextFlowPipelineProvider(this));
2426
}
2527

2628
@Override
2729
protected void init()
2830
{
2931
addController(NextFlowController.NAME, NextFlowController.class);
30-
31-
PipelineService.get().registerPipelineProvider(new NextFlowPipelineProvider(this));
3232
}
3333

3434
@Override

0 commit comments

Comments
 (0)