@platforma-open/milaboratories.mixcr-amplicon-alignment.workflow 1.19.8 → 1.19.9

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-amplicon-alignment/mixcr-amplicon-alignment/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @platforma-open/milaboratories.mixcr-amplicon-alignment.workflow@1.19.8 build /home/runner/work/mixcr-amplicon-alignment/mixcr-amplicon-alignment/workflow
3
+ > @platforma-open/milaboratories.mixcr-amplicon-alignment.workflow@1.19.9 build /home/runner/work/mixcr-amplicon-alignment/mixcr-amplicon-alignment/workflow
4
4
  > rm -rf dist && pl-tengo check && pl-tengo build
5
5
 
6
6
  Processing "src/aggregate-by-clonotype-key.tpl.tengo"...
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @platforma-open/milaboratories.mixcr-amplicon-alignment.workflow
2
2
 
3
+ ## 1.19.9
4
+
5
+ ### Patch Changes
6
+
7
+ - 2149d28: Fix column naming for range assembling features (e.g. CDR1:CDR3, FR2:FR4) without imputation.
8
+
9
+ When using a range assembling feature without "Impute non-covered part", the workflow would fail with
10
+ "column nSeqVDJRegion does not exist in export" because VDJRegion is never exported for non-full-range features.
11
+
12
+ Changes:
13
+
14
+ - Use the assembling feature itself as clonotype key column when VDJRegion is unavailable
15
+ - Fix column naming to match MiXCR output format (e.g. `CDR1_TO_FR4` instead of `{CDR1Begin:FR4End}`)
16
+ - Add unit tests covering column naming for all assembling feature variants with/without imputation
17
+
3
18
  ## 1.19.8
4
19
 
5
20
  ### Patch Changes
@@ -164,8 +164,20 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
164
164
  }
165
165
 
166
166
  productiveFeature := formatAssemblingFeature(assemblingFeature)
167
-
167
+
168
+
169
+
170
+
171
+
172
+
168
173
  outputProductiveFeature := productiveFeature
174
+ if assemblingFeature != "VDJRegion" && assemblingFeature != "CDR3" {
175
+ parts := text.split(assemblingFeature, ":")
176
+ if len(parts) == 2 && parts[1] == "FR4" {
177
+
178
+ outputProductiveFeature = parts[0] + "_TO_FR4"
179
+ }
180
+ }
169
181
 
170
182
  coreGeneFeatures := parsedFeature.coreGeneFeatures
171
183
 
@@ -187,22 +199,67 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
187
199
  [ "-jGene" ]
188
200
  ]
189
201
  } else {
190
- isVdjImputed := !is_undefined(imputedFeaturesMap["VDJRegion"]) && imputeGermline
191
- vdjColName := "nSeq" + (isVdjImputed ? "Imputed" : "") + "VDJRegion"
192
- vdjArgLabel := "-nFeature" + (isVdjImputed ? "Imputed" : "")
193
202
 
194
- clonotypeKeyColumns = [vdjColName, "bestVGene", "bestJGene"]
195
- clonotypeKeyArgs = [
196
- [ vdjArgLabel, "VDJRegion" ],
197
- [ "-vGene" ],
198
- [ "-jGene" ]
199
- ]
203
+
204
+ vdjIsAssemblingFeature := is_undefined(imputedFeaturesMap["VDJRegion"])
205
+
206
+ if vdjIsAssemblingFeature {
207
+
208
+ clonotypeKeyColumns = ["nSeqVDJRegion", "bestVGene", "bestJGene"]
209
+ clonotypeKeyArgs = [
210
+ [ "-nFeature", "VDJRegion" ],
211
+ [ "-vGene" ],
212
+ [ "-jGene" ]
213
+ ]
214
+ } else {
215
+
216
+
217
+
218
+
219
+
220
+ keyColName := "nSeq" + outputProductiveFeature
221
+ clonotypeKeyColumns = [keyColName, "bestVGene", "bestJGene"]
222
+ clonotypeKeyArgs = [
223
+ [ "-nFeature", productiveFeature ],
224
+ [ "-vGene" ],
225
+ [ "-jGene" ]
226
+ ]
227
+ }
200
228
  }
201
229
 
