@molgenis/vip-report-template 7.1.3 → 8.0.1

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.
Files changed (155) hide show
  1. package/.gitattributes +3 -1
  2. package/README.md +13 -0
  3. package/eslint.config.mjs +6 -1
  4. package/package.json +6 -5
  5. package/scripts/validateConfig/README.txt +5 -0
  6. package/scripts/validateConfig/compileValidator.ts +17 -0
  7. package/scripts/validateConfig/createSchema.ts +6 -0
  8. package/scripts/validateConfig/schema.ts +370 -0
  9. package/src/App.tsx +2 -1
  10. package/src/components/DatasetDropdown.tsx +1 -1
  11. package/src/components/GenomeBrowser.tsx +14 -4
  12. package/src/components/RecordsTable.tsx +23 -6
  13. package/src/components/VariantConsequenceContainer.tsx +9 -2
  14. package/src/components/VariantInfoTable.tsx +1 -1
  15. package/src/components/VariantsContainer.tsx +47 -9
  16. package/src/components/VariantsContainerHeader.tsx +1 -8
  17. package/src/components/field/composed/FieldGenotype.tsx +1 -1
  18. package/src/components/field/composed/FieldGenotypeStr.tsx +18 -10
  19. package/src/components/field/composed/FieldGnomAd.tsx +1 -1
  20. package/src/components/field/composed/FieldInheritanceModes.tsx +1 -4
  21. package/src/components/field/composed/FieldVkgl.tsx +1 -1
  22. package/src/components/field/typed/FieldCategorical.tsx +1 -1
  23. package/src/components/field/typed/FieldCharacter.tsx +1 -1
  24. package/src/components/field/typed/FieldFlag.tsx +1 -1
  25. package/src/components/field/typed/FieldFloat.tsx +1 -1
  26. package/src/components/field/typed/FieldInteger.tsx +1 -1
  27. package/src/components/field/typed/FieldString.tsx +1 -1
  28. package/src/components/filter/Filter.tsx +1 -0
  29. package/src/components/filter/composed/FilterAllelicImbalance.tsx +1 -0
  30. package/src/components/filter/composed/FilterComposed.tsx +7 -0
  31. package/src/components/filter/composed/FilterDeNovo.tsx +1 -0
  32. package/src/components/filter/composed/FilterHpo.tsx +1 -0
  33. package/src/components/filter/composed/FilterInheritance.tsx +1 -0
  34. package/src/components/filter/composed/FilterLocus.tsx +17 -3
  35. package/src/components/filter/composed/FilterPick.tsx +30 -0
  36. package/src/components/filter/composed/FilterVipC.tsx +1 -0
  37. package/src/components/filter/composed/FilterVipCS.tsx +1 -0
  38. package/src/components/filter/fixed/FilterFixed.tsx +7 -0
  39. package/src/components/filter/typed/FilterCategorical.tsx +18 -1
  40. package/src/components/filter/typed/FilterFlag.tsx +2 -0
  41. package/src/components/filter/typed/FilterInterval.tsx +29 -1
  42. package/src/components/filter/typed/FilterString.tsx +8 -1
  43. package/src/components/filter/typed/FilterTyped.tsx +1 -0
  44. package/src/components/form/ButtonApply.tsx +6 -2
  45. package/src/mocks/GRCh38/README.txt +15 -0
  46. package/src/mocks/GRCh38/decisionTree.json +201 -0
  47. package/src/mocks/GRCh38/family.ped +6 -0
  48. package/src/mocks/GRCh38/field_metadata.json +36 -11
  49. package/src/mocks/GRCh38/sample1.ped +1 -0
  50. package/src/mocks/GRCh38/static.ts +36 -148
  51. package/src/mocks/GRCh38/str.ped +1 -0
  52. package/src/mocks/GRCh38/vcf/family.db.blob +0 -0
  53. package/src/mocks/GRCh38/vcf/family.vcf +312 -0
  54. package/src/mocks/GRCh38/vcf/fixPaths.sql +7 -0
  55. package/src/mocks/GRCh38/vcf/no_vep.db.blob +0 -0
  56. package/src/mocks/GRCh38/vcf/{no_vep.vcf.blob → no_vep.vcf} +1 -1
  57. package/src/mocks/GRCh38/vcf/samples_0.db.blob +0 -0
  58. package/src/mocks/GRCh38/vcf/samples_1.db.blob +0 -0
  59. package/src/mocks/GRCh38/vcf/samples_100.db.blob +0 -0
  60. package/src/mocks/GRCh38/vcf/{samples_100.vcf.blob → samples_100.vcf} +6 -6
  61. package/src/mocks/GRCh38/vcf/str.db.blob +0 -0
  62. package/src/mocks/GRCh38/vcf/{str.vcf.blob → str.vcf} +17 -17
  63. package/src/mocks/MockApiClient.ts +60 -226
  64. package/src/mocks/config_cram.json +4 -0
  65. package/src/mocks/config_default_values.json +722 -0
  66. package/src/mocks/config_vcf.json +15 -1
  67. package/src/mocks/sql-wasm.wasm.blob +0 -0
  68. package/src/types/config.d.ts +9 -5
  69. package/src/types/configCells.d.ts +1 -0
  70. package/src/types/configFilter.d.ts +1 -0
  71. package/src/types/configFilterComposed.d.ts +3 -0
  72. package/src/utils/api.ts +21 -49
  73. package/src/utils/config/configCellsComposed.ts +1 -1
  74. package/src/utils/config/configCellsField.ts +2 -0
  75. package/src/utils/config/configFiltersComposed.ts +7 -0
  76. package/src/utils/config/configFiltersField.ts +3 -2
  77. package/src/utils/config/configFiltersFixed.ts +7 -0
  78. package/src/utils/config/configValidator.precompiled.ts +83402 -0
  79. package/src/utils/config/configValidator.ts +3 -368
  80. package/src/utils/csq.ts +5 -10
  81. package/src/utils/decisionTree.ts +8 -9
  82. package/src/utils/query/query.ts +3 -2
  83. package/src/utils/query/queryFilter.ts +2 -3
  84. package/src/utils/query/queryFilterComposed.ts +12 -12
  85. package/src/utils/query/queryFilterField.ts +14 -7
  86. package/src/utils/query/queryFilterFixed.ts +5 -5
  87. package/src/utils/query/querySample.ts +15 -4
  88. package/src/utils/query/selector.ts +5 -20
  89. package/src/utils/query/sort.ts +4 -4
  90. package/src/utils/vcf.ts +20 -11
  91. package/src/views/Help.tsx +2 -2
  92. package/src/views/SampleVariant.tsx +4 -1
  93. package/src/views/Samples.tsx +10 -3
  94. package/src/views/data/data.tsx +2 -2
  95. package/tests/utils/config/configFiltersComposed.test.ts +2 -0
  96. package/tests/utils/config/configFiltersField.test.ts +4 -2
  97. package/tests/utils/config/configFiltersFixed.test.ts +23 -6
  98. package/tests/utils/query/query.test.ts +34 -6
  99. package/tests/utils/query/queryFilter.test.ts +4 -31
  100. package/tests/utils/query/queryFilterComposed.test.ts +13 -13
  101. package/tests/utils/query/queryFilterField.test.ts +5 -5
  102. package/tests/utils/query/queryFilterFixed.test.ts +5 -5
  103. package/tests/utils/query/querySample.test.ts +50 -10
  104. package/tests/utils/query/sort.test.ts +1 -1
  105. package/tests/utils/vcf.test.ts +1 -0
  106. package/vite.config.mts +2 -1
  107. package/src/mocks/GRCh37/alignment.cram.blob +0 -0
  108. package/src/mocks/GRCh37/alignment.cram.crai.blob +0 -0
  109. package/src/mocks/GRCh37/decisionTree.json +0 -355
  110. package/src/mocks/GRCh37/fasta/1-10042288-10042788.fasta.gz.blob +0 -0
  111. package/src/mocks/GRCh37/fasta/1-152520538-152521038.fasta.gz.blob +0 -0
  112. package/src/mocks/GRCh37/fasta/1-16375333-16375833.fasta.gz.blob +0 -0
  113. package/src/mocks/GRCh37/fasta/1-16376162-16376662.fasta.gz.blob +0 -0
  114. package/src/mocks/GRCh37/fasta/1-17348965-17349469.fasta.gz.blob +0 -0
  115. package/src/mocks/GRCh37/fasta/1-17348969-17349469.fasta.gz.blob +0 -0
  116. package/src/mocks/GRCh37/fasta/1-17354844-17355344.fasta.gz.blob +0 -0
  117. package/src/mocks/GRCh37/fasta/10-126091249-126091749.fasta.gz.blob +0 -0
  118. package/src/mocks/GRCh37/fasta/11-134013975-134014475.fasta.gz.blob +0 -0
  119. package/src/mocks/GRCh37/fasta/13-77569878-77570378.fasta.gz.blob +0 -0
  120. package/src/mocks/GRCh37/fasta/14-105167610-105168110.fasta.gz.blob +0 -0
  121. package/src/mocks/GRCh37/fasta/14-89307588-89308088.fasta.gz.blob +0 -0
  122. package/src/mocks/GRCh37/fasta/14-89309945-89310445.fasta.gz.blob +0 -0
  123. package/src/mocks/GRCh37/fasta/14-89336157-89336657.fasta.gz.blob +0 -0
  124. package/src/mocks/GRCh37/fasta/17-29555814-29556314.fasta.gz.blob +0 -0
  125. package/src/mocks/GRCh37/fasta/17-29585172-29585672.fasta.gz.blob +0 -0
  126. package/src/mocks/GRCh37/fasta/17-29663629-29664129.fasta.gz.blob +0 -0
  127. package/src/mocks/GRCh37/fasta/17-29675976-29676476.fasta.gz.blob +0 -0
  128. package/src/mocks/GRCh37/fasta/17-29683733-29684233.fasta.gz.blob +0 -0
  129. package/src/mocks/GRCh37/fasta/19-11215896-11216396.fasta.gz.blob +0 -0
  130. package/src/mocks/GRCh37/fasta/19-11223801-11224301.fasta.gz.blob +0 -0
  131. package/src/mocks/GRCh37/fasta/19-17449149-17449649.fasta.gz.blob +0 -0
  132. package/src/mocks/GRCh37/fasta/19-17451747-17452247.fasta.gz.blob +0 -0
  133. package/src/mocks/GRCh37/fasta/2-47635417-47635917.fasta.gz.blob +0 -0
  134. package/src/mocks/GRCh37/fasta/20-62326742-62327242.fasta.gz.blob +0 -0
  135. package/src/mocks/GRCh37/fasta/22-50627343-50627843.fasta.gz.blob +0 -0
  136. package/src/mocks/GRCh37/fasta/22-50721296-50721796.fasta.gz.blob +0 -0
  137. package/src/mocks/GRCh37/fasta/4-106320044-106320544.fasta.gz.blob +0 -0
  138. package/src/mocks/GRCh37/fasta/7-42017061-42017561.fasta.gz.blob +0 -0
  139. package/src/mocks/GRCh37/fasta/7-42064707-42065207.fasta.gz.blob +0 -0
  140. package/src/mocks/GRCh37/fasta/8-145140250-145140750.fasta.gz.blob +0 -0
  141. package/src/mocks/GRCh37/fasta/8-61764893-61765393.fasta.gz.blob +0 -0
  142. package/src/mocks/GRCh37/fasta/9-107546383-107546883.fasta.gz.blob +0 -0
  143. package/src/mocks/GRCh37/fasta/9-107584614-107585114.fasta.gz.blob +0 -0
  144. package/src/mocks/GRCh37/fasta/MT-15076-15576.fasta.gz.blob +0 -0
  145. package/src/mocks/GRCh37/fasta/X-48932771-48933271.fasta.gz.blob +0 -0
  146. package/src/mocks/GRCh37/fasta/Y-2655391-2655891.fasta.gz.blob +0 -0
  147. package/src/mocks/GRCh37/field_metadata.json +0 -794
  148. package/src/mocks/GRCh37/genes.gff.gz.blob +0 -0
  149. package/src/mocks/GRCh37/sampleTree.json +0 -143
  150. package/src/mocks/GRCh37/static.ts +0 -189
  151. package/src/mocks/GRCh37/vcf/family.vcf.blob +0 -134
  152. package/src/mocks/GRCh38/vcf/family.vcf.blob +0 -272
  153. package/src/mocks/static.ts +0 -1636
  154. /package/src/mocks/GRCh38/vcf/{samples_0.vcf.blob → samples_0.vcf} +0 -0
  155. /package/src/mocks/GRCh38/vcf/{samples_1.vcf.blob → samples_1.vcf} +0 -0
