@platforma-open/milaboratories.mixcr-clonotyping-2.workflow 2.25.0 → 3.0.0

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.
@@ -1,6 +1,6 @@
1
1
   WARN  Issue while reading "/home/runner/work/mixcr-clonotyping/mixcr-clonotyping/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @platforma-open/milaboratories.mixcr-clonotyping-2.workflow@2.25.0 build /home/runner/work/mixcr-clonotyping/mixcr-clonotyping/workflow
3
+ > @platforma-open/milaboratories.mixcr-clonotyping-2.workflow@3.0.0 build /home/runner/work/mixcr-clonotyping/mixcr-clonotyping/workflow
4
4
  > rm -rf dist && pl-tengo check && pl-tengo build
5
5
 
6
6
  info: Skipping unknown file type: test/columns.test.ts
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @platforma-open/milaboratories.mixcr-clonotyping.workflow
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 5cd5710: Updated MiXCR
8
+
9
+ ## 2.26.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 4d7746d: Cell Linker Column & SDK Upgrade
14
+
3
15
  ## 2.25.0
4
16
 
5
17
  ### Minor Changes
@@ -116,7 +116,7 @@ addSpec := func(columns, additionalSpec) {
116
116
 
117
117
 
118
118
 
119
- calculateExportSpecs := func(presetSpecForBack, blockId) {
119
+ calculateExportSpecs := func(presetSpecForBack, sampleIdAxisSpec, blockId) {
120
120
  ops := exportSpecOpsFromPreset(presetSpecForBack)
121
121
 
122
122
  assemblingFeature := ops.assemblingFeature
@@ -125,6 +125,7 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
125
125
  splitByC := ops.splitByC
126
126
 
127
127
  isSingleCell := !is_undefined(cellTags) && len(cellTags) > 0
128
+ hashCellKey := isSingleCell && len(cellTags) == 1
128
129
 
129
130
  assemblingFeatureInfo := assemblingFeatureInfo(assemblingFeature)
130
131
  productiveFeature := assemblingFeatureInfo.productiveFeature
@@ -811,9 +812,10 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
811
812
 
812
813
 
813
814
 
814
- axesByClonotypeKey := undefined
815
- axesByScClonotypeKey := undefined
815
+ axisByClonotypeKeyGen := undefined
816
+ axisByScClonotypeKeyGen := undefined
816
817
  cellTagColumns := undefined
818
+ cellLinkerColumnSettingsGen := undefined
817
819
 
818
820
  if !is_undefined(clonotypeKeyColumns) {
819
821
 
@@ -831,39 +833,16 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
831
833
 
832
834
 
833
835
 
834
- axesByClonotypeKey = [ {
835
- column: "clonotypeKey",
836
- naRegex: "",
837
- spec: {
838
- name: "pl7.app/vdj/clonotypeKey",
839
- type: "String",
840
- domain: {
841
- "pl7.app/vdj/clonotypeKey/structure": string(json.encode(keyStrincture)),
842
- "pl7.app/vdj/clonotypingRunId": blockId
843
- },
844
- annotations: {
845
- "pl7.app/label": "Clonotype ID",
846
- "pl7.app/table/fontFamily": "monospace",
847
- "pl7.app/table/visibility": "default",
848
- "pl7.app/table/orderPriority": "110000",
849
- "pl7.app/segmentedBy": string(json.encode(["pl7.app/vdj/clonotypingRunId"]))
850
- }
851
- }
852
- } ]
853
-
854
- if isSingleCell {
855
- cellTagColumns = slices.map(cellTags, func(cellTag) {
856
- return "tagValue" + cellTag
857
- })
858
-
859
- axesByScClonotypeKey = [ {
860
- column: "scClonotypeKey",
836
+ axisByClonotypeKeyGen = func(chain) {
837
+ return {
838
+ column: "clonotypeKey",
861
839
  naRegex: "",
862
840
  spec: {
863
- name: "pl7.app/vdj/scClonotypeKey",
841
+ name: "pl7.app/vdj/clonotypeKey",
864
842
  type: "String",
865
843
  domain: {
866
- "pl7.app/vdj/scClonotypeKey/structure": string(json.encode(keyStrincture)),
844
+ "pl7.app/vdj/chain": chain,
845
+ "pl7.app/vdj/clonotypeKey/structure": string(json.encode(keyStrincture)),
867
846
  "pl7.app/vdj/clonotypingRunId": blockId
868
847
  },
869
848
  annotations: {
@@ -874,7 +853,69 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
874
853
  "pl7.app/segmentedBy": string(json.encode(["pl7.app/vdj/clonotypingRunId"]))
875
854
  }
876
855
  }
877
- } ]
856
+ }
857
+ }
858
+
859
+ if isSingleCell {
860
+ cellTagColumns = slices.map(cellTags, func(cellTag) {
861
+ return "tagValue" + cellTag
862
+ })
863
+
864
+ axisByScClonotypeKeyGen = func(receptor) {
865
+ return {
866
+ column: "scClonotypeKey",
867
+ naRegex: "",
868
+ spec: {
869
+ name: "pl7.app/vdj/scClonotypeKey",
870
+ type: "String",
871
+ domain: {
872
+ "pl7.app/vdj/receptor": receptor,
873
+ "pl7.app/vdj/scClonotypeKey/structure": string(json.encode(keyStrincture)),
874
+ "pl7.app/vdj/clonotypingRunId": blockId
875
+ },
876
+ annotations: {
877
+ "pl7.app/label": "Clonotype ID",
878
+ "pl7.app/table/fontFamily": "monospace",
879
+ "pl7.app/table/visibility": "default",
880
+ "pl7.app/table/orderPriority": "110000",
881
+ "pl7.app/segmentedBy": string(json.encode(["pl7.app/vdj/clonotypingRunId"]))
882
+ }
883
+ }
884
+ }
885
+ }
886
+
887
+ cellLinkerColumnSettingsGen = func(receptor) {
888
+ return {
889
+ axes: [ {
890
+ column: "sampleId",
891
+ spec: sampleIdAxisSpec
892
+ }, {
893
+ column: "cellKey",
894
+ spec: {
895
+ name: "pl7.app/sc/cellId",
896
+ type: "String",
897
+ annotations: a(110000, true, {
898
+ "pl7.app/label": "Cell ID",
899
+ "pl7.app/table/fontFamily": "monospace",
900
+ "pl7.app/parents": string(json.encode([sampleIdAxisSpec.name]))
901
+ })
902
+ }
903
+ }, axisByScClonotypeKeyGen(receptor) ],
904
+ columns: [ {
905
+ column: "1",
906
+ spec: {
907
+ name: "pl7.app/sc/cellLinker",
908
+ valueType: "Int",
909
+ annotations: a(0, undefined, {
910
+ "pl7.app/label": "Cell Linker",
911
+ "pl7.app/isLinkerColumn": "true"
912
+ })
913
+ }
914
+ } ],
915
+ storageFormat: "Binary",
916
+ partitionKeyLength: 1
917
+ }
918
+ }
878
919
 
879
920
 
880
921
 
@@ -897,8 +938,8 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
897
938
 
898
939
  cellTagColumns: cellTagColumns,
899
940
 
900
- axesByClonotypeKey: axesByClonotypeKey,
901
- axesByScClonotypeKey: axesByScClonotypeKey,
941
+ axisByClonotypeKeyGen: axisByClonotypeKeyGen,
942
+ axisByScClonotypeKeyGen: axisByScClonotypeKeyGen,
902
943
 
903
944
  columnsSpecPerSample: columnsSpecPerSample,
904
945
  columnsSpecPerSampleSc: columnsSpecPerSampleSc,
@@ -916,7 +957,10 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
916
957
  mainIsProductiveColumn: mainIsProductiveColumn,
917
958
  mainIsProductiveArgs: mainIsProductiveArgs,
918
959
 
919
- exportArgs: exportArgs
960
+ exportArgs: exportArgs,
961
+
962
+ hashCellKey: hashCellKey,
963
+ cellLinkerColumnSettingsGen: cellLinkerColumnSettingsGen
920
964
  }
921
965
  }
922
966
 
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@platforma-open/milaboratories.mixcr-clonotyping-2.workflow",
3
- "version": "2.25.0",
3
+ "version": "3.0.0",
4
4
  "description": "Tengo-based template",
5
5
  "dependencies": {
6
- "@platforma-sdk/workflow-tengo": "^4.10.0",
7
- "@platforma-open/milaboratories.software-mixcr": "4.7.0-190-develop"
6
+ "@platforma-sdk/workflow-tengo": "^4.14.0",
7
+ "@platforma-open/milaboratories.software-mixcr": "4.7.0-223-develop"
8
8
  },
9
9
  "devDependencies": {
10
- "@platforma-sdk/tengo-builder": "^2.1.12",
11
- "@platforma-sdk/test": "^1.39.21",
10
+ "@platforma-sdk/tengo-builder": "^2.1.13",
11
+ "@platforma-sdk/test": "^1.41.16",
12
12
  "vitest": "~2.1.9",
13
13
  "typescript": "~5.5.4"
14
14
  },
@@ -116,7 +116,7 @@ addSpec := func(columns, additionalSpec) {
116
116
  // Ordering rules
117
117
  // AA Sequences
118
118
 
119
- calculateExportSpecs := func(presetSpecForBack, blockId) {
119
+ calculateExportSpecs := func(presetSpecForBack, sampleIdAxisSpec, blockId) {
120
120
  ops := exportSpecOpsFromPreset(presetSpecForBack)
121
121
 
122
122
  assemblingFeature := ops.assemblingFeature
@@ -125,6 +125,7 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
125
125
  splitByC := ops.splitByC
126
126
 
127
127
  isSingleCell := !is_undefined(cellTags) && len(cellTags) > 0
128
+ hashCellKey := isSingleCell && len(cellTags) == 1
128
129
 
129
130
  assemblingFeatureInfo := assemblingFeatureInfo(assemblingFeature)
130
131
  productiveFeature := assemblingFeatureInfo.productiveFeature
@@ -811,9 +812,10 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
811
812
 
812
813
  // Axes
813
814
 
814
- axesByClonotypeKey := undefined
815
- axesByScClonotypeKey := undefined
815
+ axisByClonotypeKeyGen := undefined
816
+ axisByScClonotypeKeyGen := undefined
816
817
  cellTagColumns := undefined
818
+ cellLinkerColumnSettingsGen := undefined
817
819
 
818
820
  if !is_undefined(clonotypeKeyColumns) {
819
821
  // checking that corresponding columns exist in export
@@ -831,39 +833,16 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
831
833
  // Its absence indicates that merging would be semantically meaningless (e.g., abundance
832
834
  // measurements specific to each analysis)
833
835
 
834
- axesByClonotypeKey = [ {
835
- column: "clonotypeKey",
836
- naRegex: "",
837
- spec: {
838
- name: "pl7.app/vdj/clonotypeKey",
839
- type: "String",
840
- domain: {
841
- "pl7.app/vdj/clonotypeKey/structure": string(json.encode(keyStrincture)),
842
- "pl7.app/vdj/clonotypingRunId": blockId
843
- },
844
- annotations: {
845
- "pl7.app/label": "Clonotype ID",
846
- "pl7.app/table/fontFamily": "monospace",
847
- "pl7.app/table/visibility": "default",
848
- "pl7.app/table/orderPriority": "110000",
849
- "pl7.app/segmentedBy": string(json.encode(["pl7.app/vdj/clonotypingRunId"]))
850
- }
851
- }
852
- } ]
853
-
854
- if isSingleCell {
855
- cellTagColumns = slices.map(cellTags, func(cellTag) {
856
- return "tagValue" + cellTag
857
- })
858
-
859
- axesByScClonotypeKey = [ {
860
- column: "scClonotypeKey",
836
+ axisByClonotypeKeyGen = func(chain) {
837
+ return {
838
+ column: "clonotypeKey",
861
839
  naRegex: "",
862
840
  spec: {
863
- name: "pl7.app/vdj/scClonotypeKey",
841
+ name: "pl7.app/vdj/clonotypeKey",
864
842
  type: "String",
865
843
  domain: {
866
- "pl7.app/vdj/scClonotypeKey/structure": string(json.encode(keyStrincture)),
844
+ "pl7.app/vdj/chain": chain,
845
+ "pl7.app/vdj/clonotypeKey/structure": string(json.encode(keyStrincture)),
867
846
  "pl7.app/vdj/clonotypingRunId": blockId
868
847
  },
869
848
  annotations: {
@@ -874,7 +853,69 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
874
853
  "pl7.app/segmentedBy": string(json.encode(["pl7.app/vdj/clonotypingRunId"]))
875
854
  }
876
855
  }
877
- } ]
856
+ }
857
+ }
858
+
859
+ if isSingleCell {
860
+ cellTagColumns = slices.map(cellTags, func(cellTag) {
861
+ return "tagValue" + cellTag
862
+ })
863
+
864
+ axisByScClonotypeKeyGen = func(receptor) {
865
+ return {
866
+ column: "scClonotypeKey",
867
+ naRegex: "",
868
+ spec: {
869
+ name: "pl7.app/vdj/scClonotypeKey",
870
+ type: "String",
871
+ domain: {
872
+ "pl7.app/vdj/receptor": receptor,
873
+ "pl7.app/vdj/scClonotypeKey/structure": string(json.encode(keyStrincture)),
874
+ "pl7.app/vdj/clonotypingRunId": blockId
875
+ },
876
+ annotations: {
877
+ "pl7.app/label": "Clonotype ID",
878
+ "pl7.app/table/fontFamily": "monospace",
879
+ "pl7.app/table/visibility": "default",
880
+ "pl7.app/table/orderPriority": "110000",
881
+ "pl7.app/segmentedBy": string(json.encode(["pl7.app/vdj/clonotypingRunId"]))
882
+ }
883
+ }
884
+ }
885
+ }
886
+
887
+ cellLinkerColumnSettingsGen = func(receptor) {
888
+ return {
889
+ axes: [ {
890
+ column: "sampleId",
891
+ spec: sampleIdAxisSpec
892
+ }, {
893
+ column: "cellKey",
894
+ spec: {
895
+ name: "pl7.app/sc/cellId",
896
+ type: "String",
897
+ annotations: a(110000, true, {
898
+ "pl7.app/label": "Cell ID",
899
+ "pl7.app/table/fontFamily": "monospace",
900
+ "pl7.app/parents": string(json.encode([sampleIdAxisSpec.name]))
901
+ })
902
+ }
903
+ }, axisByScClonotypeKeyGen(receptor) ],
904
+ columns: [ {
905
+ column: "1",
906
+ spec: {
907
+ name: "pl7.app/sc/cellLinker",
908
+ valueType: "Int",
909
+ annotations: a(0, undefined, {
910
+ "pl7.app/label": "Cell Linker",
911
+ "pl7.app/isLinkerColumn": "true"
912
+ })
913
+ }
914
+ } ],
915
+ storageFormat: "Binary",
916
+ partitionKeyLength: 1
917
+ }
918
+ }
878
919
 
879
920
  // exportArgs += [ [ "-tags", "Cell" ] ]
880
921
  // axesByClonotypeKeyAndCellTag = axesByClonotypeKey + [ {
@@ -897,8 +938,8 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
897
938
 
898
939
  cellTagColumns: cellTagColumns,
899
940
 
900
- axesByClonotypeKey: axesByClonotypeKey,
901
- axesByScClonotypeKey: axesByScClonotypeKey,
941
+ axisByClonotypeKeyGen: axisByClonotypeKeyGen,
942
+ axisByScClonotypeKeyGen: axisByScClonotypeKeyGen,
902
943
 
903
944
  columnsSpecPerSample: columnsSpecPerSample,
904
945
  columnsSpecPerSampleSc: columnsSpecPerSampleSc,
@@ -916,7 +957,10 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
916
957
  mainIsProductiveColumn: mainIsProductiveColumn,
917
958
  mainIsProductiveArgs: mainIsProductiveArgs,
918
959
 
919
- exportArgs: exportArgs
960
+ exportArgs: exportArgs,
961
+
962
+ hashCellKey: hashCellKey,
963
+ cellLinkerColumnSettingsGen: cellLinkerColumnSettingsGen
920
964
  }
921
965
  }
922
966
 
@@ -1,4 +1,4 @@
1
- //tengo:hash_override 553D3080-FB87-44BC-BEEB-DB9EB5F773D8
1
+ //tengo:hash_override E5C1AD4C-B44D-4DE1-BBF1-4CB42ED7579A
2
2
 
3
3
  ll := import("@platforma-sdk/workflow-tengo:ll")
4
4
  self := import("@platforma-sdk/workflow-tengo:tpl.light")
@@ -28,6 +28,11 @@ self.body(func(inputs) {
28
28
  clonotypeKeyArgs := params.clonotypeKeyArgs
29
29
 
30
30
  cellTagColumns := params.cellTagColumns
31
+ // if false cell key will not be hashed
32
+ hashCellKey := params.hashCellKey
33
+ if !hashCellKey && !is_undefined(cellTagColumns) {
34
+ ll.assert(len(cellTagColumns) == 1, "cellTagColumns must be a single column when hashCellKey is false")
35
+ }
31
36
 
32
37
  mainAbundanceColumnUnnormalizedArgs := params.mainAbundanceColumnUnnormalizedArgs
33
38
  mainIsProductiveArgs := params.mainIsProductiveArgs
@@ -144,7 +149,7 @@ self.body(func(inputs) {
144
149
 
145
150
  dfSingleCell.addColumns(
146
151
  hashKeyDerivationExpressionPt(clonotypeKeyColumns).alias("clonotypeKey"),
147
- hashKeyDerivationExpressionPt(cellTagColumns).alias("cellKey")
152
+ hashCellKey ? hashKeyDerivationExpressionPt(cellTagColumns).alias("cellKey") : pt.col(cellTagColumns[0]).alias("cellKey")
148
153
  )
149
154
 
150
155
  dfSingleCell.save("output.tsv")
@@ -1,4 +1,4 @@
1
- //tengo:hash_override 8E4B6062-534D-4322-9CED-EC75E499A10A
1
+ //tengo:hash_override AB9AC8BA-5FFC-40C5-B2F7-AC42181524F5
2
2
 
3
3
  ll := import("@platforma-sdk/workflow-tengo:ll")
4
4
  self := import("@platforma-sdk/workflow-tengo:tpl.light")
@@ -15,7 +15,9 @@ clonotypeLabel := import(":clonotype-label")
15
15
  json := import("json")
16
16
  math := import("math")
17
17
 
18
- self.defineOutputs("abundanceTsv", "clonotypeTsv", "propertiesAPrimaryTsv", "propertiesASecondaryTsv", "propertiesBPrimaryTsv", "propertiesBSecondaryTsv")
18
+ self.defineOutputs("abundanceTsv", "clonotypeTsv",
19
+ "propertiesAPrimaryTsv", "propertiesASecondaryTsv", "propertiesBPrimaryTsv", "propertiesBSecondaryTsv",
20
+ "cellsTsv")
19
21
 
20
22
  ptablerSw := assets.importSoftware("@platforma-open/milaboratories.software-ptabler:main")
21
23
 
@@ -184,6 +186,10 @@ self.body(func(inputs) {
184
186
  )
185
187
  allChainsFilteredDf := allChainsMergedWithScKeyDf.filter(filterCondition)
186
188
 
189
+ allChainsFilteredDf.
190
+ withColumns(pt.lit(1).alias("1")).
191
+ save("cells.tsv", {columns: ["sampleId", "cellKey", "scClonotypeKey", "1"]})
192
+
187
193
  clonotypeTableDf := allChainsFilteredDf.groupBy(
188
194
  "scClonotypeKey", "clonotypeKeyA1", "clonotypeKeyA2", "clonotypeKeyB1", "clonotypeKeyB2"
189
195
  ).agg(
@@ -210,6 +216,7 @@ self.body(func(inputs) {
210
216
 
211
217
  clonotypeTsv := cellGroupingRunResult.getFile("clonotype.tsv")
212
218
  abundanceTsv := cellGroupingRunResult.getFile("abundance.tsv")
219
+ cellsTsv := cellGroupingRunResult.getFile("cells.tsv")
213
220
 
214
221
  //
215
222
  // Output processing - Reimplemented with PTabler pt API
@@ -303,6 +310,9 @@ self.body(func(inputs) {
303
310
  // used for aggregates (i.e. sampleCount and clonotypeLabel)
304
311
  clonotypeTsv: clonotypeTsv,
305
312
 
313
+ // used to build cell <-> clonotype linker column
314
+ cellsTsv: cellsTsv,
315
+
306
316
  // must have scClonotypeKey columns
307
317
  propertiesAPrimaryTsv: outputProcessingRunResult.getFile(chainMappings[0].finalOutFile),
308
318
  propertiesASecondaryTsv: outputProcessingRunResult.getFile(chainMappings[1].finalOutFile),
@@ -71,6 +71,7 @@ self.body(func(inputs) {
71
71
  presetCommonName := params.presetCommonName
72
72
  isLibraryFileGzipped := params.isLibraryFileGzipped
73
73
 
74
+ sampleIdAxisSpec := inputSpec.axesSpec[0]
74
75
 
75
76
  if is_undefined(presetSpecForBack) {
76
77
  ll.panic("no presetSpecForBack")
@@ -201,7 +202,7 @@ self.body(func(inputs) {
201
202
  }
202
203
  } ]
203
204
 
204
- exportSpecs := calculateExportSpecs(presetSpecForBack, blockId)
205
+ exportSpecs := calculateExportSpecs(presetSpecForBack, sampleIdAxisSpec, blockId)
205
206
 
206
207
  columnsSpecPerSample := exportSpecs.columnsSpecPerSample
207
208
  columnsSpecPerSampleSc := exportSpecs.columnsSpecPerSampleSc
@@ -214,8 +215,11 @@ self.body(func(inputs) {
214
215
  cellTagColumns := exportSpecs.cellTagColumns
215
216
 
216
217
  // axesByClonotypeId := exportSpecs.axesByClonotypeId
217
- axesByClonotypeKey := exportSpecs.axesByClonotypeKey
218
- axesByScClonotypeKey := exportSpecs.axesByScClonotypeKey
218
+
219
+ // function with chain argument
220
+ axisByClonotypeKeyGen := exportSpecs.axisByClonotypeKeyGen
221
+ // function with receptor argument
222
+ axisByScClonotypeKeyGen := exportSpecs.axisByScClonotypeKeyGen
219
223
 
220
224
  exportArgs := exportSpecs.exportArgs
221
225
 
@@ -227,13 +231,9 @@ self.body(func(inputs) {
227
231
  mainAbundanceColumnNormalizedArgs := exportSpecs.mainAbundanceColumnNormalizedArgs
228
232
  mainAbundanceColumnUnnormalizedArgs := exportSpecs.mainAbundanceColumnUnnormalizedArgs
229
233
 
230
- if is_undefined(axesByClonotypeKey) {
231
- ll.panic("Absent clonotype key not supported")
232
- }
233
-
234
- if len(axesByClonotypeKey) != 1 || (!is_undefined(axesByScClonotypeKey) && len(axesByScClonotypeKey) != 1) {
235
- ll.panic("Assertion error: expected exactly one clonotype key axis")
236
- }
234
+ hashCellKey := exportSpecs.hashCellKey
235
+ // function with receptor argument
236
+ cellLinkerColumnSettingsGen := exportSpecs.cellLinkerColumnSettingsGen
237
237
 
238
238
  mixcrResults := pframes.processColumn(
239
239
  { spec: inputSpec, data: inputs.inputData },
@@ -324,10 +324,6 @@ self.body(func(inputs) {
324
324
  chainInfo := chainInfos[chain]
325
325
  ll.assert(!is_undefined(chainInfo), "chainInfo not found for chain %v", chain)
326
326
 
327
- axesByClonotypeKeyWithChain := [ maps.deepTransform(axesByClonotypeKey[0], {
328
- spec: { domain: { "pl7.app/vdj/chain": chain } }
329
- }) ]
330
-
331
327
  //
332
328
  // Exporting CLNS -> TSV
333
329
  //
@@ -352,7 +348,7 @@ self.body(func(inputs) {
352
348
  type: "Xsv",
353
349
  xsvType: "tsv",
354
350
  settings: {
355
- axes: axesByClonotypeKeyWithChain,
351
+ axes: [ axisByClonotypeKeyGen(chain) ],
356
352
  columns: columnsSpecPerSample,
357
353
  storageFormat: "Binary",
358
354
  partitionKeyLength: 0
@@ -407,6 +403,8 @@ self.body(func(inputs) {
407
403
  mainAbundanceColumnUnnormalizedArgs: mainAbundanceColumnUnnormalizedArgs,
408
404
 
409
405
  cellTagColumns: cellTagColumns,
406
+ hashCellKey: hashCellKey,
407
+
410
408
  exportArgs: exportArgs,
411
409
  isLibraryFileGzipped: isLibraryFileGzipped
412
410
  },
@@ -441,7 +439,7 @@ self.body(func(inputs) {
441
439
  type: "Xsv",
442
440
  xsvType: "tsv",
443
441
  settings: {
444
- axes: axesByClonotypeKeyWithChain,
442
+ axes: [ axisByClonotypeKeyGen(chain) ],
445
443
  columns: columnsSpecPerClonotypeNoAggregates + columnsSpecPerClonotypeAggregates,
446
444
  storageFormat: "Binary",
447
445
  partitionKeyLength: 0
@@ -502,10 +500,6 @@ self.body(func(inputs) {
502
500
  for receptor in receptors {
503
501
  receptorInfo := receptorInfos[receptor]
504
502
 
505
- axesByScClonotypeKeyWithReceptor := [ maps.deepTransform(axesByScClonotypeKey[0], {
506
- spec: { domain: { "pl7.app/vdj/receptor": receptor } }
507
- }) ]
508
-
509
503
  singleCellOutputs := [ {
510
504
  type: "Resource",
511
505
  spec: {
@@ -524,8 +518,8 @@ self.body(func(inputs) {
524
518
  settings: {
525
519
  axes: [ {
526
520
  column: "sampleId",
527
- spec: inputSpec.axesSpec[0]
528
- } ] + axesByScClonotypeKeyWithReceptor,
521
+ spec: sampleIdAxisSpec
522
+ }, axisByScClonotypeKeyGen(receptor) ],
529
523
  columns: columnsSpecPerSampleSc,
530
524
  storageFormat: "Binary",
531
525
  partitionKeyLength: 1
@@ -538,7 +532,7 @@ self.body(func(inputs) {
538
532
  type: "Xsv",
539
533
  xsvType: "tsv",
540
534
  settings: {
541
- axes: axesByScClonotypeKeyWithReceptor,
535
+ axes: [ axisByScClonotypeKeyGen(receptor) ],
542
536
  columns: columnsSpecPerClonotypeSc,
543
537
  storageFormat: "Binary"
544
538
  },
@@ -599,7 +593,7 @@ self.body(func(inputs) {
599
593
  type: "Xsv",
600
594
  xsvType: "tsv",
601
595
  settings: {
602
- axes: axesByScClonotypeKeyWithReceptor,
596
+ axes: [ axisByScClonotypeKeyGen(receptor) ],
603
597
  columns: transformSpecs(isPrimary ? columnsSpecPerClonotypeNoAggregates : columnsSpecPerClonotypeSecondary, {
604
598
  spec: {
605
599
  domain: {
@@ -620,6 +614,16 @@ self.body(func(inputs) {
620
614
  }
621
615
  }
622
616
 
617
+ singleCellOutputs += [ {
618
+ type: "Xsv",
619
+ xsvType: "tsv",
620
+ settings: cellLinkerColumnSettingsGen(receptor),
621
+ mem: "16GiB",
622
+ cpu: 2,
623
+ name: "cellsLinkerTable",
624
+ path: ["cellsTsv"]
625
+ } ]
626
+
623
627
  chainA := receptorInfo.chains[0]
624
628
  chainB := receptorInfo.chains[1]
625
629
 
@@ -669,6 +673,8 @@ self.body(func(inputs) {
669
673
  singleCellResult.addXsvOutputToBuilder(clonotypes, "propertiesASecondary", "clonotypeProperties/" + receptor + "/aSecondary/")
670
674
  singleCellResult.addXsvOutputToBuilder(clonotypes, "propertiesBPrimary", "clonotypeProperties/" + receptor + "/bPrimary/")
671
675
  singleCellResult.addXsvOutputToBuilder(clonotypes, "propertiesBSecondary", "clonotypeProperties/" + receptor + "/bSecondary/")
676
+
677
+ singleCellResult.addXsvOutputToBuilder(clonotypes, "cellsLinkerTable", "clonotypeProperties/" + receptor + "/cellsLinkerTable/")
672
678
  }
673
679
  }
674
680
 
@@ -1,5 +1,6 @@
1
1
  self := import("@platforma-sdk/workflow-tengo:tpl.light")
2
2
  ll := import("@platforma-sdk/workflow-tengo:ll")
3
+ maps := import("@platforma-sdk/workflow-tengo:maps")
3
4
 
4
5
  calculateExportSpecs := import(":calculate-export-specs")
5
6
 
@@ -8,7 +9,12 @@ self.defineOutputs("exportSpecs")
8
9
  self.body(func(inputs) {
9
10
  presetSpecForBack := inputs.presetSpecForBack.getDataAsJson()
10
11
 
11
- exportSpecs := calculateExportSpecs(presetSpecForBack, "test")
12
+ exportSpecs := calculateExportSpecs(presetSpecForBack, {}, "test")
13
+ exportSpecs = maps.deepMerge(exportSpecs, {
14
+ axisByClonotypeKeyGen: undefined,
15
+ axisByScClonotypeKeyGen: undefined,
16
+ cellLinkerColumnSettingsGen: undefined
17
+ })
12
18
 
13
19
  return {
14
20
  exportSpecs: exportSpecs