202
230
  columnsSpecPerSample := []
203
231
  columnsSpecPerClonotypeNoAggregates := []
204
232
  mutationColumns := []
205
233
 
234
+
235
+
236
+
237
+ needsAssemblingFeatureExport := assemblingFeature != "CDR3" && assemblingFeature != "VDJRegion" && !is_undefined(imputedFeaturesMap["VDJRegion"])
238
+ if needsAssemblingFeatureExport {
239
+ featureIdL := text.to_lower(formatId(assemblingFeature))
240
+ keyColName := "nSeq" + outputProductiveFeature
241
+ columnsSpecPerClonotypeNoAggregates += [ {
242
+ column: keyColName,
243
+ id: "n-seq-" + featureIdL,
244
+ naRegex: "region_not_covered",
245
+ spec: {
246
+ name: "pl7.app/vdj/sequence",
247
+ valueType: "String",
248
+ domain: {
249
+ "pl7.app/vdj/feature": outputProductiveFeature,
250
+ "pl7.app/alphabet": "nucleotide"
251
+ },
252
+ annotations: a(80100, false, {
253
+ "pl7.app/vdj/isAssemblingFeature": "true",
254
+ "pl7.app/vdj/isMainSequence": "false",
255
+ "pl7.app/vdj/imputed": "false",
256
+ "pl7.app/table/fontFamily": "monospace",
257
+ "pl7.app/label": outputProductiveFeature + " nt"
258
+ })
259
+ }
260
+ } ]
261
+ }
262
+
206
263
  clonotypeLabelColumn := {
207
264
  column: "clonotypeLabel",
208
265
  id: "clonotype-label",
@@ -220,6 +277,11 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
220
277
  exportArgs := []
221
278
 
222
279
 
280
+ if needsAssemblingFeatureExport {
281
+ exportArgs += [ [ "-nFeature", productiveFeature ] ]
282
+ }
283
+
284
+
223
285
 
224
286
  hasUmi := !is_undefined(presetSpecForBack) && !is_undefined(presetSpecForBack.umiTags) && len(presetSpecForBack.umiTags) > 0
225
287
 
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-open/milaboratories.mixcr-amplicon-alignment.workflow",
3
- "version": "1.19.8",
3
+ "version": "1.19.9",
4
4
  "description": "MiXCR Amplicon Alignment Workflow",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -164,8 +164,20 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
164
164
  }
165
165
 
166
166
  productiveFeature := formatAssemblingFeature(assemblingFeature)
167
-
167
+
168
+ // MiXCR column naming for range features:
169
+ // - Ranges ending at FR4 have named aliases: CDR1_TO_FR4, FR2_TO_FR4, CDR2_TO_FR4, FR3_TO_FR4
170
+ // (defined in repseqio GeneFeature.java)
171
+ // - All other ranges use {XBegin:YEnd} format (e.g. {CDR1Begin:CDR3End})
172
+ // - Simple features (CDR3, VDJRegion) use their name directly
168
173
  outputProductiveFeature := productiveFeature
174
+ if assemblingFeature != "VDJRegion" && assemblingFeature != "CDR3" {
175
+ parts := text.split(assemblingFeature, ":")
176
+ if len(parts) == 2 && parts[1] == "FR4" {
177
+ // MiXCR has a named alias for this range
178
+ outputProductiveFeature = parts[0] + "_TO_FR4"
179
+ }
180
+ }
169
181
 
170
182
  coreGeneFeatures := parsedFeature.coreGeneFeatures
171
183
 
@@ -187,22 +199,67 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
187
199
  [ "-jGene" ]
188
200
  ]