@@ -17,25 +17,10 @@ export function createInfoSortPath(field: FieldMetadata): SortPath {
17
17
 
18
18
  function selector(field: FieldMetadata): SelectorPart[] {
19
19
  const selector: Selector = [];
20
- let currentField: FieldMetadata | undefined = field;
21
- do {
22
- if (currentField.parent && currentField.parent.nested) {
23
- const items = currentField.parent.nested.items;
24
- let i;
25
- for (i = 0; i < items.length; ++i) {
26
- if (items[i]!.id === currentField.id) {
27
- break;
28
- }
29
- }
30
- selector.push(i);
31
- if (currentField.parent.number.count !== 1) {
32
- selector.push("*");
33
- }
34
- } else {
35
- selector.push(currentField.id);
36
- }
37
- currentField = currentField.parent;
38
- } while (currentField);
39
- selector.reverse();
20
+ const currentField: FieldMetadata | undefined = field;
21
+ if (currentField.parent) {
22
+ selector.push(currentField.parent.id);
23
+ }
24
+ selector.push(currentField.id);
40
25
  return selector;
41
26
  }
@@ -1,5 +1,5 @@
1
1
  import { FieldMetadata, VcfMetadata } from "@molgenis/vip-report-vcf";
2
- import { CompareFn, SortOrder, SortPath } from "@molgenis/vip-report-api";
2
+ import { SortOrder, SortPath } from "@molgenis/vip-report-api";
3
3
  import { ConfigSort, ConfigSortOrder } from "../../types/configSort";
4
4
 
5
5
  import { createInfoSortPath } from "./selector.ts";
@@ -68,16 +68,16 @@ function createField(property: string | SortPath, recordsMeta: VcfMetadata): Fie
68
68
 
69
69
  if (path.length === 3) {
70
70
  const pathIndex = path[2];
71
- if (typeof pathIndex !== "number") throw new InvalidSortPathError(path);
71
+ if (typeof pathIndex !== "string") throw new InvalidSortPathError(path);
72
72
  if (field.nested === undefined) throw new InvalidSortPathError(path);
73
73
 
74
- field = field.nested.items[pathIndex];
74
+ field = field.nested.items.find((item) => item.id === path[2]);
75
75
  if (field === undefined) throw new InvalidSortPathError(path);
76
76
  }
77
77
  return field;
78
78
  }
