From b9e7578d7f69568d58e3467913aa7a792627720f Mon Sep 17 00:00:00 2001 From: Mariia Zueva Date: Fri, 25 Sep 2026 12:39:21 +0200 Subject: [PATCH 1/2] MILAB-7021 Sanger Ab1 files support --- .changeset/tagged-ab1-dataset-type.md | 8 + kind/src/params.ts | 1 + kind/src/types.ts | 13 + pnpm-lock.yaml | 306 ++++++++++---------- pnpm-workspace.yaml | 12 +- ui/src/dialogs/ImportDatasetDialog.vue | 41 +++ ui/src/dialogs/datasets.ts | 5 + ui/src/dialogs/file_name_parser.test.ts | 71 +++++ ui/src/dialogs/file_name_parser.ts | 23 +- ui/src/pages/DatasetPage.vue | 4 + ui/src/pages/TaggedAb1DatasetPage.vue | 111 +++++++ workflow/package.json | 2 +- workflow/src/ds-bulk-count-matrix.lib.tengo | 1 - workflow/src/ds-multiplexed-fastq.lib.tengo | 1 - workflow/src/ds-tagged-ab1.lib.tengo | 61 ++++ workflow/src/ds-tagged-xsv.lib.tengo | 1 - workflow/src/main.tpl.tengo | 3 +- workflow/src/prerun.tpl.tengo | 2 - 18 files changed, 499 insertions(+), 167 deletions(-) create mode 100644 .changeset/tagged-ab1-dataset-type.md create mode 100644 ui/src/pages/TaggedAb1DatasetPage.vue create mode 100644 workflow/src/ds-tagged-ab1.lib.tengo diff --git a/.changeset/tagged-ab1-dataset-type.md b/.changeset/tagged-ab1-dataset-type.md new file mode 100644 index 0000000..96e2b94 --- /dev/null +++ b/.changeset/tagged-ab1-dataset-type.md @@ -0,0 +1,8 @@ +--- +"@platforma-open/milaboratories.samples-and-data": minor +"@platforma-open/milaboratories.samples-and-data.kind": minor +"@platforma-open/milaboratories.samples-and-data.workflow": patch +"@platforma-open/milaboratories.samples-and-data.ui": patch +--- + +Add the `TaggedAb1` dataset type for Sanger chromatograms. One `.ab1` file per sample per tag, exported as a `File` column with `pl7.app/fileExtension` `ab1` on axes `[pl7.app/sampleId, pl7.app/sequencing/tag]`. A pattern such as `{{Sample}}_{{:Primer}}.ab1` groups the traces of one sample by the tag that tells them apart, for example the sequencing primer. diff --git a/kind/src/params.ts b/kind/src/params.ts index c61812a..e93ea4d 100644 --- a/kind/src/params.ts +++ b/kind/src/params.ts @@ -89,6 +89,7 @@ const DATASET_TYPES = { Fasta: true, Xsv: true, TaggedXsv: true, + TaggedAb1: true, BulkCountMatrix: true, CellRangerMTX: true, MultiplexedFastq: true, diff --git a/kind/src/types.ts b/kind/src/types.ts index eb7047f..35f8000 100644 --- a/kind/src/types.ts +++ b/kind/src/types.ts @@ -137,6 +137,17 @@ export interface DSContentTaggedXsv extends WithPerSampleData; + file: ImportFileHandle; +} + +/** Sanger chromatograms. Tags carry whatever distinguishes traces of one sample, e.g. the sequencing primer. */ +export interface DSContentTaggedAb1 extends WithPerSampleData { + type: "TaggedAb1"; + tags: string[]; +} + export type CellRangerMtxRole = "matrix.mtx" | "features.tsv" | "barcodes.tsv"; export type CellRangerMtxFileGroup = Partial>; @@ -218,6 +229,7 @@ export type DSContent = | DSContentFasta | DSContentXsv | DSContentTaggedXsv + | DSContentTaggedAb1 | DSContentBulkCountMatrix | DSContentCellRangerMtx | DSContentMultiplexedFastq @@ -240,6 +252,7 @@ export type DSMultilaneFastq = Dataset; export type DSTaggedFastq = Dataset; export type DSXsv = Dataset; export type DSTaggedXsv = Dataset; +export type DSTaggedAb1 = Dataset; export type DSCellRangerMtx = Dataset; export type DSBulkCountMatrix = Dataset; export type DSMultiplexedFastq = Dataset; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2611b38..4c76657 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,23 +31,23 @@ catalogs: specifier: 1.1.0 version: 1.1.0 '@platforma-sdk/block-tools': - specifier: 2.16.1 - version: 2.16.1 + specifier: 2.16.3 + version: 2.16.3 '@platforma-sdk/model': - specifier: 1.83.17 - version: 1.83.17 + specifier: 1.84.1 + version: 1.84.1 '@platforma-sdk/tengo-builder': - specifier: 4.1.1 - version: 4.1.1 + specifier: 4.1.4 + version: 4.1.4 '@platforma-sdk/test': - specifier: 1.83.25 - version: 1.83.25 + specifier: 1.84.1 + version: 1.84.1 '@platforma-sdk/ui-vue': - specifier: 1.83.21 - version: 1.83.21 + specifier: 1.84.2 + version: 1.84.2 '@platforma-sdk/workflow-tengo': - specifier: 6.11.1 - version: 6.11.1 + specifier: 6.12.1 + version: 6.12.1 '@vueuse/core': specifier: ^12.2.0 version: 12.8.2 @@ -104,7 +104,7 @@ importers: version: 1.7.2(@microsoft/api-extractor@7.52.8(@types/node@24.5.2))(@types/node@24.5.2)(@volar/typescript@2.4.28)(@vue/language-core@3.3.10)(rollup@4.37.0)(vue@3.5.25(typescript@7.0.2))(yaml@2.8.1) '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.16.1(@types/node@24.5.2) + version: 2.16.3(@types/node@24.5.2) shx: specifier: 'catalog:' version: 0.4.0 @@ -134,10 +134,10 @@ importers: version: link:../workflow '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.16.1(@types/node@24.5.2) + version: 2.16.3(@types/node@24.5.2) '@platforma-sdk/model': specifier: 'catalog:' - version: 1.83.17 + version: 1.84.1 shx: specifier: 'catalog:' version: 0.4.0 @@ -171,7 +171,7 @@ importers: version: 1.4.0 '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.16.1(@types/node@24.5.2) + version: 2.16.3(@types/node@24.5.2) model: dependencies: @@ -180,7 +180,7 @@ importers: version: link:../kind '@platforma-sdk/model': specifier: 'catalog:' - version: 1.83.17 + version: 1.84.1 typescript: specifier: '*' version: 5.8.2 @@ -196,7 +196,7 @@ importers: version: 1.4.0 '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.16.1(@types/node@24.5.2) + version: 2.16.3(@types/node@24.5.2) '@types/node': specifier: '*' version: 24.5.2 @@ -211,7 +211,7 @@ importers: version: 1.7.5 '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.16.1(@types/node@24.5.2) + version: 2.16.3(@types/node@24.5.2) software/parse-seurat: devDependencies: @@ -223,7 +223,7 @@ importers: version: 1.0.1 '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.16.1(@types/node@24.5.2) + version: 2.16.3(@types/node@24.5.2) test: dependencies: @@ -232,7 +232,7 @@ importers: version: link:../model '@platforma-sdk/model': specifier: 'catalog:' - version: 1.83.17 + version: 1.84.1 this-block: specifier: workspace:@platforma-open/milaboratories.samples-and-data@* version: link:../block @@ -248,7 +248,7 @@ importers: version: 1.4.0 '@platforma-sdk/test': specifier: 'catalog:' - version: 1.83.25(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.1)) + version: 1.84.1(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.1)) vitest: specifier: 'catalog:' version: 4.0.10(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.1) @@ -260,7 +260,7 @@ importers: version: link:../model '@platforma-sdk/ui-vue': specifier: 'catalog:' - version: 1.83.21(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.8.2) + version: 1.84.2(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.8.2) typescript: specifier: '*' version: 5.8.2 @@ -282,7 +282,7 @@ importers: version: 1.4.0 '@platforma-sdk/model': specifier: 'catalog:' - version: 1.83.17 + version: 1.84.1 '@types/node': specifier: '*' version: 24.5.2 @@ -321,14 +321,14 @@ importers: version: link:../software/parse-seurat '@platforma-sdk/workflow-tengo': specifier: 'catalog:' - version: 6.11.1 + version: 6.12.1 devDependencies: '@platforma-sdk/tengo-builder': specifier: 'catalog:' - version: 4.1.1 + version: 4.1.4 '@platforma-sdk/test': specifier: 'catalog:' - version: 1.83.25(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.1)) + version: 1.84.1(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.1)) shx: specifier: 'catalog:' version: 0.4.0 @@ -1077,8 +1077,8 @@ packages: '@microsoft/tsdoc@0.15.1': resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} - '@milaboratories/columns-collection-driver@0.2.5': - resolution: {integrity: sha512-34GWMESzpu6MGEiDCLPuglVL3MSPt3MFKGJQfKHLjmVn725bZQ2kjRjPDLZxAqs6v85GNm3PHtAub0/dqoGZYw==} + '@milaboratories/columns-collection-driver@0.2.7': + resolution: {integrity: sha512-yuDUB0TJ18P2RSR/v6i4yprH+vzaHcvm1rrV5yxhX/iyjS+51b+ZRV9ydsILInKRnBjPhwboOhL1O5J46d8fsw==} '@milaboratories/computable@2.9.8': resolution: {integrity: sha512-X0ZtxOnIJlAd9Y7CyBtWSKHgVuTKXbIryMwJaoYSEz0gY3JZVnsD0f59J/lwe3Key0/lSYh3EbL/pP5jACIzfQ==} @@ -1092,15 +1092,15 @@ packages: resolution: {integrity: sha512-Kiy0g7sEmFQxDwtnmTrdJ8XdUK0IDAB5ZnPCNEbK7lPGHIa+xG3kzfeR4y44QBdrYaK0NwG63D8Fc7dlv9KItg==} engines: {node: '>=22'} - '@milaboratories/pf-driver@1.9.3': - resolution: {integrity: sha512-qKxWIXFRWFYUllkbcZ8hPgG4uzCUzi7GMmXezddOqUDzx7vxD/4fFoQyOBqbAGzd2SDxE24KIVK1Z5LefUNGBQ==} + '@milaboratories/pf-driver@1.9.5': + resolution: {integrity: sha512-zC3yrpjolavTlt8J96nir4Xuf43khS/9Nhkh3vkHT2I2UHzHTsU2nG+u14YasZ5Otbpm9o/iXl+XiD0RBSXG6w==} engines: {node: '>=22.19.0'} - '@milaboratories/pf-spec-driver@1.5.3': - resolution: {integrity: sha512-27012xFuOegswZ4Snz7XMb5D1Rg3mSIY/INgncY/qvVLxMASkryCPbQLbiLLm9FlyAZ3rezC2l+bln8lRJmM7A==} + '@milaboratories/pf-spec-driver@1.5.5': + resolution: {integrity: sha512-iUzaODXn8KGkidX3ZTZiWhZ+ZT5LEx8CMwD3o8psyfVol3BPFJj6ehF1whVUDGWuqXoZLVrX2lzUFMT8hcu7AQ==} - '@milaboratories/pf-spec@1.0.3': - resolution: {integrity: sha512-ADXoJRjOFONLgp9WxAINvxtXtxjSg6ZADCtvE3dBSzF/e1p0ZDewhds8s3d3b6+3WFJnM8r3eOCcRdbYsPq7lg==} + '@milaboratories/pf-spec@1.0.5': + resolution: {integrity: sha512-guFIwZzPAdu8HtGKj2m6ZLEPF9DKO9AVJZzL9nCJoS4BmZ+Ffcbv1Ks+u+QUvZDRX4p+sGbWRmNBFe0E6kQEVw==} peerDependencies: '@bytecodealliance/preview2-shim': ^0.20.1 @@ -1114,29 +1114,29 @@ packages: '@milaboratories/pframes-rs-wasip2@1.1.61': resolution: {integrity: sha512-0/LwALWJxsWjRf7AyhznRCzOOR4MyzAq4OJ+FvaRuNUztSBJZeXWRBzatmDN9JjCoonap1F05cy33NXKX+hUyQ==} - '@milaboratories/pl-client@3.17.1': - resolution: {integrity: sha512-V+IIB5x0e5DwaleRYf61a/Rxxbtbqq+vCJitAB0GOA2fFuTo1LwDfxFlUgrRpzpVWX7ixpFq+lExVdI2sUBPNg==} + '@milaboratories/pl-client@3.17.3': + resolution: {integrity: sha512-O0Zddg0aBXv4G1rcw+pjL+4F9cPbk+NfIKPQ0d9HVI4cjiWLR2eBK4fzwdolqAlq+Go9frSJ/UzBMNMgWUQytw==} engines: {node: '>=22.19.0'} '@milaboratories/pl-config@1.8.5': resolution: {integrity: sha512-XnfYXSSkRxeImQ21k6I8y5apisvagcSgMGfEeyRxNdSGwUVJbbI8TwJk+XBEDQ4lErW8oqtLxKjFQuHJMzRUoQ==} - '@milaboratories/pl-crash-recorder@0.3.1': - resolution: {integrity: sha512-xv5KzVufit9ccbfUdvLLZj9aT7yZZXMOKHM4ABVyGRDKZLvNx7eQnll2nCUElwMjgJ8azr4m3kqAk1eOBydE9w==} + '@milaboratories/pl-crash-recorder@0.3.3': + resolution: {integrity: sha512-5++ZwZAgZS9FVsl6VxU3ykehVWiX5uFLAuGeZbRsjmlIqI9ViMmvfh7bQJEM7DSU+hjdKeqXtc9Gm9qd29h4fQ==} - '@milaboratories/pl-deployments@3.0.17': - resolution: {integrity: sha512-r6FRMqHleGNShS+sJhOslef+hr6IyIVDZ5MPZwLy1vPqwYP4AuPngExT+KAMs/6x9/q0xpmFXUq3vUq0dqraXQ==} + '@milaboratories/pl-deployments@3.0.19': + resolution: {integrity: sha512-UkAamK9XCy+oPytHpkGUGAmyIBPPq1HNXTq+OCdgvUFPflz6cD4uMjS6NCumOwDN6tqq3T2SsclitzW+Vor1Uw==} engines: {node: '>=22.19.0'} - '@milaboratories/pl-drivers@1.16.24': - resolution: {integrity: sha512-eBqqCenIt7UF0QlCcBAuxrMi32BqTc2ZYdn6fCl4/Sz2Q1/LcZH7cEXO0m1K06QC1xNlKbENhjpRtezZnTCerQ==} + '@milaboratories/pl-drivers@1.16.26': + resolution: {integrity: sha512-CRFqv1c8XRUpTJwpAHqs35pH7EAf0cf3pSMY6VcRbrDiCtnhatRxU3pPeqLZu7cxnU9kn5moUERCw9Ej0939Vg==} engines: {node: '>=22'} '@milaboratories/pl-error-like@1.12.10': resolution: {integrity: sha512-iHmnLG5lxJqcymUmEL8onCDGEADk1S/5RNACQ5yfTCNcCkUc+7hYrDHQpFmWXPPGC9sG+NTBxt4wr5m8UsLm2g==} - '@milaboratories/pl-errors@1.4.42': - resolution: {integrity: sha512-I1L8JbkVPMxCv/825XitRRl2aMVW+2TJ70oeuUp+ImGjyrdSTXzLpaEmd78D/R17Zwif080v51ItKZo4HcaqgA==} + '@milaboratories/pl-errors@1.4.44': + resolution: {integrity: sha512-4piZavojvos+3CJ4qV3i3z7D+8iPmiJNCexyUCBsaCTWQTu99bk0ENXr/akSRjiv4Sa9JUXtxTer033GonKtnQ==} '@milaboratories/pl-healthcheck@1.0.5': resolution: {integrity: sha512-ZkQti4VU2FapJBFRtZGfR5NDPXEAEFgTf/NfemypgY0aA75fFPi4/c3BpXX5K7dht+JOgHqkMrBHxKIuE2T+fA==} @@ -1145,12 +1145,12 @@ packages: '@milaboratories/pl-http@1.2.4': resolution: {integrity: sha512-QKmhx+WEvJCV9dUy/SBdQk/ApaJ5ewBFgm/b+XPlS10SusAdqUUTGvK5+hq8YSuUMXlHb/dk++UtI5YlDuDl2Q==} - '@milaboratories/pl-middle-layer@1.71.13': - resolution: {integrity: sha512-j+SCV1t2iI1l8N68oSxlLRSP/btokaKjPDuxSDr2btgIzaNftxykktCa5c6N/clvdVVuWykRgloj+3Lb0O7wQQ==} + '@milaboratories/pl-middle-layer@1.72.0': + resolution: {integrity: sha512-UL+VCk/XxzD+zrzX9hl0Rbf6/d8NjZ3ScL+wZXooSmIpErWIYrSeR0IAyBRAEJDeMDEMEPk5x/D/cIiwoQVMNA==} engines: {node: '>=22.19.0'} - '@milaboratories/pl-model-backend@1.4.27': - resolution: {integrity: sha512-tOGjfHKNtwZ/2ezsjoI/9zvCplbCC7fPo9Bzr9RXyqw8h4TEOgKyfTtNUvlxl9mGGvXaY5A/Jb7z1UdVdxa2pA==} + '@milaboratories/pl-model-backend@1.4.29': + resolution: {integrity: sha512-A4boh0VStHBoIlM54DqN2JHu+R10+/KVXY6mnDQd4F3wWuKlssCE2yLGoY3TIpeWCgFME7rLtei5ZLwYjV81eg==} '@milaboratories/pl-model-common@1.46.2': resolution: {integrity: sha512-VEeauisApYScvCS8lnK3zpFJ520xuTAodKJmjR8ulHcMrWMyWMfHEdGb7j5OMD0mM/OwTgmQrrJ5eB7Xd+xoOQ==} @@ -1158,21 +1158,21 @@ packages: '@milaboratories/pl-model-common@1.48.0': resolution: {integrity: sha512-oCVrjFNmjQolb7YWnbSGHnp6GGVm1PhG1lnNABp9lqYjvA2ISjIPQLIo/vT2ca0mqwLrEvbuRqiqSFOg7sQ5tQ==} - '@milaboratories/pl-model-common@1.49.0': - resolution: {integrity: sha512-xTPt0gYwUUQPdqO8jMAqHi7TSkReZdBVN27LLWtsIsF0E2QIcTxdiipAdFxRH7xKbD5X6iXsyWgWrKDCck9Q0g==} + '@milaboratories/pl-model-common@1.51.0': + resolution: {integrity: sha512-KowoSl3BTUo8N4yUYpFiIdi7JZmtGX2bm4roK9maeoGdB3Tb0SsfySsWgdDSGwWMvxHwQ6cp3DA2dkw1dFlmcg==} '@milaboratories/pl-model-middle-layer@1.30.7': resolution: {integrity: sha512-rs9x3Ron4ujR/UOdEgB8WUB1SvZ8ZAScT1Av/e4or+iiQ/CzhmK9nqtYamHVwKV+JgwIFbXQwxGvIHDVz955dQ==} - '@milaboratories/pl-model-middle-layer@1.32.1': - resolution: {integrity: sha512-MSodImNNOXnzWzQrCZbDdCl6ZbNzZQ1+uywpV7R4vZmuGwnLDym+zjTjB0H+maOLR5DGzG3QvUSP8R6W0zA5Vw==} + '@milaboratories/pl-model-middle-layer@1.33.0': + resolution: {integrity: sha512-df6VahIm5uOJfg19tbQZsytlu32gmO+tMzgsis+n8Iom+SXqvFEWzcf/uzo+PM9Un+yUcQZKUQDiWZX9RVnU7Q==} - '@milaboratories/pl-tree@1.15.2': - resolution: {integrity: sha512-w20ncdEJQ+U105onHnwMaensL1CmJ7nQEzf/d4ueHh7xViT66ghq/FPiGzemQwknJigOUz27KFPh4WCP8c7gxw==} + '@milaboratories/pl-tree@1.15.4': + resolution: {integrity: sha512-tKqhArB/LlpPZnB9driSo9vLqF2GP6laN9++IpEqKndYs+irtMVzWPY7KHk2I8A12TDPWVTSCl94pAW51Chsbg==} engines: {node: '>=22.19.0'} - '@milaboratories/ptabler-expression-js@1.2.39': - resolution: {integrity: sha512-nhEw7aBnQA0dI69+e2CXQxc1kl+Um8uf9/eXfrd0jsnc/xvYoyAoAzerlgJA707ARWdcAlANpSqgsqeviJalzA==} + '@milaboratories/ptabler-expression-js@1.2.41': + resolution: {integrity: sha512-GnZAF1PT3XWrFb6SU7twKiWPpyTV7OVu76I3XWB/zpvkHb18O1K+kiXuN0k8JzTWQCUmq6tPlRP6R5CtmhK6Fw==} '@milaboratories/resolve-helper@1.1.3': resolution: {integrity: sha512-38/dW/XRZQREOxAOOKtO0lzEWPCP/DH0qhB3q1kYcGoN++5V92/zbVwbYrMDeDcjTyo+D62iIep+sKXeWHa7Uw==} @@ -1196,8 +1196,8 @@ packages: resolution: {integrity: sha512-ef01tARUl+0Urt3x8HHAByrhYHg4Rnn7WiFyJ+joZFZl1T1pUKTgfB7Zxc8Cn06HIl4i6H7s5OSymjZePIGqfQ==} engines: {node: '>=22.19.0'} - '@milaboratories/uikit@2.15.30': - resolution: {integrity: sha512-jviPS0S6XTxKdNzJXPZ4vVzdgRU+VKcp6BjYoM7NhgVJGTdW1MFIZrFtdKuqsspyDDC18YLnLwr8CoYR43xAxg==} + '@milaboratories/uikit@2.15.33': + resolution: {integrity: sha512-+P9Xio5SRuks8ESOjV1ezbw/CE5ttkrkwE89eKVpF3B1mkx4ZgBuPfYF/iaWMj59AKo9wKE6uN5D5yzmv50WiQ==} '@napi-rs/wasm-runtime@1.1.6': resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} @@ -1749,11 +1749,11 @@ packages: '@platforma-open/milaboratories.runenv-r-samples-and-data@1.0.1': resolution: {integrity: sha512-uO8SXeZGpN27IAdUwbjAiDO7dPXTO3/7w8am7mIaKJjJQrOLI31MSuN0/r5ZG4ZU2JCJESmJHl9XMCBaYWQ82Q==} - '@platforma-open/milaboratories.software-ptabler.schema@1.15.23': - resolution: {integrity: sha512-tCrPwxtzYvO33FsgpE3FCZx4aERVKjg77aVUaYC0RNisXW7fgqIqfkN30tQZZQ77v1A7E+PC31/oKsKScehOlw==} + '@platforma-open/milaboratories.software-ptabler.schema@1.15.25': + resolution: {integrity: sha512-SZww/g+LCsVrsMzdfZv/HnzU29ASuejGTly33gOldmN7INV74ntgL0jgE3chcAAAZQjvnjmE6NUHKZ0HIZsujg==} - '@platforma-open/milaboratories.software-ptabler@2.1.13': - resolution: {integrity: sha512-TSzxI20wl13ksaz5/P3EkdybfqtMGTD3Bm49F80Iizn2mrCJHyq071Ro5ozGNHIEdKeGQ8pbqWKxRqqeXZ8LtA==} + '@platforma-open/milaboratories.software-ptabler@2.1.14': + resolution: {integrity: sha512-gUh8h12v7FG5hj5+dPHETAk69hu4kqUPKumYKUbcAcMoEi0mhh/MEm/epHPxnCVNBNJEwwbscJ5HsrJQUTr1HQ==} '@platforma-open/milaboratories.software-ptexter@1.2.6': resolution: {integrity: sha512-UNGsCwUpgnPSFFmv1YegCrzbZRlY72/XTRGTubX9PmMYqR6TYPT07hBIpHQj7Ax74+k1puH07g1QP6gIaj/6Ow==} @@ -1779,33 +1779,33 @@ packages: '@platforma-sdk/block-kind@1.1.0': resolution: {integrity: sha512-4uh+40o6BGfQcPPhkYGCjng8EXR9qn+6i5Fuc3RJ5QGfPYfcwbxT35G2V7ALUxmsqjLZ5SGL+JcW6Lm2v0cZHA==} - '@platforma-sdk/block-tools@2.16.1': - resolution: {integrity: sha512-Dzt8afWiO6j/sqMiemSbvLQ3iTABVMBO6df2ijxfKtt02vocXmTKDqVv40GWLpQwoMuzPBLmt1XJHeK3nL231A==} + '@platforma-sdk/block-tools@2.16.3': + resolution: {integrity: sha512-IESVIJNyZbFshFoCV8k6BgT4Z8cmVDeTQbkzCGOzb0xDk6k+r581W+vQ/f5Ya0kvke91Em8HKEA90fkuErSqQA==} hasBin: true '@platforma-sdk/blocks-deps-updater@2.2.0': resolution: {integrity: sha512-p9lBxhFXM9WoRsrJO7dfkiXSK+1m63yIn1sKhBO71eMbhrLMyVYHEOeNf3w5OCdbRF5QsNhXzWuiTmFK3zHFsA==} hasBin: true - '@platforma-sdk/model@1.83.17': - resolution: {integrity: sha512-r3NqgNgWPcU1i/YPCl+5avLK+Ojd4APf8AOFBOq76J20y0v9H5UV0A8P3V8fiTbbe79u209r/5LzatyZV2xFvg==} + '@platforma-sdk/model@1.84.1': + resolution: {integrity: sha512-UmA9FQzs8rU+vqHAxOvd/uk9CYhej2damyhFNB8uFh8fKjNoUYZkpBa/jvn/G//c+wtFyQdjtxaBSq0PV0Pkag==} '@platforma-sdk/package-builder-lib@1.4.0': resolution: {integrity: sha512-ORcuPJ5SM1RiQAOUVM7alWDbOWQ/gUTgLDToP/yAs2uHXhKNvXphgDQTamCX6cseMZ0uKdWzTudJAQAJM1Kvwg==} - '@platforma-sdk/tengo-builder@4.1.1': - resolution: {integrity: sha512-kCnX8Al/88L+/6Z8aCdEBDfLlN6SV37cce2iFRJzEeePA23P0fcvHDiDjycxw1rv9670jFZxc72n3Jc4xtO37g==} + '@platforma-sdk/tengo-builder@4.1.4': + resolution: {integrity: sha512-vYGuYnbQqnzmpxewixh+O/eAtc8ai6SjLoYux9dBmuIHSEzzlfJdGsJO1V6yR+BrZ1DRX0ZIY3jVR1KBnur7qg==} engines: {node: '>=22'} hasBin: true - '@platforma-sdk/test@1.83.25': - resolution: {integrity: sha512-o2YopM/I4lKPZBCq3EjPGHg5I2tJvHw2uJS6LrIRizpIUqw9icO3n1SEpwK9yPeIYFPlCr7rYuh3XtQf5zUdzQ==} + '@platforma-sdk/test@1.84.1': + resolution: {integrity: sha512-hcWmFqqUeU/Xn8g9ZcGZfglGcgBgc/828zf831TJZNoBInt/KJJ7HQyYymRcg8wMVrLJ7iirED2VO0jX9OC0Pw==} - '@platforma-sdk/ui-vue@1.83.21': - resolution: {integrity: sha512-jyK6LU13t7ZxHVNkWVOad0jchZmwusGUioSn6x/Sx7sT/CoLp1hMjctUwbsY/c3fIjCufLXboWOobmOTAyId0w==} + '@platforma-sdk/ui-vue@1.84.2': + resolution: {integrity: sha512-4qBzhV5XAtknD7FvL80OVh/UT/e0HsG45Bu2VKHkhe7noCriK50B51Wsx2hTldLvqRY8PLU5b58yFzSTBAzpxg==} - '@platforma-sdk/workflow-tengo@6.11.1': - resolution: {integrity: sha512-vEmPY8zH6FMhxXBmsZSsGxhgjsgh23AxF0zZxfbYqCm2eNMFyTFRXqLnLNhRnOjghf76npDr2ILh+VlEHJp8Tg==} + '@platforma-sdk/workflow-tengo@6.12.1': + resolution: {integrity: sha512-Fk+OIYeqbzheLdtZVGc9GcwAOc8dEvzjgHnwJdGN3rJW8dE/eZ17nYEdYIDL4KGjjqd99CyCr3sWw1fHA6ngxQ==} '@protobuf-ts/grpc-transport@2.11.1': resolution: {integrity: sha512-l6wrcFffY+tuNnuyrNCkRM8hDIsAZVLA8Mn7PKdVyYxITosYh60qW663p9kL6TWXYuDCL3oxH8ih3vLKTDyhtg==} @@ -6254,10 +6254,10 @@ snapshots: '@microsoft/tsdoc@0.15.1': optional: true - '@milaboratories/columns-collection-driver@0.2.5': + '@milaboratories/columns-collection-driver@0.2.7': dependencies: '@milaboratories/helpers': 1.14.5 - '@milaboratories/pl-model-common': 1.49.0 + '@milaboratories/pl-model-common': 1.51.0 '@milaboratories/computable@2.9.8': dependencies: @@ -6270,13 +6270,13 @@ snapshots: '@milaboratories/helpers@1.14.5': {} - '@milaboratories/pf-driver@1.9.3(@bytecodealliance/preview2-shim@0.17.8)': + '@milaboratories/pf-driver@1.9.5(@bytecodealliance/preview2-shim@0.17.8)': dependencies: '@milaboratories/helpers': 1.14.5 - '@milaboratories/pf-spec': 1.0.3(@bytecodealliance/preview2-shim@0.17.8) + '@milaboratories/pf-spec': 1.0.5(@bytecodealliance/preview2-shim@0.17.8) '@milaboratories/pframes-rs-node': 1.1.61 - '@milaboratories/pl-model-common': 1.49.0 - '@milaboratories/pl-model-middle-layer': 1.32.1 + '@milaboratories/pl-model-common': 1.51.0 + '@milaboratories/pl-model-middle-layer': 1.33.0 '@milaboratories/ts-helpers': 1.8.6 es-toolkit: 1.39.10 lru-cache: 11.2.2 @@ -6285,20 +6285,20 @@ snapshots: - encoding - supports-color - '@milaboratories/pf-spec-driver@1.5.3(@bytecodealliance/preview2-shim@0.17.8)': + '@milaboratories/pf-spec-driver@1.5.5(@bytecodealliance/preview2-shim@0.17.8)': dependencies: '@milaboratories/helpers': 1.14.5 - '@milaboratories/pf-spec': 1.0.3(@bytecodealliance/preview2-shim@0.17.8) - '@milaboratories/pl-model-common': 1.49.0 + '@milaboratories/pf-spec': 1.0.5(@bytecodealliance/preview2-shim@0.17.8) + '@milaboratories/pl-model-common': 1.51.0 '@noble/hashes': 2.2.0 transitivePeerDependencies: - '@bytecodealliance/preview2-shim' - '@milaboratories/pf-spec@1.0.3(@bytecodealliance/preview2-shim@0.17.8)': + '@milaboratories/pf-spec@1.0.5(@bytecodealliance/preview2-shim@0.17.8)': dependencies: '@bytecodealliance/preview2-shim': 0.17.8 - '@milaboratories/pl-model-common': 1.49.0 - '@milaboratories/pl-model-middle-layer': 1.32.1 + '@milaboratories/pl-model-common': 1.51.0 + '@milaboratories/pl-model-middle-layer': 1.33.0 '@milaboratories/pframes-rs-node@1.1.61': dependencies: @@ -6322,11 +6322,11 @@ snapshots: '@milaboratories/pframes-rs-wasip2@1.1.61': {} - '@milaboratories/pl-client@3.17.1': + '@milaboratories/pl-client@3.17.3': dependencies: '@grpc/grpc-js': 1.14.4 '@milaboratories/pl-http': 1.2.4 - '@milaboratories/pl-model-common': 1.49.0 + '@milaboratories/pl-model-common': 1.51.0 '@milaboratories/ts-helpers': 1.8.6 '@protobuf-ts/grpc-transport': 2.11.1(@grpc/grpc-js@1.14.4) '@protobuf-ts/runtime': 2.11.1 @@ -6346,16 +6346,16 @@ snapshots: upath: 2.0.1 yaml: 2.8.1 - '@milaboratories/pl-crash-recorder@0.3.1': + '@milaboratories/pl-crash-recorder@0.3.3': dependencies: - '@milaboratories/pl-model-common': 1.49.0 + '@milaboratories/pl-model-common': 1.51.0 - '@milaboratories/pl-deployments@3.0.17': + '@milaboratories/pl-deployments@3.0.19': dependencies: '@milaboratories/pl-config': 1.8.5 '@milaboratories/pl-healthcheck': 1.0.5 '@milaboratories/pl-http': 1.2.4 - '@milaboratories/pl-model-common': 1.49.0 + '@milaboratories/pl-model-common': 1.51.0 '@milaboratories/ts-helpers': 1.8.6 decompress: 4.2.1 ssh2: 1.16.0 @@ -6365,14 +6365,14 @@ snapshots: yaml: 2.8.1 zod: 3.25.76 - '@milaboratories/pl-drivers@1.16.24': + '@milaboratories/pl-drivers@1.16.26': dependencies: '@grpc/grpc-js': 1.14.4 '@milaboratories/computable': 2.9.8 '@milaboratories/helpers': 1.14.5 - '@milaboratories/pl-client': 3.17.1 - '@milaboratories/pl-model-common': 1.49.0 - '@milaboratories/pl-tree': 1.15.2 + '@milaboratories/pl-client': 3.17.3 + '@milaboratories/pl-model-common': 1.51.0 + '@milaboratories/pl-tree': 1.15.4 '@milaboratories/ts-helpers': 1.8.6 '@protobuf-ts/grpc-transport': 2.11.1(@grpc/grpc-js@1.14.4) '@protobuf-ts/plugin': 2.11.1 @@ -6394,9 +6394,9 @@ snapshots: json-stringify-safe: 5.0.1 zod: 3.25.76 - '@milaboratories/pl-errors@1.4.42': + '@milaboratories/pl-errors@1.4.44': dependencies: - '@milaboratories/pl-client': 3.17.1 + '@milaboratories/pl-client': 3.17.3 '@milaboratories/ts-helpers': 1.8.6 zod: 3.25.76 @@ -6412,29 +6412,29 @@ snapshots: dependencies: undici: 7.16.0 - '@milaboratories/pl-middle-layer@1.71.13(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)': + '@milaboratories/pl-middle-layer@1.72.0(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)': dependencies: - '@milaboratories/columns-collection-driver': 0.2.5 + '@milaboratories/columns-collection-driver': 0.2.7 '@milaboratories/computable': 2.9.8 '@milaboratories/helpers': 1.14.5 - '@milaboratories/pf-driver': 1.9.3(@bytecodealliance/preview2-shim@0.17.8) - '@milaboratories/pf-spec-driver': 1.5.3(@bytecodealliance/preview2-shim@0.17.8) + '@milaboratories/pf-driver': 1.9.5(@bytecodealliance/preview2-shim@0.17.8) + '@milaboratories/pf-spec-driver': 1.5.5(@bytecodealliance/preview2-shim@0.17.8) '@milaboratories/pframes-rs-node': 1.1.61 - '@milaboratories/pl-client': 3.17.1 - '@milaboratories/pl-crash-recorder': 0.3.1 - '@milaboratories/pl-deployments': 3.0.17 - '@milaboratories/pl-drivers': 1.16.24 - '@milaboratories/pl-errors': 1.4.42 + '@milaboratories/pl-client': 3.17.3 + '@milaboratories/pl-crash-recorder': 0.3.3 + '@milaboratories/pl-deployments': 3.0.19 + '@milaboratories/pl-drivers': 1.16.26 + '@milaboratories/pl-errors': 1.4.44 '@milaboratories/pl-http': 1.2.4 - '@milaboratories/pl-model-backend': 1.4.27 - '@milaboratories/pl-model-common': 1.49.0 - '@milaboratories/pl-model-middle-layer': 1.32.1 - '@milaboratories/pl-tree': 1.15.2 + '@milaboratories/pl-model-backend': 1.4.29 + '@milaboratories/pl-model-common': 1.51.0 + '@milaboratories/pl-model-middle-layer': 1.33.0 + '@milaboratories/pl-tree': 1.15.4 '@milaboratories/resolve-helper': 1.1.3 '@milaboratories/ts-helpers': 1.8.6 - '@platforma-sdk/block-tools': 2.16.1(@types/node@24.5.2) - '@platforma-sdk/model': 1.83.17 - '@platforma-sdk/workflow-tengo': 6.11.1 + '@platforma-sdk/block-tools': 2.16.3(@types/node@24.5.2) + '@platforma-sdk/model': 1.84.1 + '@platforma-sdk/workflow-tengo': 6.12.1 canonicalize: 2.1.0 denque: 2.1.0 es-toolkit: 1.39.10 @@ -6453,9 +6453,9 @@ snapshots: - encoding - supports-color - '@milaboratories/pl-model-backend@1.4.27': + '@milaboratories/pl-model-backend@1.4.29': dependencies: - '@milaboratories/pl-client': 3.17.1 + '@milaboratories/pl-client': 3.17.3 canonicalize: 2.1.0 zod: 3.25.76 @@ -6474,7 +6474,7 @@ snapshots: es-toolkit: 1.39.10 zod: 3.25.76 - '@milaboratories/pl-model-common@1.49.0': + '@milaboratories/pl-model-common@1.51.0': dependencies: '@milaboratories/helpers': 1.14.5 '@milaboratories/pl-error-like': 1.12.10 @@ -6490,27 +6490,27 @@ snapshots: utility-types: 3.11.0 zod: 3.25.76 - '@milaboratories/pl-model-middle-layer@1.32.1': + '@milaboratories/pl-model-middle-layer@1.33.0': dependencies: '@milaboratories/helpers': 1.14.5 - '@milaboratories/pl-model-common': 1.49.0 + '@milaboratories/pl-model-common': 1.51.0 es-toolkit: 1.39.10 utility-types: 3.11.0 zod: 3.25.76 - '@milaboratories/pl-tree@1.15.2': + '@milaboratories/pl-tree@1.15.4': dependencies: '@milaboratories/computable': 2.9.8 - '@milaboratories/pl-client': 3.17.1 - '@milaboratories/pl-errors': 1.4.42 + '@milaboratories/pl-client': 3.17.3 + '@milaboratories/pl-errors': 1.4.44 '@milaboratories/ts-helpers': 1.8.6 denque: 2.1.0 utility-types: 3.11.0 zod: 3.25.76 - '@milaboratories/ptabler-expression-js@1.2.39': + '@milaboratories/ptabler-expression-js@1.2.41': dependencies: - '@platforma-open/milaboratories.software-ptabler.schema': 1.15.23 + '@platforma-open/milaboratories.software-ptabler.schema': 1.15.25 '@milaboratories/resolve-helper@1.1.3': {} @@ -6756,10 +6756,10 @@ snapshots: canonicalize: 2.1.0 denque: 2.1.0 - '@milaboratories/uikit@2.15.30(typescript@5.8.2)': + '@milaboratories/uikit@2.15.33(typescript@5.8.2)': dependencies: '@milaboratories/helpers': 1.14.5 - '@platforma-sdk/model': 1.83.17 + '@platforma-sdk/model': 1.84.1 '@types/d3-array': 3.2.1 '@types/d3-axis': 3.0.6 '@types/d3-scale': 4.0.9 @@ -7167,11 +7167,11 @@ snapshots: '@platforma-open/milaboratories.runenv-r-samples-and-data@1.0.1': {} - '@platforma-open/milaboratories.software-ptabler.schema@1.15.23': + '@platforma-open/milaboratories.software-ptabler.schema@1.15.25': dependencies: - '@milaboratories/pl-model-common': 1.49.0 + '@milaboratories/pl-model-common': 1.51.0 - '@platforma-open/milaboratories.software-ptabler@2.1.13': {} + '@platforma-open/milaboratories.software-ptabler@2.1.14': {} '@platforma-open/milaboratories.software-ptexter@1.2.6': {} @@ -7195,15 +7195,15 @@ snapshots: '@platforma-sdk/block-kind@1.1.0': {} - '@platforma-sdk/block-tools@2.16.1(@types/node@24.5.2)': + '@platforma-sdk/block-tools@2.16.3(@types/node@24.5.2)': dependencies: '@aws-sdk/client-ecr-public': 3.859.0 '@aws-sdk/client-s3': 3.859.0 '@inquirer/prompts': 7.10.1(@types/node@24.5.2) '@milaboratories/pl-http': 1.2.4 - '@milaboratories/pl-model-backend': 1.4.27 - '@milaboratories/pl-model-common': 1.49.0 - '@milaboratories/pl-model-middle-layer': 1.32.1 + '@milaboratories/pl-model-backend': 1.4.29 + '@milaboratories/pl-model-common': 1.51.0 + '@milaboratories/pl-model-middle-layer': 1.33.0 '@milaboratories/resolve-helper': 1.1.3 '@milaboratories/ts-helpers': 1.8.6 '@platforma-sdk/blocks-deps-updater': 2.2.0 @@ -7224,13 +7224,13 @@ snapshots: dependencies: yaml: 2.8.1 - '@platforma-sdk/model@1.83.17': + '@platforma-sdk/model@1.84.1': dependencies: '@milaboratories/helpers': 1.14.5 '@milaboratories/pl-error-like': 1.12.10 - '@milaboratories/pl-model-common': 1.49.0 - '@milaboratories/pl-model-middle-layer': 1.32.1 - '@milaboratories/ptabler-expression-js': 1.2.39 + '@milaboratories/pl-model-common': 1.51.0 + '@milaboratories/pl-model-middle-layer': 1.33.0 + '@milaboratories/ptabler-expression-js': 1.2.41 canonicalize: 2.1.0 es-toolkit: 1.39.10 fast-json-patch: 3.1.1 @@ -7251,22 +7251,22 @@ snapshots: transitivePeerDependencies: - aws-crt - '@platforma-sdk/tengo-builder@4.1.1': + '@platforma-sdk/tengo-builder@4.1.4': dependencies: - '@milaboratories/pl-model-backend': 1.4.27 + '@milaboratories/pl-model-backend': 1.4.29 '@milaboratories/resolve-helper': 1.1.3 '@milaboratories/tengo-tester': 1.6.4 '@milaboratories/ts-helpers': 1.8.6 commander: 15.0.0 winston: 3.17.0 - '@platforma-sdk/test@1.83.25(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.1))': + '@platforma-sdk/test@1.84.1(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2)(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.1))': dependencies: '@milaboratories/computable': 2.9.8 - '@milaboratories/pl-client': 3.17.1 - '@milaboratories/pl-middle-layer': 1.71.13(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2) - '@milaboratories/pl-tree': 1.15.2 - '@platforma-sdk/model': 1.83.17 + '@milaboratories/pl-client': 3.17.3 + '@milaboratories/pl-middle-layer': 1.72.0(@bytecodealliance/preview2-shim@0.17.8)(@types/node@24.5.2) + '@milaboratories/pl-tree': 1.15.4 + '@platforma-sdk/model': 1.84.1 '@vitest/coverage-istanbul': 4.1.5(vitest@4.1.5) vitest: 4.1.5(@types/node@24.5.2)(@vitest/coverage-istanbul@4.1.5(vitest@4.0.10(@types/node@24.5.2)(lightningcss@1.32.0)(yaml@2.8.1)))(vite@8.0.16(@types/node@24.5.2)(yaml@2.8.1)) transitivePeerDependencies: @@ -7288,13 +7288,13 @@ snapshots: - supports-color - vite - '@platforma-sdk/ui-vue@1.83.21(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.8.2)': + '@platforma-sdk/ui-vue@1.84.2(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.8.2)': dependencies: - '@milaboratories/columns-collection-driver': 0.2.5 - '@milaboratories/pf-spec-driver': 1.5.3(@bytecodealliance/preview2-shim@0.17.8) - '@milaboratories/pl-model-common': 1.49.0 - '@milaboratories/uikit': 2.15.30(typescript@5.8.2) - '@platforma-sdk/model': 1.83.17 + '@milaboratories/columns-collection-driver': 0.2.7 + '@milaboratories/pf-spec-driver': 1.5.5(@bytecodealliance/preview2-shim@0.17.8) + '@milaboratories/pl-model-common': 1.51.0 + '@milaboratories/uikit': 2.15.33(typescript@5.8.2) + '@platforma-sdk/model': 1.84.1 '@types/d3-format': 3.0.4 '@types/node': 24.5.2 '@types/semver': 7.7.0 @@ -7325,11 +7325,11 @@ snapshots: - typescript - universal-cookie - '@platforma-sdk/workflow-tengo@6.11.1': + '@platforma-sdk/workflow-tengo@6.12.1': dependencies: '@milaboratories/pframes-rs-wasip2': 1.1.61 '@milaboratories/software-pframes-conv': 2.2.9 - '@platforma-open/milaboratories.software-ptabler': 2.1.13 + '@platforma-open/milaboratories.software-ptabler': 2.1.14 '@platforma-open/milaboratories.software-ptexter': 1.2.6 '@platforma-open/milaboratories.software-small-binaries': 2.1.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a6aca72..70cb6e5 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,19 +9,19 @@ packages: - workflow catalog: - "@platforma-sdk/block-tools": 2.16.1 + "@platforma-sdk/block-tools": 2.16.3 "@platforma-sdk/block-kind": 1.1.0 - "@platforma-sdk/model": 1.83.17 - "@platforma-sdk/ui-vue": 1.83.21 - "@platforma-sdk/tengo-builder": 4.1.1 - "@platforma-sdk/workflow-tengo": 6.11.1 + "@platforma-sdk/model": 1.84.1 + "@platforma-sdk/ui-vue": 1.84.2 + "@platforma-sdk/tengo-builder": 4.1.4 + "@platforma-sdk/workflow-tengo": 6.12.1 "@platforma-sdk/blocks-deps-updater": 2.2.0 "@platforma-sdk/package-builder": 3.16.0 "@platforma-open/milaboratories.runenv-python-3": ^1.7.5 "@platforma-open/milaboratories.runenv-r-samples-and-data": ^1.0.1 - "@platforma-sdk/test": 1.83.25 + "@platforma-sdk/test": 1.84.1 "@milaboratories/helpers": 1.14.5 "@milaboratories/pl-model-common": 1.48.0 diff --git a/ui/src/dialogs/ImportDatasetDialog.vue b/ui/src/dialogs/ImportDatasetDialog.vue index 93f8527..20acafb 100644 --- a/ui/src/dialogs/ImportDatasetDialog.vue +++ b/ui/src/dialogs/ImportDatasetDialog.vue @@ -11,6 +11,7 @@ import type { DSContentSeurat, DSContentMultiSampleH5ad, DSContentMultiSampleSeurat, + DSContentTaggedAb1, DSContentTaggedFastq, DSContentTaggedXsv, DSContentXsv, @@ -426,6 +427,28 @@ function addTaggedXsvDatasetContent(contentData: DSContentTaggedXsv["data"]) { } } +/** Tagged AB1 */ +function addTaggedAb1DatasetContent(contentData: DSContentTaggedAb1["data"]) { + const pattern = compiledPattern.value; + if (!pattern) throw new Error("No pattern"); + + for (const f of parsedFiles.value) { + if (!f.match) continue; + const sample = f.match.sample.value; + const sampleId = getOrCreateSample(app, sample); + const tags = _.mapValues(f.match.tags!, (v) => v.value); + + let sampleRecords = contentData[sampleId]; + if (!sampleRecords) { + sampleRecords = []; + contentData[sampleId] = sampleRecords; + } + + if (!sampleRecords.some((r) => _.isEqual(r.tags, tags))) + sampleRecords.push({ tags, file: f.handle }); + } +} + /** CellRanger MTX */ function addCellRangerMtxDatasetContent(contentData: DSContentCellRangerMtx["data"]) { for (const f of parsedFiles.value) { @@ -578,6 +601,9 @@ async function addToExistingDataset() { case "TaggedXsv": addTaggedXsvDatasetContent(dataset.content.data); break; + case "TaggedAb1": + addTaggedAb1DatasetContent(dataset.content.data); + break; case "CellRangerMTX": addCellRangerMtxDatasetContent(dataset.content.data); break; @@ -642,6 +668,21 @@ async function createNewDataset() { }); break; } + case "TaggedAb1": { + const contentData: DSContentTaggedAb1["data"] = {}; + addTaggedAb1DatasetContent(contentData); + app.model.data.datasets.push({ + label: data.newDatasetLabel, + id: newDatasetId, + content: { + type: "TaggedAb1", + gzipped: data.gzipped, + tags: pattern.tags, + data: contentData, + }, + }); + break; + } case "Xsv": { const contentData: DSContentXsv["data"] = {}; addXsvDatasetContent(contentData); diff --git a/ui/src/dialogs/datasets.ts b/ui/src/dialogs/datasets.ts index af900ad..a618e49 100644 --- a/ui/src/dialogs/datasets.ts +++ b/ui/src/dialogs/datasets.ts @@ -49,6 +49,11 @@ export const datasetTypes: Record< fileType: "Xsv", hasTags: true, }, + TaggedAb1: { + label: "Tagged Sanger AB1", + fileType: "Ab1", + hasTags: true, + }, CellRangerMTX: { label: "CellRanger MTX", fileType: "CellRangerMTX", diff --git a/ui/src/dialogs/file_name_parser.test.ts b/ui/src/dialogs/file_name_parser.test.ts index 7f69801..a2740d7 100644 --- a/ui/src/dialogs/file_name_parser.test.ts +++ b/ui/src/dialogs/file_name_parser.test.ts @@ -85,6 +85,27 @@ test.for([ target: "FebControl10_sampled_R2.fastq.gz", match: undefined, }, + { + pattern: "{{Sample}}_{{:Primer}}.ab1", + target: "A1_Fwd.ab1", + match: { + sample: { value: "A1" }, + tags: { Primer: { value: "Fwd" } }, + }, + }, + { + pattern: "{{Sample}}_{{:Primer}}.ab1", + target: "H12_Rev.ab1", + match: { + sample: { value: "H12" }, + tags: { Primer: { value: "Rev" } }, + }, + }, + { + pattern: "{{Sample}}_{{:Primer}}.ab1", + target: "A1_Fwd.fastq.gz", + match: undefined, + }, ])("matching test for $pattern and $target", ({ pattern, target, match }, { expect }) => { const fileNamePattern = FileNamePattern.parse(pattern); const actualMatch = fileNamePattern.match(target); @@ -92,6 +113,56 @@ test.for([ else expect(actualMatch).to.toMatchObject(match); }); +test("infer ab1 pattern from a two-primer 96-well plate", ({ expect }) => { + const wells = []; + for (const row of ["A", "B", "C", "D", "E", "F", "G", "H"]) + for (let col = 1; col <= 12; col++) wells.push(`${row}${col}`); + + const fileNames = [...wells.map((w) => `${w}_Fwd.ab1`), ...wells.map((w) => `${w}_Rev.ab1`)]; + expect(fileNames.length).toBe(192); + + const inferred = inferFileNamePattern(fileNames); + expect(inferred).toBeDefined(); + expect(inferred!.pattern.rawPattern).toBe("{{Sample}}_{{:Primer}}.ab1"); + expect(inferred!.pattern.datasetType).toBe("TaggedAb1"); + expect(inferred!.pattern.tags).toEqual(["Primer"]); + + // 96 wells, not 192 — the primer must land in the tag, not in the sample name. + const samples = new Set(fileNames.map((f) => inferred!.pattern.match(f)!.sample.value)); + expect(samples.size).toBe(96); +}); + +test("infer ab1 pattern with a dash separator", ({ expect }) => { + const inferred = inferFileNamePattern(["A1-M13F.ab1", "A1-M13R.ab1", "B2-M13F.ab1"]); + expect(inferred?.pattern.rawPattern).toBe("{{Sample}}-{{:Primer}}.ab1"); + expect(inferred?.pattern.datasetType).toBe("TaggedAb1"); +}); + +test("adding tags to the dedup key leaves fastq inference alone", ({ expect }) => { + const inferred = inferFileNamePattern([ + "SampleA_L001_R1_001.fastq.gz", + "SampleA_L001_R2_001.fastq.gz", + "SampleB_L001_R1_001.fastq.gz", + "SampleB_L001_R2_001.fastq.gz", + ]); + expect(inferred?.pattern.rawPattern).toBe("{{Sample}}_L{{n}}_{{RR}}_{{n}}.fastq.gz"); + expect(inferred?.readIndices).toEqual(["R1", "R2"]); +}); + +test("ab1 pattern resolves to the TaggedAb1 dataset type", ({ expect }) => { + const pattern = FileNamePattern.parse("{{Sample}}_{{:Primer}}.ab1"); + expect(pattern.fileContentType).toBe("Ab1"); + expect(pattern.hasTagMatchers).toBe(true); + expect(pattern.tags).toEqual(["Primer"]); + expect(pattern.datasetType).toBe("TaggedAb1"); +}); + +test("ab1 pattern without tags has no dataset type", ({ expect }) => { + const pattern = FileNamePattern.parse("{{Sample}}.ab1"); + expect(pattern.fileContentType).toBe("Ab1"); + expect(pattern.datasetType).toBeUndefined(); +}); + test("wrapped string builder - pattern", ({ expect }) => { const fileNamePattern = FileNamePattern.parse("{{s}}{{R}}L{{n}}D{{*}}.fastq.gz"); const wrapped = fileNamePattern.buildFormattedPattern({ diff --git a/ui/src/dialogs/file_name_parser.ts b/ui/src/dialogs/file_name_parser.ts index 7a7a6c5..678fbaa 100644 --- a/ui/src/dialogs/file_name_parser.ts +++ b/ui/src/dialogs/file_name_parser.ts @@ -10,7 +10,8 @@ export type FileContentType = | "CellRangerMTX" | "H5AD" | "H5" - | "Seurat"; + | "Seurat" + | "Ab1"; function extractFileContentType(pattern: string): FileContentType { if (pattern.includes("CellRangerFileRole")) return "CellRangerMTX"; @@ -23,6 +24,7 @@ function extractFileContentType(pattern: string): FileContentType { else if (pt.endsWith("h5ad")) return "H5AD"; else if (pt.endsWith("h5")) return "H5"; else if (["rds", "RDS"].some((rds) => pt.endsWith(rds))) return "Seurat"; + else if (["ab1", "AB1"].some((ab1) => pt.endsWith(ab1))) return "Ab1"; else if ( pt.endsWith("matrix.mtx") || pt.endsWith("features.tsv") || @@ -122,6 +124,8 @@ export class FileNamePattern { return "H5"; case "Seurat": return "Seurat"; + case "Ab1": + return this.hasTagMatchers ? "TaggedAb1" : undefined; case "CellRangerMTX": return "CellRangerMTX"; default: @@ -483,6 +487,20 @@ const wellKnownPattern: WellKnownPattern[] = [ extensions: ["rds", "RDS"], minimalPercent: 0.9, }, + // One Sanger reaction is one template plus one primer, so a sample read with several + // primers yields one file per primer. The primer name is the usual separator. + { + patternWithoutExtension: "{{Sample}}_{{:Primer}}", + defaultReadIndices: [], + extensions: ["ab1", "AB1"], + minimalPercent: 0.9, + }, + { + patternWithoutExtension: "{{Sample}}-{{:Primer}}", + defaultReadIndices: [], + extensions: ["ab1", "AB1"], + minimalPercent: 0.9, + }, ]; export type InferFileNamePatternOps = { @@ -527,6 +545,9 @@ export function inferFileNamePattern( if (match.lane) sample += "___" + match.lane.value; if (match.readIndex) sample += "___" + match.readIndex.value; if (match.cellRangerFileRole) sample += "___" + match.cellRangerFileRole.value; + // Tags separate files of one sample just as lane and read index do. Without them a + // tagged candidate looks like a duplicate on its second file and is always rejected. + for (const tag of pattern.tags) sample += "___" + (match.tags?.[tag]?.value ?? ""); if (samples.has(sample)) continue outer; samples.add(sample); matchedFiles++; diff --git a/ui/src/pages/DatasetPage.vue b/ui/src/pages/DatasetPage.vue index f678aa1..a2d7c7a 100644 --- a/ui/src/pages/DatasetPage.vue +++ b/ui/src/pages/DatasetPage.vue @@ -16,6 +16,7 @@ import FastaDatasetPage from "../pages/FastaDatasetPage.vue"; import FastqDatasetPage from "../pages/FastqDatasetPage.vue"; import MultilaneFastqDatasetPage from "../pages/MultilaneFastqDatasetPage.vue"; import MultiplexedFastqDatasetPage from "../pages/MultiplexedFastqDatasetPage.vue"; +import TaggedAb1DatasetPage from "../pages/TaggedAb1DatasetPage.vue"; import TaggedFastqDatasetPage from "../pages/TaggedFastqDatasetPage.vue"; import TaggedXsvDatasetPage from "../pages/TaggedXsvDatasetPage.vue"; import XsvDatasetPage from "../pages/XsvDatasetPage.vue"; @@ -120,6 +121,9 @@ const datasetTypeLabel = datasetTypeLabels[dataset.content.type]; + diff --git a/ui/src/pages/TaggedAb1DatasetPage.vue b/ui/src/pages/TaggedAb1DatasetPage.vue new file mode 100644 index 0000000..460890a --- /dev/null +++ b/ui/src/pages/TaggedAb1DatasetPage.vue @@ -0,0 +1,111 @@ + + + diff --git a/workflow/package.json b/workflow/package.json index a689374..5630cdf 100644 --- a/workflow/package.json +++ b/workflow/package.json @@ -10,7 +10,7 @@ "scripts": { "build": "shx rm -rf dist && pl-tengo build", "format": "/usr/bin/env emacs --script ./format.el || echo 'No emacs.'", - "check": "pl-tengo check", + "check": "pl-tengo imports && pl-tengo check", "test": "pl-tengo test" }, "dependencies": { diff --git a/workflow/src/ds-bulk-count-matrix.lib.tengo b/workflow/src/ds-bulk-count-matrix.lib.tengo index 18b45f9..53c048f 100644 --- a/workflow/src/ds-bulk-count-matrix.lib.tengo +++ b/workflow/src/ds-bulk-count-matrix.lib.tengo @@ -1,5 +1,4 @@ ll := import("@platforma-sdk/workflow-tengo:ll") -pt := import("@platforma-sdk/workflow-tengo:pt") render := import("@platforma-sdk/workflow-tengo:render") assets := import("@platforma-sdk/workflow-tengo:assets") pColumn := import("@platforma-sdk/workflow-tengo:pframes.pcolumn") diff --git a/workflow/src/ds-multiplexed-fastq.lib.tengo b/workflow/src/ds-multiplexed-fastq.lib.tengo index 9c488e1..67b5664 100644 --- a/workflow/src/ds-multiplexed-fastq.lib.tengo +++ b/workflow/src/ds-multiplexed-fastq.lib.tengo @@ -2,7 +2,6 @@ ll := import("@platforma-sdk/workflow-tengo:ll") pColumn := import("@platforma-sdk/workflow-tengo:pframes.pcolumn") json := import("json") -smart := import("@platforma-sdk/workflow-tengo:smart") util := import(":util") createGroupAxis := func(groupIdAxis, _) { diff --git a/workflow/src/ds-tagged-ab1.lib.tengo b/workflow/src/ds-tagged-ab1.lib.tengo new file mode 100644 index 0000000..31fe24f --- /dev/null +++ b/workflow/src/ds-tagged-ab1.lib.tengo @@ -0,0 +1,61 @@ + +ll := import("@platforma-sdk/workflow-tengo:ll") +pColumn := import("@platforma-sdk/workflow-tengo:pframes.pcolumn") + +util := import(":util") + +createDataset := func(blockId, sampleIdAxis, dataset, importFile) { + + spec := util.datasetColumnSpecBase(blockId, dataset, "ab1") + + axesSpec := [sampleIdAxis] + + for tag in dataset.content.tags { + axesSpec = append(axesSpec, { + type: "String", + name: "pl7.app/sequencing/tag", + domain: { + "pl7.app/sequencing/tag": tag + }, + annotations: { + "pl7.app/label": tag + " tag" + } + }) + } + + spec.axesSpec = axesSpec + + data := pColumn.resourceMapBuilder(len(axesSpec)) + + for sampleId, records in dataset.content.data { + for record in records { + key := [sampleId] + for tag in dataset.content.tags { + tagValue := record.tags[tag] + if !tagValue { + ll.panic("No tag value for %v in sample %v", tag, sampleId) + } + key = append(key, tagValue) + } + + importHandle := record.file + if !importHandle { + ll.panic("File handle not set for key %v", key) + } + data.add(key, importFile(importHandle)) + } + } + + result := {} + result["dataset." + dataset.id] = { + spec: spec, + data: data.build() + } + + return result +} + +export { + isGrouped: false, + createDataset: createDataset +} diff --git a/workflow/src/ds-tagged-xsv.lib.tengo b/workflow/src/ds-tagged-xsv.lib.tengo index ac03897..fd552c7 100644 --- a/workflow/src/ds-tagged-xsv.lib.tengo +++ b/workflow/src/ds-tagged-xsv.lib.tengo @@ -1,7 +1,6 @@ ll := import("@platforma-sdk/workflow-tengo:ll") pColumn := import("@platforma-sdk/workflow-tengo:pframes.pcolumn") -maps := import("@platforma-sdk/workflow-tengo:maps") util := import(":util") diff --git a/workflow/src/main.tpl.tengo b/workflow/src/main.tpl.tengo index 8cc9532..9af4ff1 100644 --- a/workflow/src/main.tpl.tengo +++ b/workflow/src/main.tpl.tengo @@ -6,7 +6,6 @@ file := import("@platforma-sdk/workflow-tengo:file") pSpec := import("@platforma-sdk/workflow-tengo:pframes.spec") pframes := import("@platforma-sdk/workflow-tengo:pframes") metadata := import(":metadata") -smart := import("@platforma-sdk/workflow-tengo:smart") assets := import("@platforma-sdk/workflow-tengo:assets") util := import(":util") @@ -18,6 +17,7 @@ dsMultilaneFastq := import(":ds-multilane-fastq") dsTaggedFastq := import(":ds-tagged-fastq") dsXsv := import(":ds-xsv") dsTaggedXsv := import(":ds-tagged-xsv") +dsTaggedAb1 := import(":ds-tagged-ab1") dsCellRangerMtx := import(":ds-cellranger-mtx") dsBulkCountMatrix := import(":ds-bulk-count-matrix") dsMultiplexedFastq := import(":ds-multiplexed-fastq") @@ -34,6 +34,7 @@ dsProcessors := { "TaggedFastq": dsTaggedFastq, "Xsv": dsXsv, "TaggedXsv": dsTaggedXsv, + "TaggedAb1": dsTaggedAb1, "H5AD": dsH5ad, "H5": dsH5, "Seurat": dsSeurat, diff --git a/workflow/src/prerun.tpl.tengo b/workflow/src/prerun.tpl.tengo index c912fac..1c43b6a 100644 --- a/workflow/src/prerun.tpl.tengo +++ b/workflow/src/prerun.tpl.tengo @@ -1,8 +1,6 @@ wf := import("@platforma-sdk/workflow-tengo:workflow") file := import("@platforma-sdk/workflow-tengo:file") maps := import("@platforma-sdk/workflow-tengo:maps") -smart := import("@platforma-sdk/workflow-tengo:smart") -ll := import("@platforma-sdk/workflow-tengo:ll") dsBulkCountMatrix := import(":ds-bulk-count-matrix") multiSampleH5AD := import(":ds-multisample-h5ad") From fc7a2b27208ba0f363afc4fda67386ccb3dc9c55 Mon Sep 17 00:00:00 2001 From: Mariia Zueva Date: Fri, 25 Sep 2026 12:53:03 +0200 Subject: [PATCH 2/2] MILAB-7021 Sanger Ab1 files support - review fixes --- ui/src/dialogs/ImportDatasetDialog.vue | 14 +++++++++++++- ui/src/dialogs/file_name_parser.test.ts | 6 ++++++ ui/src/dialogs/file_name_parser.ts | 7 +++++-- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/ui/src/dialogs/ImportDatasetDialog.vue b/ui/src/dialogs/ImportDatasetDialog.vue index 20acafb..1c829cd 100644 --- a/ui/src/dialogs/ImportDatasetDialog.vue +++ b/ui/src/dialogs/ImportDatasetDialog.vue @@ -187,6 +187,17 @@ watch(isOneOfDialogsOpened, (v) => { // Pattern compilation and file name matching const { patternError, compiledPattern } = usePatternCompilation(data); +/** Records are stored under the target dataset's tag names; the workflow reads them by those names. */ +const tagMismatchError = computed(() => { + if (data.mode !== "add-to-existing") return undefined; + const ds = app.model.data.datasets.find((ds) => ds.id === data.targetAddDataset); + const pattern = compiledPattern.value; + if (!ds || !pattern || !("tags" in ds.content)) return undefined; + const expected = [...ds.content.tags].sort(); + if (_.isEqual(pattern.tags, expected)) return undefined; + return `Pattern tags must match the dataset tags: ${expected.join(", ")}`; +}); + function updateDataFromPattern(v: FileNamePattern | undefined) { if (v && !addingToFixedDataset.value) { data.datasetType = v.datasetType; @@ -970,6 +981,7 @@ watch(availableColumnsOptions, (options) => { const canCreateOrAdd = computed(() => { const basicConditions = hasMatchedFiles.value && + tagMismatchError.value === undefined && (data.mode === "create-new-dataset" || data.targetAddDataset !== undefined) && data.datasetType !== undefined && !data.loadingColumns && @@ -1025,7 +1037,7 @@ const canCreateOrAdd = computed(() => { Gzipped - +
Parsing files to extract column information...
diff --git a/ui/src/dialogs/file_name_parser.test.ts b/ui/src/dialogs/file_name_parser.test.ts index a2740d7..d8c54d9 100644 --- a/ui/src/dialogs/file_name_parser.test.ts +++ b/ui/src/dialogs/file_name_parser.test.ts @@ -157,6 +157,12 @@ test("ab1 pattern resolves to the TaggedAb1 dataset type", ({ expect }) => { expect(pattern.datasetType).toBe("TaggedAb1"); }); +test("compressed ab1 pattern is rejected", ({ expect }) => { + expect(() => FileNamePattern.parse("{{Sample}}_{{:Primer}}.ab1.gz")).toThrow( + "Compressed AB1 files are not supported", + ); +}); + test("ab1 pattern without tags has no dataset type", ({ expect }) => { const pattern = FileNamePattern.parse("{{Sample}}.ab1"); expect(pattern.fileContentType).toBe("Ab1"); diff --git a/ui/src/dialogs/file_name_parser.ts b/ui/src/dialogs/file_name_parser.ts index 678fbaa..c32f887 100644 --- a/ui/src/dialogs/file_name_parser.ts +++ b/ui/src/dialogs/file_name_parser.ts @@ -24,8 +24,11 @@ function extractFileContentType(pattern: string): FileContentType { else if (pt.endsWith("h5ad")) return "H5AD"; else if (pt.endsWith("h5")) return "H5"; else if (["rds", "RDS"].some((rds) => pt.endsWith(rds))) return "Seurat"; - else if (["ab1", "AB1"].some((ab1) => pt.endsWith(ab1))) return "Ab1"; - else if ( + else if (["ab1", "AB1"].some((ab1) => pt.endsWith(ab1))) { + // Readers of the dataset column expect a raw trace, so there is no ab1.gz to publish. + if (pt !== pattern) throw new Error("Compressed AB1 files are not supported"); + return "Ab1"; + } else if ( pt.endsWith("matrix.mtx") || pt.endsWith("features.tsv") || pt.endsWith("genes.tsv") ||