189
201
  } else {
190
- isVdjImputed := !is_undefined(imputedFeaturesMap["VDJRegion"]) && imputeGermline
191
- vdjColName := "nSeq" + (isVdjImputed ? "Imputed" : "") + "VDJRegion"
192
- vdjArgLabel := "-nFeature" + (isVdjImputed ? "Imputed" : "")
193
-
194
- clonotypeKeyColumns = [vdjColName, "bestVGene", "bestJGene"]
195
- clonotypeKeyArgs = [
196
- [ vdjArgLabel, "VDJRegion" ],
197
- [ "-vGene" ],
198
- [ "-jGene" ]
199
- ]
202
+ // VDJRegion is the assembling feature itself only when it's NOT in the imputed list
203
+ // (e.g. VDJRegion or FR1:FR4 as the assembling feature)
204
+ vdjIsAssemblingFeature := is_undefined(imputedFeaturesMap["VDJRegion"])
205
+
206
+ if vdjIsAssemblingFeature {
207
+ // VDJRegion IS the assembling feature, use it directly as the key
208
+ clonotypeKeyColumns = ["nSeqVDJRegion", "bestVGene", "bestJGene"]
209
+ clonotypeKeyArgs = [
210
+ [ "-nFeature", "VDJRegion" ],
211
+ [ "-vGene" ],
212
+ [ "-jGene" ]
213
+ ]
214
+ } else {
215
+ // Range feature where VDJRegion is NOT the assembling feature (e.g. CDR1:CDR3, FR2:FR4)
216
+ // Always use the assembling feature itself as the key, even with imputation enabled.
217
+ // Imputed VDJRegion is NOT guaranteed unique per clone (two clones with different
218
+ // assembling feature sequences can produce the same imputed VDJRegion).
219
+ // The assembling feature sequence IS unique by definition (it defines the clone).
220
+ keyColName := "nSeq" + outputProductiveFeature
221
+ clonotypeKeyColumns = [keyColName, "bestVGene", "bestJGene"]
222
+ clonotypeKeyArgs = [
223
+ [ "-nFeature", productiveFeature ],
224
+ [ "-vGene" ],
225
+ [ "-jGene" ]
226
+ ]
227
+ }
200
228
  }
201
229
 
202
230
  columnsSpecPerSample := []
203
231
  columnsSpecPerClonotypeNoAggregates := []
204
232
  mutationColumns := []
205
233
 
234
+ // For range features where VDJRegion is not the assembling feature, we need to export
235
+ // the combined assembling feature sequence column explicitly (individual features are
236
+ // exported in the loop below, but the combined feature like {CDR1Begin:CDR3End} is not)
237
+ needsAssemblingFeatureExport := assemblingFeature != "CDR3" && assemblingFeature != "VDJRegion" && !is_undefined(imputedFeaturesMap["VDJRegion"])
238
+ if needsAssemblingFeatureExport {
239
+ featureIdL := text.to_lower(formatId(assemblingFeature))
240
+ keyColName := "nSeq" + outputProductiveFeature
241
+ columnsSpecPerClonotypeNoAggregates += [ {
242
+ column: keyColName,
243
+ id: "n-seq-" + featureIdL,
244
+ naRegex: "region_not_covered",
245
+ spec: {
246
+ name: "pl7.app/vdj/sequence",
247
+ valueType: "String",
248
+ domain: {
249
+ "pl7.app/vdj/feature": outputProductiveFeature,
250
+ "pl7.app/alphabet": "nucleotide"
251
+ },
252
+ annotations: a(80100, false, {
253
+ "pl7.app/vdj/isAssemblingFeature": "true",
254
+ "pl7.app/vdj/isMainSequence": "false",
255
+ "pl7.app/vdj/imputed": "false",
256
+ "pl7.app/table/fontFamily": "monospace",
257
+ "pl7.app/label": outputProductiveFeature + " nt"
258
+ })
259
+ }
260
+ } ]
261
+ }
262
+
206
263
  clonotypeLabelColumn := {
207
264
  column: "clonotypeLabel",
208
265
  id: "clonotype-label",
@@ -219,6 +276,11 @@ calculateExportSpecs := func(presetSpecForBack, blockId) {
219
276
  // array of array of arg groups
220
277
  exportArgs := []
221
278
 
279
+ // Add the assembling feature export arg if needed (column spec was added above)
280
+ if needsAssemblingFeatureExport {
281
+ exportArgs += [ [ "-nFeature", productiveFeature ] ]
282
+ }
283
+
222
284
  // Abundance - reads by default; switch to UMI columns if umiTags are present
223
285
 
224
286
  hasUmi := !is_undefined(presetSpecForBack) && !is_undefined(presetSpecForBack.umiTags) && len(presetSpecForBack.umiTags) > 0