Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/structurer-v3-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@platforma-open/milaboratories.vj-usage.model": minor
"@platforma-open/milaboratories.vj-usage.ui": minor
"@platforma-open/milaboratories.vj-usage.workflow": patch
"@platforma-open/milaboratories.vj-usage": minor
---

Migrate onto the block-tools structurer (full SDK upgrade: model/ui-vue 1.79.14,
workflow-tengo 6.6.3, tengo-builder 4.0.8) and BlockModelV3.

Persisted state is preserved via the legacy upgrader. UI bindings move to
`app.model.data`. The three plot view states, `weightedFlag`, and the derived
block label are now UI-only and no longer stale the block; only the dataset,
allele/gene toggle, single-cell chain, and custom block label do. Graphs remain
the standard `<GraphMaker>` component.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ log/
.turbo
vite.config.*.timestamp-*
software/**/*.sw.json
software/*.tgz
software/*.tgz
software/**/*.tgz
.DS_Store
.vscode/sftp.json
test-dry-run.json
1 change: 1 addition & 0 deletions .structure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":1}
19 changes: 1 addition & 18 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
{
"vitest.disableWorkspaceWarning": true,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[vue]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"cSpell.words": [
"datasource",
"pframe",
"prerun"
],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
},
"eslint.enable": true,
"eslint.format.enable": true
"typescript.tsdk": "node_modules/typescript/lib"
}
6 changes: 5 additions & 1 deletion block/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export {};
declare const blockSpec: {
type: "dev-v2";
folder: string;
};

export { blockSpec };
8 changes: 7 additions & 1 deletion block/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
export {};
const blockSpec = {
type: "dev-v2",
folder: __dirname,
};

module.exports = {
blockSpec,
};
23 changes: 12 additions & 11 deletions block/package.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
{
"name": "@platforma-open/milaboratories.vj-usage",
"version": "2.1.19",
"files": [
"index.d.ts",
"index.js"
],
"scripts": {
"build": "shx rm -rf ./block-pack && block-tools pack",
"mark-stable": "block-tools mark-stable -r 's3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1'",
"prepublishOnly": "block-tools pack && block-tools publish -r 's3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1'",
"do-pack": "shx rm -f *.tgz && block-tools pack && pnpm pack && shx mv *.tgz package.tgz"
},
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@platforma-open/milaboratories.vj-usage.workflow": "workspace:*",
"@platforma-open/milaboratories.vj-usage.model": "workspace:*",
"@platforma-open/milaboratories.vj-usage.ui": "workspace:*"
"@platforma-open/milaboratories.vj-usage.ui": "workspace:*",
"@platforma-open/milaboratories.vj-usage.workflow": "workspace:*"
},
"devDependencies": {
"@platforma-sdk/block-tools": "catalog:",
"shx": "catalog:"
},
"packageManager": "pnpm@9.12.0",
"block": {
"components": {
"workflow": "@platforma-open/milaboratories.vj-usage.workflow/dist/tengo/tpl/main.plj.gz",
Expand All @@ -42,9 +47,5 @@
},
"marketplaceRanking": 16300
}
},
"devDependencies": {
"@platforma-sdk/block-tools": "catalog:"
},
"packageManager": "pnpm@9.12.0"
}
}
4 changes: 4 additions & 0 deletions model/.oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": ["node_modules/@milaboratories/ts-builder/configs/oxfmt.json"],
"ignorePatterns": ["dist", "coverage", "CHANGELOG.md"]
}
3 changes: 3 additions & 0 deletions model/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["node_modules/@milaboratories/ts-builder/dist/configs/oxlint-block-model.json"]
}
4 changes: 0 additions & 4 deletions model/eslint.config.mjs

This file was deleted.

