@sdeverywhere/plugin-check 0.3.0 → 0.3.2

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,11 +1,22 @@
1
1
  // Copyright (c) 2022 Climate Interactive / New Venture Fund
2
2
 
3
- import type { Bundle, CompareOptions, ConfigOptions, DatasetKey } from '@sdeverywhere/check-core'
3
+ import type {
4
+ Bundle,
5
+ ComparisonOptions,
6
+ ComparisonScenarioSpec,
7
+ ComparisonSpecs,
8
+ ConfigInitOptions,
9
+ ConfigOptions,
10
+ DatasetKey,
11
+ InputId,
12
+ InputVar
13
+ } from '@sdeverywhere/check-core'
4
14
 
5
- import { DatasetManager, ScenarioManager } from '@sdeverywhere/check-core'
6
-
7
- // Load the yaml test files
8
- const yamlGlob = import.meta.glob(__YAML_PATH__, {
15
+ // Load the yaml test files. The `./__YAML_PATH__` part will be replaced by Vite
16
+ // (see `vite-config-for-tests.ts`). Note that we provide a placeholder here that
17
+ // looks like a valid glob pattern, since Vite's dependency resolver will report
18
+ // errors if it is invalid (not a literal).
19
+ const yamlGlob = import.meta.glob('./__YAML_PATH__', {
9
20
  eager: true,
10
21
  as: 'raw'
11
22
  })
@@ -24,56 +35,125 @@ const renamedDatasetKeys: Map<DatasetKey, DatasetKey> = new Map([
24
35
  // ['Model_old_name', 'Model_new_name']
25
36
  ])
26
37
 
27
- export interface BundleOptions {
28
- nameL?: string
29
- nameR?: string
30
- }
31
-
32
38
  // TODO: Make this pluggable (with default implementation similar to below)
33
39
  export async function getConfigOptions(
34
40
  bundleL: Bundle | undefined,
35
41
  bundleR: Bundle,
36
- opts: BundleOptions
42
+ opts?: ConfigInitOptions
37
43
  ): Promise<ConfigOptions> {
38
- // Only include compare options if the baseline bundle is defined (and
44
+ // Only include comparison options if the baseline bundle is defined (and
39
45
  // has the same version)
40
- let compareOptions: CompareOptions
46
+ let comparisonOptions: ComparisonOptions
41
47
  if (bundleL && bundleL.version === bundleR.version) {
42
48
  // Configure the set of input scenarios used for comparisons. This includes
43
49
  // the default matrix of scenarios; for any output variable, the model will
44
50
  // be run:
45
51
  // - once with all inputs at their default
46
- // - once with all inputs at their minimum
47
- // - once with all inputs at their maximum
48
52
  // - twice for each input
49
53
  // - once with single input at its minimum
50
54
  // - once with single input at its maximum
51
- const scenarios = new ScenarioManager(bundleL, bundleR)
52
- scenarios.addScenarioMatrix()
55
+ // TODO: Also read specs from comparison yaml files
56
+ const baseComparisonSpecs = createBaseComparisonSpecs(bundleL, bundleR)
53
57
 
54
- // Configure the dataset keys (corresponding to the available model outputs
55
- // in the given bundles) that can be used to compare two versions of the model
56
- const datasets = new DatasetManager(bundleL, bundleR, renamedDatasetKeys)
57
-
58
- compareOptions = {
58
+ comparisonOptions = {
59
59
  baseline: {
60
- name: opts?.nameL || 'baseline',
60
+ name: opts?.bundleNameL || 'baseline',
61
61
  bundle: bundleL
62
62
  },
63
63
  thresholds: [1, 5, 10],
64
- scenarios,
65
- datasets
64
+ specs: [baseComparisonSpecs],
65
+ datasets: {
66
+ renamedDatasetKeys
67
+ }
66
68
  }
67
69
  }
68
70
 
69
71
  return {
70
72
  current: {
71
- name: opts?.nameR || 'current',
73
+ name: opts?.bundleNameR || 'current',
72
74
  bundle: bundleR
73
75
  },
74
76
  check: {
75
77
  tests
76
78
  },
77
- compare: compareOptions
79
+ comparison: comparisonOptions
80
+ }
81
+ }
82
+
83
+ function createBaseComparisonSpecs(bundleL: Bundle, bundleR: Bundle): ComparisonSpecs {
84
+ // Get the union of all input IDs appearing in left and/or right
85
+ const allInputIds: Set<InputId> = new Set()
86
+ const addInputs = (bundle: Bundle, inputsMap: Map<InputId, InputVar>) => {
87
+ for (const inputVar of bundle.modelSpec.inputVars.values()) {
88
+ allInputIds.add(inputVar.inputId)
89
+ inputsMap.set(inputVar.inputId, inputVar)
90
+ }
91
+ }
92
+ const inputsByIdL: Map<InputId, InputVar> = new Map()
93
+ const inputsByIdR: Map<InputId, InputVar> = new Map()
94
+ addInputs(bundleL, inputsByIdL)
95
+ addInputs(bundleR, inputsByIdR)
96
+
97
+ // Create an "all inputs at default" scenario
98
+ const scenarios: ComparisonScenarioSpec[] = []
99
+ scenarios.push({
100
+ kind: 'scenario-with-all-inputs',
101
+ id: 'all_inputs_at_default',
102
+ title: 'All inputs',
103
+ subtitle: 'at default',
104
+ position: 'default'
105
+ })
106
+
107
+ // Create "input at min/max" scenarios for all inputs (that appear in either "left" or "right")
108
+ const addScenario = (inputId: InputId, position: 'min' | 'max') => {
109
+ const inputL = inputsByIdL.get(inputId)
110
+ const inputR = inputsByIdR.get(inputId)
111
+ if (inputL === undefined || inputR === undefined) {
112
+ return
113
+ }
114
+
115
+ // Don't add a scenario if the input's min or max is equal to its default (this is already
116
+ // covered by the `all_inputs_at_default` scenario from above)
117
+ if (position === 'min') {
118
+ if (inputL.minValue === inputL.defaultValue && inputR.minValue === inputR.defaultValue) {
119
+ return
120
+ }
121
+ } else {
122
+ if (inputL.maxValue === inputL.defaultValue && inputR.maxValue === inputR.defaultValue) {
123
+ return
124
+ }
125
+ }
126
+
127
+ // Derive the scenario name from the related slider name (or if not defined, the variable name)
128
+ const input = inputR || inputL
129
+ const path = input.relatedItem?.locationPath
130
+ const title = path ? path[path.length - 1] : input.varName
131
+
132
+ scenarios.push({
133
+ kind: 'scenario-with-inputs',
134
+ id: `id_${inputId}_at_${position}`,
135
+ title,
136
+ subtitle: `at ${position}`,
137
+ inputs: [
138
+ {
139
+ kind: 'input-at-position',
140
+ inputName: `id ${inputId}`,
141
+ position
142
+ }
143
+ ]
144
+ })
145
+ }
146
+
147
+ // Add an "at min" and "at max" scenario for each input
148
+ const inputIds = [...allInputIds]
149
+ for (const inputId of inputIds) {
150
+ addScenario(inputId, 'min')
151
+ addScenario(inputId, 'max')
152
+ }
153
+
154
+ return {
155
+ scenarios,
156
+ scenarioGroups: [],
157
+ viewGroups: []
78
158
  }
79
159
  }
@@ -11,7 +11,7 @@
11
11
  // Enable warnings for transpilation-unsafe code
12
12
  "isolatedModules": true,
13
13
  // Enable strict enforcement of `import type`
14
- "importsNotUsedAsValues": "error",
14
+ "verbatimModuleSyntax": true,
15
15
  "noImplicitAny": true,
16
16
  "noUnusedLocals": true,
17
17
  "noUnusedParameters": true,
@@ -1,5 +0,0 @@
1
- // Copyright (c) 2022 Climate Interactive / New Venture Fund
2
-
3
- // These values are injected by Vite at build time, so we need to
4
- // declare types for them here
5
- declare const __YAML_PATH__: string