diff --git a/cromwell-api-rs/.gitignore b/cromwell-api-rs/.gitignore index bd6db2a..7b26a90 100644 --- a/cromwell-api-rs/.gitignore +++ b/cromwell-api-rs/.gitignore @@ -1,2 +1,5 @@ .DS_Store -/target \ No newline at end of file +/target +cromwell-8*.jar +cromwell-executions +cromwell-workflow-logs \ No newline at end of file diff --git a/cromwell-api-rs/examples/README.md b/cromwell-api-rs/examples/README.md new file mode 100644 index 0000000..6c4c4b0 --- /dev/null +++ b/cromwell-api-rs/examples/README.md @@ -0,0 +1,40 @@ +## How to test the workflow + +1. Start the cromwell server + +```bash +java -jar cromwell-82.jar server +``` + +2. Install the dependencies + +```bash +conda activate biomedgps +conda install -c conda-forge xxx + +# Example for installing the R package +R -e "devtools::install_git('file:///Users/jy006/Documents/Code/BioMiner/omix-insight-r')" +``` + +3. Run the workflow + +```bash +cargo run --example hello_world +``` + +## How to deploy the workflow + +1. Rsync the workflow to the server + +```bash +rsync -avP ./cromwell-api-rs/examples/boxplot/ root@drugs.3steps.cn:/data/biomedgps/cromwell/workflows/boxplot-v0.1.0 +``` + +2. Update the dependencies + +```bash +# Login to the server + +# Update the dependencies +/opt/miniconda3/envs/biomedgps/bin/R -e "devtools::install_git('file:///data/biomedgps/cromwell/workflows/boxplot-v0.1.0')" +``` diff --git a/cromwell-api-rs/examples/boxplot/README.md b/cromwell-api-rs/examples/boxplot/README.md index 4f26ca8..6f47022 100644 --- a/cromwell-api-rs/examples/boxplot/README.md +++ b/cromwell-api-rs/examples/boxplot/README.md @@ -6,7 +6,7 @@ Generate boxplot for selected genes / proteins across groups. - `exp_file`: Expression matrix file. - `sample_info_file`: Sample information file. -- `which_entrez_ids`: Which entrez ids to select. +- `which_ids`: Which ids to select. - `which_gene_symbols`: Which gene symbols to select. - `which_groups`: Which groups to select. - `output_file`: Output file. diff --git a/cromwell-api-rs/examples/boxplot/boxplot.R b/cromwell-api-rs/examples/boxplot/boxplot.R index b26d491..5849b09 100644 --- a/cromwell-api-rs/examples/boxplot/boxplot.R +++ b/cromwell-api-rs/examples/boxplot/boxplot.R @@ -15,15 +15,15 @@ library(OmixInsightR) print("Running OmixInsightR for boxplot") print("Querying data from expression matrix and sample information") -which_entrez_ids <- unlist(strsplit(args_json$which_entrez_ids, ",")) +which_ids <- unlist(strsplit(args_json$which_ids, ",")) which_gene_symbols <- unlist(strsplit(args_json$which_gene_symbols, ",")) which_groups <- unlist(strsplit(args_json$which_groups, ",")) -print(paste("Querying data from expression matrix and sample information:", args_json$exp_file, args_json$sample_info_file, which_entrez_ids, which_gene_symbols, which_groups)) +print(paste("Querying data from expression matrix and sample information:", args_json$exp_file, args_json$sample_info_file, which_ids, which_gene_symbols, which_groups)) d <- query_data( exp_file = args_json$exp_file, sample_info_file = args_json$sample_info_file, - which_entrez_ids = which_entrez_ids, + which_ids = which_ids, which_gene_symbols = which_gene_symbols, which_groups = which_groups ) diff --git a/cromwell-api-rs/examples/boxplot/inputs.json b/cromwell-api-rs/examples/boxplot/inputs.json index c9340f6..332c1f4 100644 --- a/cromwell-api-rs/examples/boxplot/inputs.json +++ b/cromwell-api-rs/examples/boxplot/inputs.json @@ -1,7 +1,7 @@ { "boxplot.boxplot_task.exp_file": "/Users/jy006/Documents/Code/BioMiner/omix-insight-r/examples/gene_expression.tsv", "boxplot.boxplot_task.sample_info_file": "/Users/jy006/Documents/Code/BioMiner/omix-insight-r/examples/sample_info.tsv", - "boxplot.boxplot_task.which_entrez_ids": [], + "boxplot.boxplot_task.which_ids": [], "boxplot.boxplot_task.which_gene_symbols": ["BDNF", "TP53"], "boxplot.boxplot_task.which_groups": ["Female_MECFS", "Female_Control"], "boxplot.boxplot_task.method": "t.test", diff --git a/cromwell-api-rs/examples/boxplot/workflow.wdl b/cromwell-api-rs/examples/boxplot/workflow.wdl index b747c25..9e49ff6 100644 --- a/cromwell-api-rs/examples/boxplot/workflow.wdl +++ b/cromwell-api-rs/examples/boxplot/workflow.wdl @@ -9,7 +9,7 @@ task boxplot_task { input { String exp_file String sample_info_file - Array[String] which_entrez_ids + Array[String] which_ids Array[String] which_gene_symbols Array[String] which_groups String method @@ -23,7 +23,7 @@ task boxplot_task { echo "Generating a args file, named args.json" echo "exp_file: ~{exp_file}" echo "sample_info_file: ~{sample_info_file}" - echo "which_entrez_ids: ~{sep=", " which_entrez_ids}" + echo "which_ids: ~{sep=", " which_ids}" echo "which_gene_symbols: ~{sep=", " which_gene_symbols}" echo "which_groups: ~{sep=", " which_groups}" echo "method: ~{method}" @@ -36,7 +36,7 @@ task boxplot_task { { "exp_file": "~{exp_file}", "sample_info_file": "~{sample_info_file}", - "which_entrez_ids": "~{sep=',' which_entrez_ids}", + "which_ids": "~{sep=',' which_ids}", "which_gene_symbols": "~{sep=',' which_gene_symbols}", "which_groups": "~{sep=',' which_groups}", "method": "~{method}", diff --git a/studio/src/StatEngine/components/ArgumentForm/index.tsx b/studio/src/StatEngine/components/ArgumentForm/index.tsx index 159b2d4..6e9c2b1 100644 --- a/studio/src/StatEngine/components/ArgumentForm/index.tsx +++ b/studio/src/StatEngine/components/ArgumentForm/index.tsx @@ -18,13 +18,80 @@ type DataItem = { const datasets = [ { datasetName: "GSE251790_Female_CSF", + datasetDescription: "[Protein] Deep phenotyping of Post-infectious Myalgic Encephalomyelitis/Chronic Fatigue Syndrome", fieldValue: { - exp_file: "/data/biomedgps/cromwell/data/gene_expression.tsv", - sample_info_file: "/data/biomedgps/cromwell/data/sample_info.tsv", + exp_file: "/data/biomedgps/cromwell/data/GSE251790_Female_CSF/expression_data.tsv", + sample_info_file: "/data/biomedgps/cromwell/data/GSE251790_Female_CSF/sample_info.tsv", }, fieldValueEnum: { which_groups: ["Female_MECFS", "Female_Control"] } + }, + { + datasetName: "GSE245661_Female_Muscle", + datasetDescription: "[RNA-seq] Deep phenotyping of Post-infectious Myalgic Encephalomyelitis/Chronic Fatigue Syndrome", + fieldValue: { + exp_file: "/data/biomedgps/cromwell/data/GSE245661_Female_Muscle/expression_data.tsv", + sample_info_file: "/data/biomedgps/cromwell/data/GSE245661_Female_Muscle/sample_info.tsv", + }, + fieldValueEnum: { + which_groups: ["HV", "MECFS"] + } + }, + { + datasetName: "GSE254030_Female_Plasma", + datasetDescription: "[Protein] Deep phenotyping of Post-infectious Myalgic Encephalomyelitis/Chronic Fatigue Syndrome", + fieldValue: { + exp_file: "/data/biomedgps/cromwell/data/GSE254030_Female_Plasma/expression_data.tsv", + sample_info_file: "/data/biomedgps/cromwell/data/GSE254030_Female_Plasma/sample_info.tsv", + }, + fieldValueEnum: { + which_groups: ["Female_MECFS", "Female_Control"] + } + }, + { + datasetName: "GSE254030_Full_Plasma", + datasetDescription: "[Protein] Deep phenotyping of Post-infectious Myalgic Encephalomyelitis/Chronic Fatigue Syndrome", + fieldValue: { + exp_file: "/data/biomedgps/cromwell/data/GSE254030_Full_Plasma/expression_data.tsv", + sample_info_file: "/data/biomedgps/cromwell/data/GSE254030_Full_Plasma/sample_info.tsv", + }, + fieldValueEnum: { + which_groups: ["MECFS", "Control"] + } + }, + { + datasetName: "GSE254030_Male_Plasma", + datasetDescription: "[Protein] Deep phenotyping of Post-infectious Myalgic Encephalomyelitis/Chronic Fatigue Syndrome", + fieldValue: { + exp_file: "/data/biomedgps/cromwell/data/GSE254030_Male_Plasma/expression_data.tsv", + sample_info_file: "/data/biomedgps/cromwell/data/GSE254030_Male_Plasma/sample_info.tsv", + }, + fieldValueEnum: { + which_groups: ["Male_MECFS", "Male_Control"] + } + }, + { + datasetName: "MaureenHanson_Female_Plasma", + datasetDescription: "[Protein] In-Depth Analysis of the Plasma Proteome in ME/CFS Exposes Disrupted Ephrin-Eph and Immune System Signaling", + fieldValue: { + exp_file: "/data/biomedgps/cromwell/data/MaureenHanson_Female_Plasma/expression_data.tsv", + sample_info_file: "/data/biomedgps/cromwell/data/MaureenHanson_Female_Plasma/sample_info.tsv", + }, + fieldValueEnum: { + which_groups: ["MECFS", "Control"] + } + }, + { + datasetName: "Zenodo7781290_Bacterial_Viral", + datasetDescription: "A multi-platform approach to identify a blood-based host protein signature for distinguishing between bacterial and viral infections in febrile children (PERFORM): a multi-cohort machine learning study", + fieldValue: { + exp_file: "/data/biomedgps/cromwell/data/Zenodo7781290_Bacterial_Viral/expression_data.tsv", + sample_info_file: "/data/biomedgps/cromwell/data/Zenodo7781290_Bacterial_Viral/sample_info.tsv", + }, + fieldValueEnum: { + which_groups: ["DefiniteBacterial", "DefiniteViral", "HealthyControl"] + } } ] @@ -133,8 +200,10 @@ const ArgumentForm: React.FC = (props) => { }) }}> {datasets.map((dataset) => ( - - {dataset.datasetName} + + {dataset.datasetName} +
+ {dataset.datasetDescription}
))} diff --git a/studio/src/StatEngine/components/ResultPanel/index.tsx b/studio/src/StatEngine/components/ResultPanel/index.tsx index eedb98b..def50f0 100644 --- a/studio/src/StatEngine/components/ResultPanel/index.tsx +++ b/studio/src/StatEngine/components/ResultPanel/index.tsx @@ -24,6 +24,7 @@ import type { ChartResult } from '../WorkflowList/data'; import type { PlotlyChart } from 'biominer-components/dist/PlotlyViewer/data'; import { fetchFileByFileName } from '../../../services/swagger/KnowledgeGraph'; import type { Workflow, TaskHistory, FileMeta } from '../WorkflowList/data'; +import { formatDuration } from './util'; // AG Grid theme import 'ag-grid-enterprise'; @@ -207,23 +208,7 @@ const ResultPanel: React.FC = (props) => { duration = finishedTime - startedTime; } - const durationInSeconds = duration / 1000; - - if (durationInSeconds <= 0) { - return '0s'; - } - - if (durationInSeconds < 60) { - return `${Math.round(durationInSeconds)}s`; - } else if (durationInSeconds < 3600) { - const minutes = Math.floor(durationInSeconds / 60); - const seconds = Math.round(durationInSeconds % 60); - return seconds > 0 ? `${minutes}min ${seconds}s` : `${minutes}min`; - } else { - const hours = Math.floor(durationInSeconds / 3600); - const minutes = Math.floor((durationInSeconds % 3600) / 60); - return minutes > 0 ? `${hours}h ${minutes}min` : `${hours}h`; - } + return formatDuration(duration); }; useEffect(() => { diff --git a/studio/src/StatEngine/components/ResultPanel/util.ts b/studio/src/StatEngine/components/ResultPanel/util.ts new file mode 100644 index 0000000..f20983b --- /dev/null +++ b/studio/src/StatEngine/components/ResultPanel/util.ts @@ -0,0 +1,19 @@ +export const formatDuration = (duration: number) => { + const durationInSeconds = duration / 1000; + + if (durationInSeconds <= 0) { + return '0s'; + } + + if (durationInSeconds < 60) { + return `${Math.round(durationInSeconds)}s`; + } else if (durationInSeconds < 3600) { + const minutes = Math.floor(durationInSeconds / 60); + const seconds = Math.round(durationInSeconds % 60); + return seconds > 0 ? `${minutes}min ${seconds}s` : `${minutes}min`; + } else { + const hours = Math.floor(durationInSeconds / 3600); + const minutes = Math.floor((durationInSeconds % 3600) / 60); + return minutes > 0 ? `${hours}h ${minutes}min` : `${hours}h`; + } +}; diff --git a/studio/src/pages/OmicsData/TaskHistory.tsx b/studio/src/pages/OmicsData/TaskHistory.tsx index 0eb5778..26776bb 100644 --- a/studio/src/pages/OmicsData/TaskHistory.tsx +++ b/studio/src/pages/OmicsData/TaskHistory.tsx @@ -3,6 +3,7 @@ import { Table, Row, Tag, Space, message, Popover, Button, Input } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { TaskHistory, TaskHistoryTableData } from '../../StatEngine/components/WorkflowList/data'; import { fetchTasks, deleteTask } from '@/services/swagger/KnowledgeGraph'; +import { formatDuration } from '../../StatEngine/components/ResultPanel/util'; type TaskHistoryTableProps = { page?: number; @@ -35,7 +36,7 @@ const TaskHistoryTable: React.FC = forwardRef((props, ref align: 'center', dataIndex: 'task_name', fixed: 'left', - width: 150, + width: 200, }, { title: 'Description', @@ -90,6 +91,19 @@ const TaskHistoryTable: React.FC = forwardRef((props, ref }, width: 200, }, + { + title: 'Duration', + key: 'duration', + align: 'center', + width: 100, + render: (text, record) => { + const finishedTime = record.finished_time ? new Date(record.finished_time).getTime() : new Date().getTime(); + const submittedTime = new Date(record.submitted_time).getTime(); + const duration = finishedTime - submittedTime; + + return formatDuration(duration); + }, + }, { title: 'Status', dataIndex: 'status',