biopipen 0.17.7__py3-none-any.whl → 0.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of biopipen might be problematic. Click here for more details.

biopipen/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.17.7"
1
+ __version__ = "0.18.0"
biopipen/core/filters.py CHANGED
@@ -107,6 +107,8 @@ def r(
107
107
  ignoreintkey: bool = True,
108
108
  todot: str = None,
109
109
  sortkeys: bool = False,
110
+ skip: int = 0,
111
+ _i: int = 0,
110
112
  ) -> str:
111
113
  """Convert a python object into R repr
112
114
 
@@ -117,6 +119,7 @@ def r(
117
119
  >>> {"a": 1, "b": 2} -> list(a = 1, b = 2)
118
120
 
119
121
  Args:
122
+ obj: The object to convert
120
123
  ignoreintkey: When keys of a dict are integers, whether we should
121
124
  ignore them. For example, when `True`, `{1: 1, 2: 2}` will be
122
125
  translated into `"list(1, 2)"`, but `"list(`1` = 1, `2` = 2)"`
@@ -130,6 +133,10 @@ def r(
130
133
  But sometimes, you want to keep orginal order, for example,
131
134
  arguments passed the `dplyr::mutate` function. Because the later
132
135
  arguments can refer to the earlier ones.
136
+ skip: Levels to skip for `todot`. For example, `skip=1` will skip
137
+ the first level of the keys. When `todot` is `"-"`, `skip=1` will
138
+ convert `{"a-b": {"c-d": 1}}` to ``list(`a-b` = list(`c.d` = 1))``
139
+ _i: Current level of the keys. Used internally
133
140
 
134
141
  Returns:
135
142
  Then converted string representation of the object
@@ -165,31 +172,37 @@ def r(
165
172
  wrapper = "c"
166
173
  return "{}({})".format(
167
174
  wrapper,
168
- ",".join([r(i, ignoreintkey, todot, sortkeys) for i in obj]),
175
+ ", ".join(
176
+ [r(i, ignoreintkey, todot, sortkeys, skip, _i + 1) for i in obj]
177
+ ),
169
178
  )
170
179
  if isinstance(obj, dict):
171
180
  # list allow repeated names
172
- return "list({})".format(
173
- ",".join(
174
- [
175
- "`{0}`={1}".format(k, r(v, ignoreintkey, todot, sortkeys))
176
- if isinstance(k, int) and not ignoreintkey
177
- else r(v, ignoreintkey, todot, sortkeys)
178
- if isinstance(k, int) and ignoreintkey
179
- else "`{0}`={1}".format(
180
- (
181
- str(k)
182
- if not todot
183
- else str(k).replace(todot, ".")
184
- ).split("#")[0],
185
- r(v, ignoreintkey, todot, sortkeys),
186
- )
187
- for k, v in (
188
- sorted(obj.items()) if sortkeys else obj.items()
189
- )
190
- ]
191
- )
192
- )
181
+ items = []
182
+ keys = obj.keys()
183
+ if sortkeys:
184
+ keys = sorted(keys)
185
+ for k in keys:
186
+ v = obj[k]
187
+ if isinstance(k, int) and not ignoreintkey:
188
+ item = (
189
+ f"`{k}`={r(v, ignoreintkey, todot, sortkeys, skip, _i + 1)}"
190
+ )
191
+ elif isinstance(k, int) and ignoreintkey:
192
+ item = r(v, ignoreintkey, todot, sortkeys, skip, _i + 1)
193
+ else:
194
+ key = str(k)
195
+ if todot and _i >= skip:
196
+ key = key.replace(todot, ".")
197
+ item = (
198
+ f"`{key}`="
199
+ f"{r(v, ignoreintkey, todot, sortkeys, skip, _i + 1)}"
200
+ )
201
+ items.append(item)
202
+
203
+ return f"list({', '.join(items)})"
204
+
193
205
  if isinstance(obj, Namespace):
194
- return r(vars(obj), ignoreintkey, todot, sortkeys)
206
+ return r(vars(obj), ignoreintkey, todot, sortkeys, skip, _i)
207
+
195
208
  return repr(obj)
biopipen/ns/delim.py CHANGED
@@ -82,7 +82,7 @@ class SampleInfo(Proc):
82
82
  - on: The column name in the data for the stats.
83
83
  Default is `Sample`.
84
84
  If there are duplicated values in `on`, and you want to do stats
85
- on the unique values, you can use `distinct:` prefix.
85
+ on the unique values, you can use `distinct:`/`unique:` prefix.
86
86
  For example, `distinct:Patient` will keep only the first
87
87
  duplicated value in `Patient` and do stats on the unique values.
88
88
  - group: The column name in the data for the group ids.
biopipen/ns/scrna.py CHANGED
@@ -253,50 +253,88 @@ class SeuratClusterStats(Proc):
253
253
  outdir: The output directory
254
254
 
255
255
  Envs:
256
+ stats_defaults (ns): The default parameters for `stats`.
257
+ The parameters from the cases can overwrite the default parameters.
258
+ - frac (flag): Whether to output the fraction of cells instead of number.
259
+ - pie (flag): Also output a pie chart?
260
+ - table (flag): Whether to output a table (in tab-delimited format) and in the report.
261
+ - ident: The column name in metadata to use as the identity.
262
+ - group-by: The column name in metadata to group the cells.
263
+ Does NOT support for pie charts.
264
+ - split-by: The column name in metadata to split the cells into
265
+ different plots.
266
+ - subset: An expression to subset the cells, will be passed to
267
+ `dplyr::filter()` on metadata.
268
+ - devpars (ns): The device parameters for the plots.
269
+ - res (type=int): The resolution of the plots.
270
+ - height (type=int): The height of the plots.
271
+ - width (type=int): The width of the plots.
256
272
  stats (type=json): The number/fraction of cells to plot.
257
- * `nCells_*` - Number of cells for each cluster.
258
- You can specify `by` to group the cells by a metadata column,
259
- and `devpars` to specify the device parameters for the plot.
260
- You can also specify `filter` to filter the cells under certain
261
- conditions using metadata columns.
262
- * `fracCells_*` - Fraction of cells for each cluster.
263
- Similar to `nCells_*`, but the fraction is calculated
264
- instead of the absolute number.
265
- features (type=json): The plots for features, include gene expressions,
266
- and columns from metadata.
267
- * `features` - The set of features (separated by comma) for the plots,
268
- unless `features` for those plots is specified.
269
- One could also specify a file with features (one per line).
270
- * `ridgeplots` - The ridge plots for the features.
271
- See [`Seurat::RidgePlot`](https://satijalab.org/seurat/reference/ridgeplot).
272
- * `vlnplots` - Violin plots for the features.
273
- See [`Seurat::VlnPlot`](https://satijalab.org/seurat/reference/vlnplot).
274
- You can have `boxplot` key to add `geom_boxplot()` to the violin plots.
275
- * `featureplots` - The feature plots for the features.
276
- See [`Seurat::FeaturePlot`](https://satijalab.org/seurat/reference/featureplot).
277
- * `dotplot` - Dot plots for the features.
278
- See [`Seurat::DotPlot`](https://satijalab.org/seurat/reference/dotplot).
279
- * `heatmap` - Heatmap for the features.
280
- See [`Seurat::DoHeatmap`](https://satijalab.org/seurat/reference/doheatmap).
281
- You can specify `average=True` to plot on the average of the expressions.
282
- * `table` - The table for the features, only gene expressions are supported.
283
- (supported keys: title, log2, subset and features).
284
- * All the above can have `devpars` to define the output figures
285
- and `plus` to add elements to the `ggplot` object.
286
- You can also have `subset` to subset the data.
287
- Multiple cases can be distinguished by `ridgeplots` and
288
- `ridgeplots_1`.
289
- If no `features` specified, will use `envs.features`. If you want to use
290
- the default variant features `VariantFeatures(srtobj)`, specify
291
- `features = 20` for the first 20. Or you can also specify the features
292
- directly to `features`.
273
+ Keys are the names of the plots and values are the dicts inherited from `env.stats_defaults`.
274
+ Here are some examples -
275
+ >>> {
276
+ >>> "nCells_All": {},
277
+ >>> "nCells_Sample": {"kind": "num", "group-by": "Sample"},
278
+ >>> "fracCells_Sample": {"kind": "frac", "group-by": "Sample"},
279
+ >>> }
280
+ features_defaults (ns): The default parameters for `features`.
281
+ - features: The features to plot.
282
+ It can be either a string with comma separated features, a list of features, a file path with `file://` prefix with features
283
+ (one per line), or an integer to use the top N features from `VariantFeatures(srtobj)`.
284
+ - ident: The column name in metadata to use as the identity.
285
+ - subset: An expression to subset the cells, will be passed to `tidyrseurat::filter()`.
286
+ - devpars (ns): The device parameters for the plots. Does not work for `table`.
287
+ - res (type=int): The resolution of the plots.
288
+ - height (type=int): The height of the plots.
289
+ - width (type=int): The width of the plots.
290
+ - plus: The extra elements to add to the `ggplot` object. Does not work for `table`.
291
+ - group-by: Group cells in different ways (for example, orig.ident). Works for `ridge`, `vln`, and `dot`.
292
+ It also works for `feature` as `shape.by` being passed to [`Seurat::FeaturePlot`](https://satijalab.org/seurat/reference/featureplot).
293
+ - split-by: The column name in metadata to split the cells into different plots.
294
+ It works for `vln`, `feature`, and `dot`.
295
+ - assay: The assay to use.
296
+ - slot: The slot to use.
297
+ - section: The section to put the plot in the report.
298
+ If not specified, the case title will be used.
299
+ - ncol: The number of columns for the plots.
300
+ - kind (choice): The kind of the plot or table.
301
+ - ridge: Use `Seurat::RidgePlot`.
302
+ - ridgeplot: Same as `ridge`.
303
+ - vln: Use `Seurat::VlnPlot`.
304
+ - vlnplot: Same as `vln`.
305
+ - violin: Same as `vln`.
306
+ - violinplot: Same as `vln`.
307
+ - feature: Use `Seurat::FeaturePlot`.
308
+ - featureplot: Same as `feature`.
309
+ - dot: Use `Seurat::DotPlot`.
310
+ - dotplot: Same as `dot`.
311
+ - heatmap: Use `Seurat::DoHeatmap`.
312
+ You can specify `average=True` to plot on the average of the expressions.
313
+ - table: The table for the features, only gene expressions are supported.
314
+ (supported keys: ident, subset, and features).
315
+ features (type=json): The plots for features, include gene expressions, and columns from metadata.
316
+ Keys are the titles of the cases and values are the dicts inherited from `env.features_defaults`. It can also have other parameters from
317
+ each Seurat function used by `kind`. Note that for argument name with `.`, you should use `-` instead.
318
+ dimplots_defaults (ns): The default parameters for `dimplots`.
319
+ - ident: The column name in metadata to use as the identity.
320
+ - group-by: Same as `ident`. How the points are colored.
321
+ - split-by: The column name in metadata to split the cells into different plots.
322
+ - shape-by: The column name in metadata to use as the shape.
323
+ - devpars (ns): The device parameters for the plots.
324
+ - res (type=int): The resolution of the plots.
325
+ - height (type=int): The height of the plots.
326
+ - width (type=int): The width of the plots.
327
+ - reduction (choice): Which dimensionality reduction to use.
328
+ - dim: Use `Seurat::DimPlot`.
329
+ First searches for `umap`, then `tsne`, then `pca`.
330
+ - auto: Same as `dim`
331
+ - umap: Use `Seurat::UMAPPlot`.
332
+ - tsne: Use `Seurat::TSNEPlot`.
333
+ - pca: Use `Seurat::PCAPlot`.
334
+ - <more>: See <https://satijalab.org/seurat/reference/dimplot>
293
335
  dimplots (type=json): The dimensional reduction plots.
294
- * `<case>` - The case to plot.
295
- Keys are the arguments for `Seurat::Dimplot()`, plus `devpars`
296
- for the plots. `devpars` is a dictionary with keys `res`,
297
- `height` and `width` to specify the resolution, height and
298
- width of the plot. The hyphen (`-`) in the keys will be replaced
299
- by `.`.
336
+ Keys are the titles of the plots and values are the dicts inherited from `env.dimplots_defaults`. It can also have other parameters from
337
+ [`Seurat::DimPlot`](https://satijalab.org/seurat/reference/dimplot).
300
338
 
301
339
  Requires:
302
340
  r-seurat:
@@ -306,17 +344,55 @@ class SeuratClusterStats(Proc):
306
344
  output = "outdir:dir:{{in.srtobj | stem}}.cluster_stats"
307
345
  lang = config.lang.rscript
308
346
  envs = {
347
+ "stats_defaults": {
348
+ "frac": False,
349
+ "pie": False,
350
+ "table": False,
351
+ "ident": "seurat_clusters",
352
+ "group-by": None,
353
+ "split-by": None,
354
+ "subset": None,
355
+ "devpars": {"res": 100, "height": 800, "width": 1000},
356
+ },
309
357
  "stats": {
310
- "nCells_All": {},
311
- "nCells_Sample": {"by": "Sample"},
312
- "fracCells_Sample": {"by": "Sample"},
358
+ "Number of cells in each cluster": {
359
+ "pie": True,
360
+ },
361
+ "Number of cells in each cluster by Sample": {
362
+ "group-by": "Sample",
363
+ "table": True,
364
+ "frac": True,
365
+ },
366
+ },
367
+ "features_defaults": {
368
+ "features": None,
369
+ "ident": "seurat_clusters",
370
+ "subset": None,
371
+ "devpars": {"res": 100},
372
+ "plus": None,
373
+ "group-by": None,
374
+ "split-by": None,
375
+ "assay": None,
376
+ "section": None,
377
+ "slot": None,
378
+ "kind": None,
379
+ "ncol": 2,
313
380
  },
314
381
  "features": {},
382
+ "dimplots_defaults": {
383
+ "ident": "seurat_clusters",
384
+ "group-by": None,
385
+ "split-by": None,
386
+ "shape-by": None,
387
+ "reduction": "dim",
388
+ "devpars": {"res": 100, "height": 800, "width": 1000},
389
+ },
315
390
  "dimplots": {
316
- "Ident": {
317
- "group-by": "ident",
318
- "devpars": {"res": 100, "height": 1000, "width": 1000},
319
- }
391
+ "Dimensional reduction plot": {
392
+ "label": True,
393
+ "label-box": True,
394
+ "repel": True,
395
+ },
320
396
  },
321
397
  }
322
398
  script = "file://../scripts/scrna/SeuratClusterStats.R"
@@ -691,6 +767,12 @@ class MarkersFinder(Proc):
691
767
  `p_val_adj`. For example, `"p_val_adj < 0.05 & abs(avg_log2FC) > 1"`
692
768
  to select markers with adjusted p-value < 0.05 and absolute log2
693
769
  fold change > 1.
770
+ volcano_genes (type=auto): The genes to label in the volcano plot if they are
771
+ significant markers.
772
+ If `True`, all significant markers will be labeled. If `False`, no
773
+ genes will be labeled. Otherwise, specify the genes to label.
774
+ It could be either a string with comma separated genes, or a list
775
+ of genes.
694
776
  section: The section name for the report.
695
777
  Worked only when `each` is not specified and `ident-2` is specified.
696
778
  Otherwise, the section name will be constructed from `each` and
@@ -728,6 +810,7 @@ class MarkersFinder(Proc):
728
810
  "KEGG_2021_Human",
729
811
  ],
730
812
  "sigmarkers": "p_val_adj < 0.05",
813
+ "volcano_genes": True,
731
814
  "cases": {},
732
815
  }
733
816
  order = 5
biopipen/ns/tcr.py CHANGED
@@ -1126,10 +1126,13 @@ class TESSA(Proc):
1126
1126
  within_sample (flag): Whether the TCR networks are constructed only
1127
1127
  within TCRs from the same sample/patient (True) or with all the
1128
1128
  TCRs in the meta data matrix (False).
1129
+ assay: Which assay to use to extract the expression matrix.
1130
+ Only works if `in.srtobj` is an RDS file of a Seurat object and
1131
+ `envs.reduction` is not provided.
1129
1132
  predefined_b (flag): Whether use the predefined `b` or not.
1130
1133
  Please check the paper of tessa for more details about the b vector.
1131
1134
  If True, the tessa will not update b in the MCMC iterations.
1132
- max_iter (int): The maximum number of iterations for MCMC.
1135
+ max_iter (type=int): The maximum number of iterations for MCMC.
1133
1136
  """