30 changes: 16 additions & 14 deletions model/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,36 @@
"version": "2.1.7",
"description": "Block model",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js",
"sources": "./src/index.ts"
"sources": "./src/index.ts",
"import": "./dist/index.js"
},
"./dist/model.json": "./dist/model.json"
"./dist/*": "./dist/*"
},
"scripts": {
"build": "ts-builder build --target block-model && block-tools build-model",
"watch": "ts-builder build --target block-model --watch",
"type-check": "ts-builder types --target block-model",
"lint": "eslint ."
"fmt": "ts-builder format",
"check": "ts-builder check --target block-model"
},
"dependencies": {
"@platforma-sdk/model": "catalog:",
"@milaboratories/graph-maker": "catalog:"
"@milaboratories/graph-maker": "catalog:",
"@platforma-sdk/model": "catalog:"
},
"devDependencies": {
"@milaboratories/ts-builder": "catalog:",
"@milaboratories/ts-configs": "catalog:",
"@platforma-sdk/block-tools": "catalog:",
"@platforma-sdk/eslint-config": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
"eslint": "catalog:"
},
"peerDependencies": {
"@types/node": "*",
"typescript": "*"
}
}
}
44 changes: 44 additions & 0 deletions model/src/dataModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { GraphMakerState } from "@milaboratories/graph-maker";
import { DataModelBuilder } from "@platforma-sdk/model";
import { getDefaultBlockLabel } from "./label";
import type { BlockData, LegacyBlockArgs, LegacyUiState } from "./types";

const usagePlot = (title: string, currentTab: GraphMakerState["currentTab"]): GraphMakerState => ({
title,
template: "heatmapClustered",
currentTab,
layersSettings: { heatmapClustered: { normalizationDirection: null } },
});

const initData = (): BlockData => ({
datasetRef: undefined,
scChain: "A",
allele: false,
defaultBlockLabel: getDefaultBlockLabel({ allele: false, isSingleCell: false }),
customBlockLabel: "",
weightedFlag: true,
vUsagePlotState: usagePlot("V Usage", "settings"),
jUsagePlotState: usagePlot("J Usage", null),
vjUsagePlotState: usagePlot("V/J Usage", null),
});

export const blockDataModel = new DataModelBuilder()
.from<BlockData>("v1")
// V1 split analysis params + block labels across `args`; the three plot states
// and `weightedFlag` lived under `uiState`. Fold both into unified `data`.
.upgradeLegacy<LegacyBlockArgs, LegacyUiState>(({ args, uiState }) => {
const allele = args?.allele ?? false;
return {
datasetRef: args?.datasetRef,
scChain: args?.scChain ?? "A",
allele,
defaultBlockLabel:
args?.defaultBlockLabel ?? getDefaultBlockLabel({ allele, isSingleCell: false }),
customBlockLabel: args?.customBlockLabel ?? "",
weightedFlag: uiState?.weightedFlag ?? true,
vUsagePlotState: uiState?.vUsagePlotState ?? usagePlot("V Usage", "settings"),
jUsagePlotState: uiState?.jUsagePlotState ?? usagePlot("J Usage", null),
vjUsagePlotState: uiState?.vjUsagePlotState ?? usagePlot("V/J Usage", null),
};
})
.init(initData);
144 changes: 47 additions & 97 deletions model/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,122 +1,72 @@
import type { GraphMakerState } from '@milaboratories/graph-maker';
import type { InferOutputsType, PlRef } from '@platforma-sdk/model';
import { BlockModel, createPFrameForGraphs } from '@platforma-sdk/model';
import { getDefaultBlockLabel } from './label';

export type BlockArgs = {
defaultBlockLabel: string;
customBlockLabel: string;
datasetRef?: PlRef;
scChain?: string;
allele?: boolean;
};

export type UiState = {
weightedFlag: boolean;
vUsagePlotState: GraphMakerState;
jUsagePlotState: GraphMakerState;
vjUsagePlotState: GraphMakerState;
};

export const model = BlockModel.create()