79
79
 
80
- function createDirection(compare?: "asc" | "desc" | CompareFn): Direction {
80
+ function createDirection(compare?: "asc" | "desc"): Direction {
81
81
  if (compare === undefined) return DIRECTION_ASCENDING;
82
82
  else if (compare === DIRECTION_ASCENDING) return DIRECTION_ASCENDING;
83
83
  else if (compare === DIRECTION_DESCENDING) return DIRECTION_DESCENDING;
package/src/utils/vcf.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  RecordSample,
7
7
  Value,
8
8
  ValueDescription,
9
+ ValueObject,
9
10
  ValueString,
10
11
  VcfMetadata,
11
12
  VcfRecord,
@@ -38,7 +39,11 @@ function createFieldMapTypedRec(keyPrefix: string, fieldMetadataList: FieldMetad
38
39
  fieldMetadataList.forEach((fieldMetadata, index) => {
39
40
  const key = `${keyPrefix}/${fieldMetadata.id}`;
40
41
  if (fieldMetadata.nested) {
41
- const nestedFields = createFieldMapTypedRec(key, fieldMetadata.nested.items);
42
+ const nestedItemsArray = Object.keys(fieldMetadata.nested?.items)
43
+ .map(Number)
44
+ .sort((a, b) => a - b)
45
+ .map((index) => fieldMetadata.nested?.items[index]) as FieldMetadata[];
46
+ const nestedFields = createFieldMapTypedRec(key, nestedItemsArray);
42
47
  Object.assign(fields, nestedFields);
43
48
 
44
49
  // add parent field, but update items with items in nestedFields
@@ -47,7 +52,7 @@ function createFieldMapTypedRec(keyPrefix: string, fieldMetadataList: FieldMetad
47
52
  index,
48
53
  nested: {
49
54
  ...fieldMetadata.nested,
50
- items: fieldMetadata.nested.items.map((field) => nestedFields[`${key}/${field.id}`]!),
55
+ items: nestedItemsArray.map((field) => nestedFields[`${key}/${field.id}`]!),
51
56
  },
52
57
  };
53
58
  } else {
@@ -89,7 +94,7 @@ function mapFieldValue(value: Value | Genotype | undefined, fieldMetadata: Field
89
94
  } else if (fieldMetadata.number.count === 1) {
90
95
  definedValue = value !== undefined ? value : null;
91
96
  } else {
92
- definedValue = value !== undefined && value !== null ? value : [];
97
+ definedValue = value !== undefined && value !== null && value !== "" ? value : [];
93
98
  }
94
99
  return mapFieldValueDefined(definedValue, fieldMetadata);
95
100
  }
@@ -100,6 +105,7 @@ function getFieldValue(
100
105
  fieldMetadata: FieldMetadataWrapper,
101
106
  ): FieldValue {
102
107
  let value: Value | Genotype | undefined;
108
+ let multiValueArray;
103
109
  if (fieldMetadata.parent) {
104
110
  if (fieldMetadata.parent.id in valueContainer) {
105
111
  const parentValue = valueContainer[fieldMetadata.parent.id]!;
@@ -112,11 +118,15 @@ function getFieldValue(
112
118
  } else {
113
119
  if (parentValueArray.length < valueIndex) throw new RuntimeError();
114
120
  const multiValue = parentValueArray[valueIndex]!;
115
- if (!Array.isArray(multiValue)) throw new InvalidVcfError();
116
- const multiValueArray = multiValue as Value[];
117
-
118
- if (multiValueArray.length < fieldMetadata.index) throw new InvalidVcfError();
119
- value = multiValueArray[fieldMetadata.index]!;
121
+ if (!Array.isArray(multiValue)) {
122
+ multiValueArray = multiValue as ValueObject;
123
+ if (multiValueArray === null) throw new InvalidVcfError();
124
+ value = multiValueArray[fieldMetadata.id]!;
125
+ } else {
126
+ multiValueArray = multiValue as Value[];
127
+ if (multiValueArray.length < fieldMetadata.index) throw new InvalidVcfError();
128
+ value = multiValueArray[fieldMetadata.index]!;
129
+ }
120
130
  }
121
131
  } else {
122
132
  value = undefined;
@@ -128,7 +138,6 @@ function getFieldValue(
128
138
  value = undefined;
129
139
  }
130
140
  }
131
-
132
141
  return mapFieldValue(value, fieldMetadata);
133
142
  }
134
143
 
@@ -188,7 +197,7 @@ function createCategoricalValue(infoMetadata: FieldMetadata, value: ValueString)
188
197
  if (infoMetadata.categories === undefined) throw new RuntimeError();
189
198
 
190
199
  let valueDescription: ValueDescription | null;
191
- if (value !== null) {
200
+ if (value !== null && value !== "") {
192
201
  if (!(value in infoMetadata.categories)) {
193
202
  throw new RuntimeError(
194
203
  `invalid categorical field '${infoMetadata.id}' value '${value}' is not one of [${Object.keys(infoMetadata.categories).join(", ")}]`,
@@ -202,7 +211,7 @@ function createCategoricalValue(infoMetadata: FieldMetadata, value: ValueString)
202
211
  }
203
212
 
204
213
  function getRecordSample(sample: SampleContainer, record: Item<VcfRecord>): RecordSample {
205
- const recordSample = record.data.s[sample.item.data.index];
214
+ const recordSample = record.data.s[sample.item.data.index as number];
206
215
  if (recordSample === undefined) throw new ArrayIndexOutOfBoundsException();
207
216
  return recordSample;
208
217
  }
@@ -91,11 +91,11 @@ export const Help: Component = () => {
91
91
  <tbody>
92
92
  <tr>
93
93
  <th>Filename:</th>
94
- <td>{metadata()?.htsFile.uri}</td>
94
+ <td>{metadata().app.htsFile?.uri}</td>
95
95
  </tr>
96
96
  <tr>
97
97
  <th>Assembly:</th>
98
- <td>{metadata()?.htsFile.genomeAssembly}</td>
98
+ <td>{metadata().app.htsFile?.genomeAssembly}</td>
99
99
  </tr>
100
100
  </tbody>
101
101
  </Table>
@@ -5,11 +5,14 @@ import { getConfig, getMetadata, getRecordById, getSampleById } from "./data/dat
5
5
  import { parseVariantType } from "../utils/variantType.ts";
6
6
  import { VariantContainer } from "../components/VariantContainer.tsx";
7
7
  import { VariantBreadcrumb } from "../components/VariantBreadcrumb.tsx";
8
+ import { Item, Sample } from "@molgenis/vip-report-api";
9
+ import { getPedigreeSamples } from "../utils/sample.ts";
8
10
 
9
11
  export const SampleVariant: Component<RouteSectionProps> = (props) => {
10
12
  const variantType = () => parseVariantType(props.params.variantType);
11
13
  const sample = createAsync(() => getSampleById(props.params.sampleId));
12
- const record = createAsync(() => getRecordById(props.params.variantId));
14
+ const samples = (): Item<Sample>[] => (sample() ? getPedigreeSamples(sample()!) : []);
15
+ const record = createAsync(() => getRecordById(props.params.variantId, samples()));
13
16
  const metadata = createAsync(() => getMetadata());
14
17
  const config = createAsync(() => getConfig());
15
18
 
@@ -4,7 +4,7 @@ import { PageChangeEvent, Pager } from "../components/Pager";
4
4
  import { SearchBox } from "../components/SearchBox";
5
5
  import { Breadcrumb } from "../components/Breadcrumb";
6
6
  import { useStore } from "../store";
7
- import { Params, Query, QueryClause } from "@molgenis/vip-report-api";
7
+ import { Params, Query, QueryClause, SortOrder } from "@molgenis/vip-report-api";
8
8
  import { Loader } from "../components/Loader";
9
9
  import { fetchSamples } from "../utils/api.ts";
10
10
  import { Checkbox, CheckboxEvent } from "../components/form/Checkbox.tsx";
@@ -33,11 +33,11 @@ export const Samples: Component = () => {
33
33
  function createQuery(search: string | undefined, probandFilterValue: boolean | undefined): Query | null {
34
34
  const searchQuery: QueryClause | undefined =
35
35
  search !== undefined && search !== ""
36
- ? { selector: ["person", "individualId"], operator: "~=", args: search }
36
+ ? { selector: ["sample", "individualId"], operator: "~=", args: search }
37
37
  : undefined;
38
38
  const probandQuery: QueryClause | undefined =
39
39
  probandFilterValue !== undefined && probandFilterValue
40
- ? { selector: ["proband"], operator: "==", args: probandFilterValue }
40
+ ? { selector: ["sample", "proband"], operator: "==", args: probandFilterValue }
41
41
  : undefined;
42
42
  if (searchQuery !== undefined || probandQuery != undefined) {
43
43
  const args: QueryClause[] = [];
@@ -57,9 +57,16 @@ export const Samples: Component = () => {
57
57
  return null;
58
58
  }
59
59
 
60
+ const sortOrder: SortOrder[] = [
61
+ { property: ["sample", "familyId"], compare: "asc" },
62
+ { property: ["sample", "proband"], compare: "desc" },
63
+ { property: ["sample", "individualId"], compare: "asc" },
64
+ ];
65
+
60
66
  const params = (): Params => {
61
67
  return {
62
68
  query: createQuery(searchQuery(), probandFilterValue()) || undefined,
69
+ sort: sortOrder,
63
70
  page: page() || undefined,
64
71
  };
65
72
  };
@@ -1,5 +1,5 @@
1
1
  import { query } from "@solidjs/router";
2
- import { DecisionTree, Item } from "@molgenis/vip-report-api";
2
+ import { DecisionTree, Item, Sample } from "@molgenis/vip-report-api";
3
3
  import { VcfRecord } from "@molgenis/vip-report-vcf";
4
4
  import {
5
5
  fetchConfig,
@@ -24,7 +24,7 @@ export const getSampleById = query(
24
24
  );
25
25
 
26
26
  export const getRecordById = query(
27
- (id: string | undefined): Promise<Item<VcfRecord>> => fetchRecordById(parseId(id)),
27
+ (id: string | undefined, samples?: Item<Sample>[]): Promise<Item<VcfRecord>> => fetchRecordById(parseId(id), samples),
28
28
  "variant",
29
29
  );
30
30
 
@@ -36,6 +36,7 @@ describe("config filters composed", () => {
36
36
  const configBase: ConfigJsonFilterComposed = {
37
37
  type: "composed",
38
38
  name: "hpo",
39
+ defaultValue: "TEST"
39
40
  };
40
41
  const sample = {
41
42
  phenotypes: [
@@ -56,6 +57,7 @@ describe("config filters composed", () => {
56
57
  const filter = initConfigFilterComposed(configBase, configVip, metadata, sample) as ConfigFilterHpo;
57
58
  expect(filter.type).toStrictEqual("composed");
58
59
  expect(filter.id).toStrictEqual("hpo");
60
+ expect(filter.defaultValue).toStrictEqual("TEST");
59
61
  expect(filter.field).toStrictEqual({
60
62
  ...fieldHpo,
61
63
  categories: {
@@ -2,7 +2,7 @@ import { afterEach, describe, expect, test, vi } from "vitest";
2
2
  import { SampleContainer, VcfMetadataContainer } from "../../../src/utils/api.ts";
3
3
  import { initConfigFiltersGenotype, initConfigFiltersInfo } from "../../../src/utils/config/configFiltersField.ts";
4
4
  import { FieldMetadataWrapper, getInfoFieldsRegex, getSampleFieldsRegex } from "../../../src/utils/vcf.ts";
5
- import { ConfigJsonFieldGenotype, ConfigJsonFieldInfo } from "../../../src/types/config";
5
+ import {ConfigJsonFieldGenotype, ConfigJsonFieldInfo, ConfigJsonFilterInfo} from "../../../src/types/config";
6
6
 
7
7
  describe("config filters field", () => {
8
8
  vi.mock(import("../../../src/utils/vcf.ts"));
@@ -15,11 +15,12 @@ describe("config filters field", () => {
15
15
  const metadata = {} as VcfMetadataContainer;
16
16
 
17
17
  test("config matches one field", () => {
18
- const config: ConfigJsonFieldInfo = {
18
+ const config: ConfigJsonFilterInfo = {
19
19
  type: "info",
20
20
  name: "field",
21
21
  label: "my_field_label",
22
22
  description: "my_field_description",
23
+ defaultValue: "TEST"
23
24
  };
24
25
  const field = {
25
26
  id: "INFO/field",
@@ -35,6 +36,7 @@ describe("config filters field", () => {
35
36
  expect(filter.field).toStrictEqual(field);
36
37
  expect(filter.label()).toStrictEqual("my_field_label");
37
38
  expect(filter.description()).toStrictEqual("my_field_description");
39
+ expect(filter.defaultValue).toStrictEqual("TEST");
38
40
 
39
41
  expect(getInfoFieldsRegex).toHaveBeenCalledWith(metadata, /^field$/);
40
42
  });
@@ -4,66 +4,83 @@ import { initConfigFilterFixed } from "../../../src/utils/config/configFiltersFi
4
4
  describe("config filters fixed", () => {
5
5
  describe("initConfigFilterFixed", () => {
6
6
  test("chrom", () => {
7
- const filter = initConfigFilterFixed({ type: "fixed", name: "chrom" });
7
+ const filter = initConfigFilterFixed({ type: "fixed", name: "chrom", defaultValue: "test" });
8
8
 
9
9
  expect(filter.type).toStrictEqual("fixed");
10
10
  expect(filter.id).toStrictEqual("chrom");
11
11
  expect(filter.label()).toStrictEqual("Chromosome");
12
12
  expect(filter.description()).toStrictEqual(null);
13
+ expect(filter.defaultValue).toStrictEqual("test");
13
14
  });
14
15
 
15
16
  test("pos", () => {
16
- const filter = initConfigFilterFixed({ type: "fixed", name: "pos" });
17
+ const filter = initConfigFilterFixed({ type: "fixed", name: "pos", defaultValue: "test" });
17
18
 
18
19
  expect(filter.type).toStrictEqual("fixed");
19
20
  expect(filter.id).toStrictEqual("pos");
20
21
  expect(filter.label()).toStrictEqual("Position");
21
22
  expect(filter.description()).toStrictEqual(null);
23
+ expect(filter.defaultValue).toStrictEqual("test");
22
24
  });
23
25
 
24
26
  test("id", () => {
25
- const filter = initConfigFilterFixed({ type: "fixed", name: "id" });
27
+ const filter = initConfigFilterFixed({ type: "fixed", name: "id", defaultValue: "test" });
26
28
 
27
29
  expect(filter.type).toStrictEqual("fixed");
28
30
  expect(filter.id).toStrictEqual("id");
29
31
  expect(filter.label()).toStrictEqual("Identifiers");
30
32
  expect(filter.description()).toStrictEqual(null);
33
+ expect(filter.defaultValue).toStrictEqual("test");
31
34
  });
32
35
 
33
36
  test("ref", () => {
34
- const filter = initConfigFilterFixed({ type: "fixed", name: "ref" });
37
+ const filter = initConfigFilterFixed({ type: "fixed", name: "ref", defaultValue: "test" });
35
38
 
36
39
  expect(filter.type).toStrictEqual("fixed");
37
40
  expect(filter.id).toStrictEqual("ref");
38
41
  expect(filter.label()).toStrictEqual("Reference");
39
42
  expect(filter.description()).toStrictEqual(null);
43
+ expect(filter.defaultValue).toStrictEqual("test");
40
44
  });
41
45
 
42
46
  test("alt", () => {
43
- const filter = initConfigFilterFixed({ type: "fixed", name: "alt" });
47
+ const filter = initConfigFilterFixed({ type: "fixed", name: "alt", defaultValue: "test" });
44
48
 
45
49
  expect(filter.type).toStrictEqual("fixed");
46
50
  expect(filter.id).toStrictEqual("alt");
47
51
  expect(filter.label()).toStrictEqual("Alt");
48
52
  expect(filter.description()).toStrictEqual(null);
53
+ expect(filter.defaultValue).toStrictEqual("test");
49
54
  });
50
55
 
51
56
  test("qual", () => {
52
- const filter = initConfigFilterFixed({ type: "fixed", name: "qual" });
57
+ const filter = initConfigFilterFixed({ type: "fixed", name: "qual", defaultValue: "test" });
53
58
 
54
59
  expect(filter.type).toStrictEqual("fixed");
55
60
  expect(filter.id).toStrictEqual("qual");
56
61
  expect(filter.label()).toStrictEqual("Quality");
57
62
  expect(filter.description()).toStrictEqual(null);
63
+ expect(filter.defaultValue).toStrictEqual("test");
58
64
  });
59
65
 
60
66
  test("filter", () => {
67
+ const filter = initConfigFilterFixed({ type: "fixed", name: "filter", defaultValue: "test" });
68
+
69
+ expect(filter.type).toStrictEqual("fixed");
70
+ expect(filter.id).toStrictEqual("filter");
71
+ expect(filter.label()).toStrictEqual("Filter");
72
+ expect(filter.description()).toStrictEqual(null);
73
+ expect(filter.defaultValue).toStrictEqual("test");
74
+ });
75
+
76
+ test("filter without default", () => {
61
77
  const filter = initConfigFilterFixed({ type: "fixed", name: "filter" });
62
78
 
63
79
  expect(filter.type).toStrictEqual("fixed");
64
80
  expect(filter.id).toStrictEqual("filter");
65
81
  expect(filter.label()).toStrictEqual("Filter");
66
82
  expect(filter.description()).toStrictEqual(null);
83
+ expect(filter.defaultValue).toStrictEqual(undefined);
67
84
  });
68
85
 
69
86
  test("filter with custom label and description", () => {
@@ -8,9 +8,10 @@ import { createQuerySample } from "../../../src/utils/query/querySample.ts";
8
8
  import { createQueryFilters } from "../../../src/utils/query/queryFilter.ts";
9
9
  import { Query } from "@molgenis/vip-report-api";
10
10
  import { Config, ConfigFilters, ConfigVariants, ConfigVip, ConfigVipParams } from "../../../src/types/config";
11
- import { SampleContainer } from "../../../src/utils/api.ts";
11
+ import { MetadataContainer, SampleContainer, VcfMetadataContainer } from "../../../src/utils/api.ts";
12
12
  import { VariantType } from "../../../src/utils/variantType.ts";
13
13
  import { ConfigFilterFixed } from "../../../src/types/configFilter";
14
+ import { FieldMetadataWrapper } from "../../../src/utils/vcf.ts";
14
15
 
15
16
  describe("query", () => {
16
17
  vi.mock(import("../../../src/utils/query/queryFilter.ts"));
@@ -33,11 +34,20 @@ describe("query", () => {
33
34
  const queryVariantType: Query = { selector: "a", operator: "==", args: "b" };
34
35
  const querySample: Query = { selector: "c", operator: "==", args: "d" };
35
36
  const queryFilters: Query = { selector: "e", operator: "==", args: "f" };
37
+ const fieldMetadata = { id: "GT", number: { type: "NUMBER", count: 1 } } as FieldMetadataWrapper;
38
+ const vcfMetadata = {
39
+ format: { GT: fieldMetadata },
40
+ } as Partial<VcfMetadataContainer> as VcfMetadataContainer;
41
+ const meta = {
42
+ app: {},
43
+ records: vcfMetadata,
44
+ variantTypeIds: { }
45
+ } as MetadataContainer
36
46
  vi.mocked(createQueryVariantType).mockReturnValue(queryVariantType);
37
47
  vi.mocked(createQuerySample).mockReturnValue(querySample);
38
48
  vi.mocked(createQueryFilters).mockReturnValue(queryFilters);
39
49
 
40
- expect(createQuery(config, variantType, sample, filterValues)).toStrictEqual({
50
+ expect(createQuery(config, meta, variantType, sample, filterValues)).toStrictEqual({
41
51
  args: [
42
52
  { selector: "a", operator: "==", args: "b" },
43
53
  { selector: "c", operator: "==", args: "d" },
@@ -46,28 +56,46 @@ describe("query", () => {
46
56
  operator: "and",
47
57
  });
48
58
  expect(createQueryVariantType).toHaveBeenCalledWith(variantType);
49
- expect(createQuerySample).toHaveBeenCalledWith(configVip, sample);
59
+ expect(createQuerySample).toHaveBeenCalledWith(configVip, sample, meta);
50
60
  expect(createQueryFilters).toHaveBeenCalledWith(configFilters, filterValues);
51
61
  });
52
62
 
53
63
  test("sample", () => {
54
64
  const querySample: Query = { selector: "c", operator: "==", args: "d" };
65
+ const fieldMetadata = { id: "GT", number: { type: "NUMBER", count: 1 } } as FieldMetadataWrapper;
66
+ const vcfMetadata = {
67
+ format: { GT: fieldMetadata },
68
+ } as Partial<VcfMetadataContainer> as VcfMetadataContainer;
69
+ const meta = {
70
+ app: {},
71
+ records: vcfMetadata,
72
+ variantTypeIds: { }
73
+ } as MetadataContainer
55
74
  vi.mocked(createQueryVariantType).mockReturnValue(null);
56
75
  vi.mocked(createQuerySample).mockReturnValue(querySample);
57
76
  vi.mocked(createQueryFilters).mockReturnValue(null);
58
77
 
59
- expect(createQuery(config, variantType, sample, filterValues)).toStrictEqual(querySample);
78
+ expect(createQuery(config, meta, variantType, sample, filterValues)).toStrictEqual(querySample);
60
79
  expect(createQueryVariantType).toHaveBeenCalledWith(variantType);
61
- expect(createQuerySample).toHaveBeenCalledWith(configVip, sample);
80
+ expect(createQuerySample).toHaveBeenCalledWith(configVip, sample, meta);
62
81
  expect(createQueryFilters).toHaveBeenCalledWith(configFilters, filterValues);
63
82
  });
64
83
 
65
84
  test("variant type=null, sample=null and filters=null", () => {
66
85
  const variantType = { id: "all" } as VariantType;
86
+ const fieldMetadata = { id: "GT", number: { type: "NUMBER", count: 1 } } as FieldMetadataWrapper;
87
+ const vcfMetadata = {
88
+ format: { GT: fieldMetadata },
89
+ } as Partial<VcfMetadataContainer> as VcfMetadataContainer;
90
+ const meta = {
91
+ app: {},
92
+ records: vcfMetadata,
93
+ variantTypeIds: { }
94
+ } as MetadataContainer
67
95
  vi.mocked(createQueryVariantType).mockReturnValue(null);
68
96
  vi.mocked(createQueryFilters).mockReturnValue(null);
69
97
 
70
- expect(createQuery(config, variantType, null, filterValues)).toStrictEqual(null);
98
+ expect(createQuery(config, meta, variantType, null, filterValues)).toStrictEqual(null);
71
99
  expect(createQueryVariantType).toHaveBeenCalledWith(variantType);
72
100
  expect(createQueryFilters).toHaveBeenCalledWith(configFilters, filterValues);
73
101
  });
@@ -175,7 +175,7 @@ describe("query filters", () => {
175
175
 
176
176
  test("multi_value=false nested_value=false args=single", () => {
177
177
  const value: FilterValueString = ["y"];
178
- expect(createQueryFilterString(selector, value, false, false)).toStrictEqual({
178
+ expect(createQueryFilterString(selector, value, false)).toStrictEqual({
179
179
  selector: ["x"],
180
180
  operator: "in",
181
181
  args: ["y"],
@@ -184,45 +184,18 @@ describe("query filters", () => {
184
184
 
185
185
  test("multi_value=false nested_value=false args=multi", () => {
186
186
  const value: FilterValueString = ["y", "z"];
187
- expect(createQueryFilterString(selector, value, false, false)).toStrictEqual({
187
+ expect(createQueryFilterString(selector, value, false)).toStrictEqual({
188
188
  selector: ["x"],
189
189
  operator: "in",
190
190
  args: ["y", "z"],
191
191
  });
192
192
  });
193
193
 
194
- test("multi_value=true nested_value=false args=single", () => {
195
- const value: FilterValueString = ["y"];
196
- expect(createQueryFilterString(selector, value, true, false)).toStrictEqual({
197
- selector: ["x"],
198
- operator: "has_any",
199
- args: ["y"],
200
- });
201
- });
202
-
203
- test("multi_value=false nested_value=true args=single", () => {
204
- const value: FilterValueString = ["y"];
205
- expect(createQueryFilterString(selector, value, false, true)).toStrictEqual({
206
- selector: ["x"],
207
- operator: "has_any",
208
- args: ["y"],
209
- });
210
- });
211
-
212
- test("multi_value=true nested_value=true args=single", () => {
213
- const value: FilterValueString = ["y"];
214
- expect(createQueryFilterString(selector, value, true, true)).toStrictEqual({
215
- selector: ["x"],
216
- operator: "any_has_any",
217
- args: ["y"],
218
- });
219
- });
220
-
221
194
  describe("categorical select null", () => {
222
195
  test("categorical __null", () => {
223
196
  const value: FilterValueString = ["__null"];
224
197
 
225
- expect(createQueryFilterString(selector, value, false, false)).toStrictEqual({
198
+ expect(createQueryFilterString(selector, value, false)).toStrictEqual({
226
199
  selector: ["x"],
227
200
  operator: "in",
228
201
  args: [null],
@@ -232,7 +205,7 @@ describe("query filters", () => {
232
205
  test("categorical category and __null", () => {
233
206
  const value: FilterValueString = ["c0", "__null"];
234
207
 
235
- expect(createQueryFilterString(selector, value, false, false)).toStrictEqual({
208
+ expect(createQueryFilterString(selector, value, false)).toStrictEqual({
236
209
  selector: ["x"],
237
210
  operator: "in",
238
211
  args: ["c0", null],
@@ -57,7 +57,7 @@ describe("query composed filters", () => {
57
57
  args: [query, queryPos],
58
58
  operator: "and",
59
59
  });
60
- expect(createQueryFilterString).toHaveBeenCalledWith(["c"], ["chr1"], false, false);
60
+ expect(createQueryFilterString).toHaveBeenCalledWith(["c"], ["chr1"], false);
61
61
  expect(createQueryFilterClosedInterval).toHaveBeenCalledWith(["p"], { left: 1, right: 2 });
62
62
  });
63
63
 
@@ -72,7 +72,7 @@ describe("query composed filters", () => {
72
72
  args: [query, queryPos],
73
73
  operator: "and",
74
74
  });
75
- expect(createQueryFilterString).toHaveBeenCalledWith(["c"], ["chr1"], false, false);
75
+ expect(createQueryFilterString).toHaveBeenCalledWith(["c"], ["chr1"], false);
76
76
  expect(createQueryFilterClosedInterval).toHaveBeenCalledWith(["p"], { left: 1, right: undefined });
77
77
  });
78
78
 
@@ -87,7 +87,7 @@ describe("query composed filters", () => {
87
87
  args: [query, queryPos],
88
88
  operator: "and",
89
89
  });
90
- expect(createQueryFilterString).toHaveBeenCalledWith(["c"], ["chr1"], false, false);
90
+ expect(createQueryFilterString).toHaveBeenCalledWith(["c"], ["chr1"], false);
91
91
  expect(createQueryFilterClosedInterval).toHaveBeenCalledWith(["p"], { left: undefined, right: 2 });
92
92
  });
93
93
 
@@ -96,7 +96,7 @@ describe("query composed filters", () => {
96
96
  const value = { chromosome: "chr1" } as FilterValueLocus;
97
97
  vi.mocked(createQueryFilterString).mockReturnValue(query);
98
98
  expect(createQueryFilterComposed(config, value)).toStrictEqual(query);
99
- expect(createQueryFilterString).toHaveBeenCalledWith(["c"], ["chr1"], false, false);
99
+ expect(createQueryFilterString).toHaveBeenCalledWith(["c"], ["chr1"], false);
100
100
  });
101
101
  });
102
102
 
@@ -104,9 +104,9 @@ describe("query composed filters", () => {
104
104
  const config = {
105
105
  type: "composed",
106
106
  id: "composed/allelicImbalance",
107
- sample: { item: { data: { index: 1 } } } as SampleContainer,
107
+ sample: { item: { id: 1, data: { index: 1 } } } as SampleContainer,
108
108
  viabField: { id: "VIAB" },
109
- genotypeField: { id: "GT" },
109
+ genotypeField: { id: "GT_type" },
110
110
  } as ConfigFilterAllelicImbalance;
111
111
  const queryInterval: Query = { selector: "viab", operator: "==", args: "inside" };
112
112
  const queryIntervalClosed: Query = { selector: "viab", operator: "==", args: "outside" };
@@ -115,14 +115,14 @@ describe("query composed filters", () => {
115
115
  args: [
116
116
  {
117
117
  args: [
118
- { selector: ["s", 1, "GT", "t"], operator: "in", args: ["hom_a", "hom_r"] },
118
+ { selector: ["s", 1, "GT_type"], operator: "in", args: ["HOM_ALT", "HOM_REF"] },
119
119
  { selector: "viab", operator: "==", args: "inside" },
120
120
  ],
121
121
  operator: "and",
122
122
  },
123
123
  {
124
124
  args: [
125
- { selector: ["s", 1, "GT", "t"], operator: "==", args: "het" },
125
+ { selector: ["s", 1, "GT_type"], operator: "==", args: "HET" },
126
126
  { selector: "viab", operator: "==", args: "outside" },
127
127
  ],
128
128
  operator: "and",
@@ -146,14 +146,14 @@ describe("query composed filters", () => {
146
146
  args: [
147
147
  {
148
148
  args: [
149
- { selector: ["s", 1, "GT", "t"], operator: "in", args: ["hom_a", "hom_r"] },
149
+ { selector: ["s", 1, "GT_type"], operator: "in", args: ["HOM_ALT", "HOM_REF"] },
150
150
  { selector: "viab", operator: "==", args: "outside" },
151
151
  ],
152
152
  operator: "and",
153
153
  },
154
154
  {
155
155
  args: [
156
- { selector: ["s", 1, "GT", "t"], operator: "==", args: "het" },
156
+ { selector: ["s", 1, "GT_type"], operator: "==", args: "HET" },
157
157
  { selector: "viab", operator: "==", args: "inside" },
158
158
  ],
159
159
  operator: "and",
@@ -205,7 +205,7 @@ describe("query composed filters", () => {
205
205
  const config = {
206
206
  type: "composed",
207
207
  id: "composed/inheritanceMatch",
208
- sample: { item: { data: { index: 1 } } } as SampleContainer,
208
+ sample: { item: { id: 1, data: { index: 1 } } } as SampleContainer,
209
209
  vimField: { id: "VIM" },
210
210
  } as ConfigFilterInheritanceMatch;
211
211
 
@@ -259,7 +259,7 @@ describe("query composed filters", () => {
259
259
  const config = {
260
260
  type: "composed",
261
261
  id: "composed/deNovo",
262
- sample: { item: { data: { index: 1 } } } as SampleContainer,
262
+ sample: { item: { id: 1, data: { index: 1 } } } as SampleContainer,
263
263
  vidField: { id: "VID" },
264
264
  } as ConfigFilterDeNovo;
265
265
 
@@ -300,7 +300,7 @@ describe("query composed filters", () => {
300
300
  });
301
301
 
302
302
  test("vipCS", () => {
303
- const sample = { item: { data: { index: 1 } } } as SampleContainer;
303
+ const sample = { item: { id: 1, data: { index: 1 } } } as SampleContainer;
304
304
  const field = { id: "f", number: {}, categories: {} };
305
305
  const config = { type: "composed", id: "composed/vipCS", field, sample } as ConfigFilterVipCS;
306
306
  const value = ["chr1"] as FilterValueVipCS;