1134
1137
  input = "immdata:file,srtobj:file"
1135
1138
  output = """outfile:file:
@@ -1143,6 +1146,7 @@ class TESSA(Proc):
1143
1146
  envs = {
1144
1147
  "python": config.lang.python,
1145
1148
  "prefix": "{Sample}_",
1149
+ "assay": "RNA",
1146
1150
  "within_sample": False,
1147
1151
  "predefined_b": False,
1148
1152
  "max_iter": 1000,
@@ -25,10 +25,21 @@
25
25
  />
26
26
  {%- else -%}
27
27
  <h{{h+1}}>Markers</h{{h+1}}>
28
- <DataTable
29
- src={{ casedir | joinpaths: "markers.txt" | quote }}
30
- data={ {{ casedir | joinpaths: "markers.txt" | datatable: sep="\t", nrows=100 }} }
31
- />
28
+ <Tabs>
29
+ <Tab label="Markers" />
30
+ <Tab label="Volcano Plot" />
31
+ <svelte:fragment slot="content">
32
+ <TabContent>
33
+ <DataTable
34
+ src={{ casedir | joinpaths: "markers.txt" | quote }}
35
+ data={ {{ casedir | joinpaths: "markers.txt" | datatable: sep="\t", nrows=100 }} }
36
+ />
37
+ </TabContent>
38
+ <TabContent>
39
+ <Image src={{ casedir | joinpaths: "volcano.png" | quote }} />
40
+ </TabContent>
41
+ </svelte:fragment>
42
+ </Tabs>
32
43
 
33
44
  <h{{h+1}}>Enrichment analysis</h{{h+1}}>
34
45
  {{ enrichr_report(casedir) }}
@@ -49,10 +60,21 @@
49
60
  />
50
61
  {%- else -%}
51
62
  <h{{h+2}}>Markers</h{{h+2}}>
52
- <DataTable
53
- src={{ casedir | joinpaths: "markers.txt" | quote }}
54
- data={ {{ casedir | joinpaths: "markers.txt" | datatable: sep="\t", nrows=100 }} }
55
- />
63
+ <Tabs>
64
+ <Tab label="Markers" />
65
+ <Tab label="Volcano Plot" />
66
+ <svelte:fragment slot="content">
67
+ <TabContent>
68
+ <DataTable
69
+ src={{ casedir | joinpaths: "markers.txt" | quote }}
70
+ data={ {{ casedir | joinpaths: "markers.txt" | datatable: sep="\t", nrows=100 }} }
71
+ />
72
+ </TabContent>
73
+ <TabContent>
74
+ <Image src={{ casedir | joinpaths: "volcano.png" | quote }} />
75
+ </TabContent>
76
+ </svelte:fragment>
77
+ </Tabs>
56
78
 
57
79
  <h{{h+2}}>Enrichment analysis</h{{h+2}}>
58
80
  {{ enrichr_report(casedir) }}
@@ -6,118 +6,73 @@
6
6
  </script>
7
7
 
8
8
  {%- macro report_job(job, h=1) -%}
9
- {%- for statname in proc.envs.stats -%}
10
- {%- if statname.startswith("nCells") -%}
11
- {%- set num_or_frac = "Number" -%}
12
- {%- set rest_title = statname | replace: "nCells_", "" | replace: "_", " " -%}
13
- {%- else -%}
14
- {%- set num_or_frac = "Fraction" -%}
15
- {%- set rest_title = statname | replace: "fracCells_", "" | replace: "_", " " -%}
16
- {%- endif -%}
17
- {%- if rest_title == "All" or rest_title == "ALL" -%}
18
- {%- set rest_title = "" -%}
19
- {%- else -%}
20
- {%- set rest_title = "(" + rest_title + ")" -%}
21
- {%- endif -%}
22
-
23
- {%- set plotfile = job.out.outdir | joinpaths: "stats", statname + ".png" -%}
24
- {%- set tablefile = plotfile + ".txt" -%}
25
- <h{{h}}>{{num_or_frac}} of cells {{rest_title}}</h{{h}}>
26
- <Tabs>
27
- <Tab label="Plot" />
28
- <Tab label="Table" />
29
- <svelte:fragment slot="content">
30
- <TabContent>
31
- <Image src={{plotfile | quote}} />
32
- </TabContent>
33
- <TabContent>
34
- <DataTable src={{tablefile | quote}}
35
- data={ {{tablefile | datatable: sep="\t", nrows=100 }} } />
36
- </TabContent>
37
- </svelte:fragment>
38
- </Tabs>
39
- {%- endfor -%}
40
-
41
- {%- if job.out.outdir | glob: "features/table-*.tsv" -%}
42
- <h{{h}}>Feature matrix</h{{h}}>
43
- {%- set tabfiles = job.out.outdir | glob: "features/table-*.tsv" -%}
44
- {%- for tabfile in tabfiles -%}
45
- {%- set title = tabfile | append: ".title" | read | escape -%}
46
- {%- if not title.startswith("table-") or len(tabfiles) > 1 -%}
47
- <h{{h+1}}>{{ title | escape }}</h{{h+1}}>
48
- {%- endif -%}
49
- <p><DataTable src={{tabfile | quote}}
50
- data={ {{tabfile | datatable: sep="\t", nrows=100 }} } /></p>
51
- {%- endfor -%}
52
- {%- endif -%}
53
-
54
- {%- if job.out.outdir | glob: "features/ridgeplots-*.png" -%}
55
- <h{{h}}>Ridge plots of features</h{{h}}>
56
- {%- set figures = job.out.outdir | glob: "features/ridgeplots-*.png" -%}
57
- {%- for figure in figures -%}
58
- {%- set title = figure | append: ".title" | read | escape -%}
59
- {%- if not title.startswith("ridgeplots-") or len(figures) > 1 -%}
60
- <h{{h+1}}>{{ title | escape }}</h{{h+1}}>
61
- {%- endif -%}
62
- <p><Image src={{figure | quote}} /></p>
63
- {%- endfor -%}
64
- {%- endif -%}
65
-
66
- {%- if job.out.outdir | glob: "features/vlnplots-*.png" -%}
67
- <h{{h}}>Violin plots of features</h{{h}}>
68
- {%- set figures = job.out.outdir | glob: "features/vlnplots-*.png" -%}
69
- {%- for figure in figures -%}
70
- {%- set title = figure | append: ".title" | read | escape -%}
71
- {%- if not title.startswith("vlnplots-") or len(figures) > 1 -%}
72
- <h{{h+1}}>{{ title | escape }}</h{{h+1}}>
73
- {%- endif -%}
74
- <p><Image src={{figure | quote}} /></p>
75
- {%- endfor -%}
76
- {%- endif -%}
77
-
78
- {%- if job.out.outdir | glob: "features/featureplots-*.png" -%}
79
- <h{{h}}>Feature plots of features</h{{h}}>
80
- {%- set figures = job.out.outdir | glob: "features/featureplots-*.png" -%}
81
- {%- for figure in figures -%}
82
- {%- set title = figure | append: ".title" | read | escape -%}
83
- {%- if not title.startswith("featureplots-") or len(figures) > 1 -%}
84
- <h{{h+1}}>{{ title | escape }}</h{{h+1}}>
85
- {%- endif -%}
86
- <p><Image src={{figure | quote}} /></p>
87
- {%- endfor -%}
88
- {%- endif -%}
89
-
90
- {%- if job.out.outdir | glob: "features/dotplot-*.png" -%}
91
- <h{{h}}>Dot plot of features</h{{h}}>
92
- {%- set figures = job.out.outdir | glob: "features/dotplot-*.png" -%}
93
- {%- for figure in figures -%}
94
- {%- set title = figure | append: ".title" | read | escape -%}
95
- {%- if not title.startswith("dotplot-") or len(figures) > 1 -%}
96
- <h{{h+1}}>{{ title | escape }}</h{{h+1}}>
97
- {%- endif -%}
98
- <p><Image src={{figure | quote}} /></p>
99
- {%- endfor -%}
100
- {%- endif -%}
101
-
102
- {%- if job.out.outdir | glob: "features/heatmap-*.png" -%}
103
- <h{{h}}>Heatmap of features</h{{h}}>
104
- {%- set figures = job.out.outdir | glob: "features/heatmap-*.png" -%}
105
- {%- for figure in figures -%}
106
- {%- set title = figure | append: ".title" | read | escape -%}
107
- {%- if not title.startswith("heatmap-") or len(figures) > 1 -%}
108
- <h{{h+1}}>{{ title | escape }}</h{{h+1}}>
109
- {%- endif -%}
110
- <p><Image src={{figure | quote}} /></p>
111
- {%- endfor -%}
112
- {%- endif -%}
9
+ {%- set stats_reports_file = job.out.outdir | joinpaths: "stats", "report_toc.json" -%}
10
+ {%- set features_reports_file = job.out.outdir | joinpaths: "features", "report_toc.json" -%}
11
+ {%- set dimplots_reports_file = job.out.outdir | joinpaths: "dimplots", "report_toc.json" -%}
113
12
 
13
+ {%- if stats_reports_file | exists -%}
14
+ {%- set stats = stats_reports_file | config: "json" -%}
15
+ {% for key, value in stats.items() -%}
16
+ <h{{h}}>{{key | escape}}</h{{h}}>
17
+ <Tabs>
18
+ {% if 'bar' in value -%}
19
+ <Tab label="Bar plot" />
20
+ {% endif -%}
21
+ {% if 'pie' in value -%}
22
+ <Tab label="Pie chart" />
23
+ {% endif -%}
24
+ {% if 'table' in value -%}
25
+ <Tab label="Table" />
26
+ {% endif -%}
27
+ <svelte:fragment slot="content">
28
+ {% if 'bar' in value -%}
29
+ <TabContent>
30
+ <Image src="{{job.out.outdir}}/stats/{{value.bar}}" />
31
+ </TabContent>
32
+ {% endif -%}
33
+ {% if 'pie' in value -%}
34
+ <TabContent>
35
+ <Image src="{{job.out.outdir}}/stats/{{value.pie}}" />
36
+ </TabContent>
37
+ {% endif -%}
38
+ {% if 'table' in value -%}
39
+ <TabContent>
40
+ <DataTable src="{{job.out.outdir}}/stats/{{value.table}}"
41
+ data={ {{job.out.outdir | joinpaths: "stats", value.table | datatable: sep="\t", nrows=100 }} }
42
+ />
43
+ </TabContent>
44
+ {% endif -%}
45
+ </svelte:fragment>
46
+ </Tabs>
47
+ {%- endfor -%}
48
+ {%- endif -%}
114
49
 
115
- {%- if job.out.outdir | joinpaths: "dimplots" | isdir -%}
116
- <h{{h}}>Dimensional reduction plots</h{{h}}>
117
- {%- set dpimgs = job.out.outdir | glob: "dimplots", "*.png" -%}
118
- {{ table_of_images(dpimgs, caps=[]) }}
119
- {%- endif -%}
50
+ {%- if features_reports_file | exists -%}
51
+ {%- set features = features_reports_file | config: "json" %}
52
+ {% for key, value in features.items() -%}
53
+ <h{{h}}>{{key | escape}}</h{{h}}>
54
+ {% for val in value -%}
55
+ {% if "name" in val -%}
56
+ <h{{h+1}}>{{val.name | escape}}</h{{h+1}}>
57
+ {%- endif -%}
58
+ {% if val.kind == "table" -%}
59
+ <DataTable src="{{job.out.outdir}}/features/{{val.file}}"
60
+ data={ {{job.out.outdir | joinpaths: "features", val.file | datatable: sep="\t", nrows=100 }} }
61
+ />
62
+ {% else -%}
63
+ <Image src="{{job.out.outdir}}/features/{{val.file}}" />
64
+ {% endif -%}
65
+ {%- endfor -%}
66
+ {%- endfor -%}
67
+ {%- endif -%}
120
68
 
69
+ {%- if dimplots_reports_file | exists -%}
70
+ {%- set dimplots = dimplots_reports_file | config: "json" %}
71
+ {% for key, value in dimplots.items() -%}
72
+ <h{{h}}>{{key | escape}}</h{{h}}>
73
+ <Image src="{{job.out.outdir}}/dimplots/{{value}}" />
74
+ {%- endfor -%}
75
+ {%- endif -%}
121
76
  {%- endmacro -%}
122
77
 
123
78
  {%- macro head_job(job) -%}