.withArgs<BlockArgs>({
defaultBlockLabel: getDefaultBlockLabel({
allele: false,
isSingleCell: false,
}),
customBlockLabel: '',
scChain: 'A',
allele: false,
import type { InferOutputsType } from "@platforma-sdk/model";
import { BlockModelV3, createPFrameForGraphs } from "@platforma-sdk/model";
import { blockDataModel } from "./dataModel";
import type { BlockArgs } from "./types";

export { getDefaultBlockLabel } from "./label";
export { blockDataModel } from "./dataModel";
Comment thread
vadimpiven marked this conversation as resolved.
export type { BlockArgs, BlockData } from "./types";

export const platforma = BlockModelV3.create(blockDataModel)
.args<BlockArgs>((data) => {
if (data.datasetRef === undefined) throw new Error("Dataset is required");
Comment thread
vadimpiven marked this conversation as resolved.
return {
datasetRef: data.datasetRef,
scChain: data.scChain,
allele: data.allele,
customBlockLabel: data.customBlockLabel,
};
})

.withUiState<UiState>({
weightedFlag: true,
vUsagePlotState: {
title: 'V Usage',
template: 'heatmapClustered',
currentTab: 'settings',
layersSettings: {
heatmapClustered: {
normalizationDirection: null,
.output("datasetOptions", (ctx) =>
ctx.resultPool.getOptions(
[
{
axes: [{ name: "pl7.app/sampleId" }, { name: "pl7.app/vdj/clonotypeKey" }],
annotations: { "pl7.app/isAnchor": "true" },
},
},
},
jUsagePlotState: {
title: 'J Usage',
template: 'heatmapClustered',
currentTab: null,
layersSettings: {
heatmapClustered: {
normalizationDirection: null,
},
},
},
vjUsagePlotState: {
title: 'V/J Usage',
template: 'heatmapClustered',
currentTab: null,
layersSettings: {
heatmapClustered: {
normalizationDirection: null,
{
axes: [{ name: "pl7.app/sampleId" }, { name: "pl7.app/vdj/scClonotypeKey" }],
annotations: { "pl7.app/isAnchor": "true" },
},
},
},
})

.argsValid((ctx) => ctx.args.datasetRef !== undefined)

.output('datasetOptions', (ctx) =>
ctx.resultPool.getOptions([{
axes: [
{ name: 'pl7.app/sampleId' },
{ name: 'pl7.app/vdj/clonotypeKey' },
],
annotations: { 'pl7.app/isAnchor': 'true' },
}, {
axes: [
{ name: 'pl7.app/sampleId' },
{ name: 'pl7.app/vdj/scClonotypeKey' },
],
annotations: { 'pl7.app/isAnchor': 'true' },
}],
{
// suppress native label of the column (e.g. "Number of Reads") to show only the dataset label
label: { includeNativeLabel: false },
}),
{
// suppress native label of the column (e.g. "Number of Reads") to show only the dataset label
label: { includeNativeLabel: false },
},
),
)

.output('datasetSpec', (ctx) => {
if (ctx.args.datasetRef === undefined) {
.output("datasetSpec", (ctx) => {
if (ctx.data.datasetRef === undefined) {
return undefined;
}
Comment thread
vadimpiven marked this conversation as resolved.

return ctx.resultPool.getPColumnSpecByRef(ctx.args.datasetRef);
return ctx.resultPool.getPColumnSpecByRef(ctx.data.datasetRef);
})

.outputWithStatus('pf', (ctx) => {
const pCols = ctx.outputs?.resolve('pf')?.getPColumns();
.outputWithStatus("pf", (ctx) => {
const pCols = ctx.outputs?.resolve("pf")?.getPColumns();
if (pCols === undefined) {
return undefined;
}

return createPFrameForGraphs(ctx, pCols);
})

.output('isRunning', (ctx) => ctx.outputs?.getIsReadyOrError() === false)
.output("isRunning", (ctx) => ctx.outputs?.getIsReadyOrError() === false)

.title(() => 'V/J Usage')
.title(() => "V/J Usage")

.subtitle((ctx) => ctx.args.customBlockLabel || ctx.args.defaultBlockLabel)
.subtitle((ctx) => ctx.data.customBlockLabel || ctx.data.defaultBlockLabel)

.sections((_) => [
{ type: 'link', href: '/', label: 'V Gene Usage' },
{ type: 'link', href: '/jUsage', label: 'J Gene Usage' },
{ type: 'link', href: '/vjUsage', label: 'V/J Gene Usage' },
.sections(() => [
{ type: "link" as const, href: "/" as const, label: "V Gene Usage" },
{ type: "link" as const, href: "/jUsage" as const, label: "J Gene Usage" },
{ type: "link" as const, href: "/vjUsage" as const, label: "V/J Gene Usage" },
])

.done(2);

export type BlockOutputs = InferOutputsType<typeof model>;
.done();

export { getDefaultBlockLabel } from './label';
export type Platforma = typeof platforma;
export type BlockOutputs = InferOutputsType<typeof platforma>;
Loading
Loading