Skip to content

Commit

Permalink
wip: test passes
Browse files Browse the repository at this point in the history
  • Loading branch information
dbolotin committed Aug 23, 2024
1 parent dc5f7c6 commit d5a1fda
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 25 deletions.
4 changes: 4 additions & 0 deletions model/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export const platforma = BlockModel.create<BlockArgs>('Heavy')
ctx.precalc?.resolve({ field: 'presets', assertFieldType: 'Input' })?.getFileHandle()
)

.output('preset', (ctx) =>
ctx.precalc?.resolve({ field: 'preset', assertFieldType: 'Input' })?.getDataAsString()
)

.output('qc', (ctx) =>
ctx.outputs?.resolve({ field: 'qc', assertFieldType: 'Input' })?.getDataAsJson()
)
Expand Down
23 changes: 19 additions & 4 deletions test/src/wf.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ import * as tp from 'node:timers/promises';

blockTest('empty imputs', { timeout: 10000 }, async ({ rawPrj: project, ml, helpers, expect }) => {
const blockId = await project.addBlock('Block', myBlockSpec);
await project.runBlock(blockId);
const stableState = await helpers.awaitBlockDoneAndGetStableBlockState<typeof platforma>(
blockId,
const stableState = await awaitStableState(
project.getBlockState(blockId),
5000
);
) as InferBlockState<typeof platforma>;
expect(stableState.outputs).toMatchObject({ inputOptions: { ok: true, value: [] } });
const presetsOutput = wrapOutputs(stableState.outputs).presets;

Check failure on line 21 in test/src/wf.test.ts

View workflow job for this annotation

GitHub Actions / run / unified (build test publish)

src/wf.test.ts > empty imputs

Error: 1 errors, first error: F [Error]: error in field presets of NG:0xc4: {"errorType":"","message":"resource \"NG:0xD1\" has input errors:\nfield \"NG:0xD1/blob\": resource \"NG:0xD0\" has input errors:\nfield \"NG:0xD0/resource\": resource \"NG:0xDD\" has input errors:\nfield \"NG:0xDD/workdirIn\": failed to run command: \"/usr/bin/env\" exited with code 127.\nHere is the latest command output:\n\tenv: 'mixcr': No such file or directory\n"} at resolveWithCommon (bundle.js:4048) at resolve (bundle.js:4045) at <anonymous> (bundle.js:4474) at <anonymous> (bundle.js) Host: QuickJSUnwrapError: at P.unwrapResult (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/[email protected]/node_modules/quickjs-emscripten-core/src/context.ts:1023:27) at block (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/@milaboratory[email protected]_@[email protected]/node_modules/@milaboratory/pl-middle-layer/src/js_render/context.ts:108:19) at Function.withScope (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/[email protected]/node_modules/quickjs-emscripten-core/src/lifetime.ts:251:14) at _JsExecutionContext.runCallback (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/@milaboratory[email protected]_@[email protected]/node_modules/@milaboratory/pl-middle-layer/src/js_render/context.ts:101:20) at Object.___kernel___ (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/@milaboratory[email protected]_@[email protected]/node_modules/@milaboratory/pl-middle-layer/src/js_render/index.ts:26:25) at renderSelfState (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/@milaboratory[email protected]/node_modules/@milaboratory/computable/src/computable/computable_state.ts:384:28) at updateCellStateWithoutValue (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/@milaboratory[email protected]/node_modules/@milaboratory/computable/src/computable/computable_state.ts:566:7) at calculateChildren (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/@milaboratory[email protected]/node_modules/@milaboratory/computable/src/computable/computable_state.ts:420:16) at updateCellStateWithoutValue (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/@milaboratory[email protected]/node_modules/@milaboratory/computable/src/computable/computable_state.ts:571:26) at calculateChildren (/home/runner/work/block-mixcr-clonotyping/block-mixcr-clonotyping/node_modules/.pnpm/@milaboratory[email protected]/node_modules/@milaboratory/computable/src/computable/computable_state.ts:420:16) { cause: { name: 'Error', message: `error in field presets of NG:0xc4: {"errorType":"","message":"resource \\"NG:0xD1\\" has input errors:\\nfield \\"NG:0xD1/blob\\": resource \\"NG:0xD0\\" has input errors:\\nfield \\"NG:0xD0/resource\\": resource \\"NG:0xDD\\" has input errors:\\nfield \\"NG:0xDD/workdirIn\\": failed to run command: \\"/usr/bin/env\\" exited with code 127.\\nHere is the latest command output:\\n\\tenv: 'mixcr': No such file or directory\\n"}`, stack: ' at resolveWithCommon (bundle.js:4048)\n' + ' at resolve (bundle.js:4045)\n' + ' at <anonymous> (bundle.js:4474)\n' + ' at <anonymous> (bundle.js)\n' } } ❯ P.unwrapResult ../node_modules/.pnpm/[email protected]/node_modules/quickjs-emscripten-core/src/context.ts:1023:27 ❯ block ../node_modules/.pnpm/@milaboratory[email protected]_@[email protected]/node_modules/@milaboratory/pl-middle-layer/src/js_render/context.ts:108:19 ❯ Function.withScope ../node_modules/.pnpm/[email protected]/node_modules/quickjs-emscripten-core/src/lifetime.ts:251:14 ❯ _JsExecutionContext.runCallback ../node_modules/.pnpm/@milabora
const presetsStr = Buffer.from(
Expand All @@ -27,6 +26,22 @@ blockTest('empty imputs', { timeout: 10000 }, async ({ rawPrj: project, ml, help
expect(presets).length.gt(10);
});

blockTest(
'preset content',
{ timeout: 10000 },
async ({ rawPrj: project, ml, helpers, expect }) => {
const blockId = await project.addBlock('Block', myBlockSpec);
await project.setBlockArgs(blockId, {
preset: 'milab-human-dna-xcr-7genes-multiplex'
} satisfies BlockArgs);
const stableState = await awaitStableState(
project.getBlockState(blockId),
5000
) as InferBlockState<typeof platforma>;
expect(stableState.outputs).toMatchObject({ preset: { ok: true } });

Check failure on line 41 in test/src/wf.test.ts

View workflow job for this annotation

GitHub Actions / run / unified (build test publish)

src/wf.test.ts > preset content

AssertionError: expected { …(4) } to match object { preset: { ok: true } } (12 matching properties omitted from actual) - Expected + Received Object { "preset": Object { - "ok": true, + "ok": false, }, } ❯ src/wf.test.ts:41:33
}
);

blockTest(
'simple project',
{ timeout: 20000 },
Expand Down
2 changes: 1 addition & 1 deletion workflow/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"type": "module",
"description": "Tengo-based template",
"scripts": {
"build": "rm -rf dist && pl-tengo check && pl-tengo build",
"build": "node ./scripts/build-static.mjs src/pfconv_params.json src/pfconv_params.lib.tengo && rm -rf dist && pl-tengo check && pl-tengo build",
"format": "/usr/bin/env emacs --script ./format.el"
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions workflow/scripts/build-static.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import fs from 'node:fs/promises'

const jsonContent = await fs.readFile(process.argv[2])
fs.writeFile(process.argv[3], "export " + jsonContent)
16 changes: 16 additions & 0 deletions workflow/src/calculate-pfconv-params.tpl.tengo
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// calculate-pfconv-params

self := import("@milaboratory/tengo-sdk:tpl")

pvconfParams := import(":pfconv_params")

self.defineOutputs("params")

self.body(func(inputs) {
// preset content
preset := inputs.preset

return {
params: pvconfParams
}
})
31 changes: 31 additions & 0 deletions workflow/src/get-preset.tpl.tengo
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// get preset

self := import("@milaboratory/tengo-sdk:tpl")
ll := import("@milaboratory/tengo-sdk:ll")
exec := import("@milaboratory/tengo-sdk:exec")

self.defineOutputs("preset")

self.body(func(inputs) {
preset := inputs.preset

if !is_string(preset) {
ll.panic("preset is not string: %v", preset)
}

mixcrCmd := exec.builder().
printErrStreamToStdout().
cmd("mixcr").
arg("exportPreset").
arg("--preset-name").
arg(preset).
arg("preset.json").
saveFileContent("preset.json").
run()

presetContent := mixcrCmd.getFileContent("preset.json")

return {
preset: presetContent
}
})
22 changes: 22 additions & 0 deletions workflow/src/list-presets.tpl.tengo
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// list presets

self := import("@milaboratory/tengo-sdk:tpl")
ll := import("@milaboratory/tengo-sdk:ll")
exec := import("@milaboratory/tengo-sdk:exec")
file := import("@milaboratory/tengo-sdk:file")

self.defineOutputs("presets")

self.body(func(inputs) {
listPresets := exec.builder().
cmd("mixcr").
arg("listPresetSpecificationsForUI").
arg("presets.json").
inUiQueue().
saveFile("presets.json").
run()

return {
presets: file.exportFile(listPresets.getFile("presets.json"))
}
})
19 changes: 17 additions & 2 deletions workflow/src/main.tpl.tengo
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ ll := import("@milaboratory/tengo-sdk:ll")
smart := import("@milaboratory/tengo-sdk:smart")
file := import("@milaboratory/tengo-sdk:file")
llPFrames := import("@milaboratory/tengo-sdk:pframes.ll")
exec := import("@milaboratory/tengo-sdk:exec")

json := import("json")

runMixcrTpl := ll.importTemplate(":run-mixcr")
getPresetTpl := ll.importTemplate(":get-preset")
calculatePfconvParamsTpl := ll.importTemplate(":calculate-pfconv-params")
processTpl := ll.importTemplate(":process")

wf.setPreRun(ll.importTemplate(":prerun"))

Expand All @@ -21,10 +24,22 @@ wf.body(func(args) {

input := wf.resolve(inputRef)

runMixcr := render.createEphemeral(runMixcrTpl, {
getPreset := render.create(getPresetTpl, {
preset: args.preset
})
presetContent := getPreset.output("preset", 24 * 60 * 60 * 1000)

calculatePfconvParams := render.create(calculatePfconvParamsTpl, {
preset: presetContent
})
pfconvParams := calculatePfconvParams.output("params", 24 * 60 * 60 * 1000)

runMixcr := render.createEphemeral(processTpl, {
params: smart.createJsonResource({
preset: preset
}),
presetContent: presetContent,
pfconvParams: pfconvParams,
inputSpec: input.getFutureInputField("spec"),
inputData: input.getFutureInputField("data")
})
Expand Down
13 changes: 12 additions & 1 deletion workflow/src/mixcr-analyze.tpl.tengo
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,22 @@ self.defineOutputs("qc")

self.body(func(inputs) {
inputData := inputs[pConstants.VALUE_FIELD_NAME]
params := inputs.params

params := inputs.params
preset := params.preset
fileExtension := params.fileExtension

presetContent := inputs.presetContent.getDataAsJson()
pfconvParams := inputs.pfconvParams // already in JSON

if !is_map(presetContent) {
ll.panic("malformed presetContent %v", presetContent)
}

if !is_map(pfconvParams) {
ll.panic("malformed pfconvParams %v", pfconvParams)
}

inputDataMeta := inputData.getDataAsJson()

if inputDataMeta.keyLength == 1 {
Expand Down
File renamed without changes.
36 changes: 36 additions & 0 deletions workflow/src/pfconv_params.lib.tengo
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export {
"axes": [
{
"column": "cloneId",
"spec": {
"name": "pl7.app/vdj/cloneId",
"type": "Long"
}
}
],
"columns": [
{
"column": "readCount",
"id": "read-count",
"name": "pl7.app/vdj/readCount",
"type": "Long",
"annotations": {
"pl7.app/label": "Number Of Reads"
}
},
{
"column": "nSeqCDR3",
"id": "n-seq-cdr3",
"type": "String",
"name": "pl7.app/vdj/nucleotideSequence",
"domain": {
"pl7.app/vdj/feature": "CDR3"
},
"annotations": {
"pl7.app/label": "CDR3 Nucleotide Sequence"
}
}
],
"storageFormat": "Binary",
"partitionKeyLength": 0
}
32 changes: 19 additions & 13 deletions workflow/src/prerun.tpl.tengo
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
// pre-run

wf := import("@milaboratory/tengo-sdk:workflow")
exec := import("@milaboratory/tengo-sdk:exec")
file := import("@milaboratory/tengo-sdk:file")
render := import("@milaboratory/tengo-sdk:render")
ll := import("@milaboratory/tengo-sdk:ll")

listPresetsTpl := ll.importTemplate(":list-presets")
getPresetTpl := ll.importTemplate(":get-preset")

wf.body(func(args) {

listPresets := exec.builder().
cmd("mixcr").
arg("listPresetSpecificationsForUI").
arg("presets.json").
inUiQueue().
cache(24 * 60 * 60 * 1000).
saveFile("presets.json").
run()
outputs := {}

listPresets := render.create(listPresetsTpl, {})
outputs.presets = listPresets.output("presets", 24 * 60 * 60 * 1000)

if is_string(args.preset) {
getPreset := render.create(getPresetTpl, {
preset: args.preset
})
outputs.preset = getPreset.output("preset", 24 * 60 * 60 * 1000)
}

return {
outputs: {
presets: file.exportFile(listPresets.getFile("presets.json"))
},
outputs: outputs,
exports: {}
}
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// run-mixcr
// process

self := import("@milaboratory/tengo-sdk:tpl")

Expand All @@ -9,7 +9,7 @@ llPFrames := import("@milaboratory/tengo-sdk:pframes.ll")

json := import("json")

mixcrAggTpl := ll.importTemplate(":mixcr-analyze")
mixcrAnalyzeTpl := ll.importTemplate(":mixcr-analyze")

self.awaitState("InputsLocked")
self.awaitState("params", "ResourceReady")
Expand All @@ -20,17 +20,30 @@ self.body(func(inputs) {
preset := params.preset
inputSpec := self.rawInputs().inputSpec.getValue().getDataAsJson()

presetContent := self.rawInputs().presetContent
pfconvParams := self.rawInputs().pfconvParams

if is_undefined(presetContent) {
ll.panic("no presetContent")
}

if is_undefined(pfconvParams) {
ll.panic("no pfconvParams")
}

fileExtension := inputSpec.domain["pl7.app/fileExtension"]

mixcrResults := llPFrames.aggregate(
self.rawInputs().inputData, [1], mixcrAggTpl,
self.rawInputs().inputData, [1], mixcrAnalyzeTpl,
["qc"],
false,
{
params: smart.createJsonResource({
preset: preset,
fileExtension: fileExtension
})
}),
presetContent: presetContent,
pfconvParams: pfconvParams
}
)

Expand Down

0 comments on commit d5a1fda

Please sign in to comment.