|
| 1 | +<!doctype html> |
| 2 | +<html lang="en"> |
| 3 | +<head> |
| 4 | + <meta charset="utf-8"> |
| 5 | + <title>Operator Docu</title> |
| 6 | + <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 7 | + <link rel="stylesheet" href="global.css" /> |
| 8 | +</head> |
| 9 | +<body class="loop-docu"> |
| 10 | +<h1>Converter Operator Documentation</h1> |
| 11 | +<hr /> |
| 12 | +<h1>Backend</h1> |
| 13 | +<h2>Overview</h2> |
| 14 | +<p>The converter can apply additional calculations to the selected <strong>x-values</strong> and <strong>y-values</strong>. These calculations are stored in <code>xOperations</code> and <code>yOperations</code> and are executed when an output table is processed.</p> |
| 15 | +<p>There are two layers involved:</p> |
| 16 | +<ol> |
| 17 | +<li><strong>Four operation types</strong>, which define <em>where</em> the right-hand value comes from.</li> |
| 18 | +<li><strong>Four mathematical operators</strong>, which define <em>how</em> that value is applied to the current x- or y-value.</li> |
| 19 | +</ol> |
| 20 | +<p>The four operation types are:</p> |
| 21 | +<ul> |
| 22 | +<li><code>column</code></li> |
| 23 | +<li><code>value</code></li> |
| 24 | +<li><code>metadata_value</code></li> |
| 25 | +<li><code>header_value</code></li> |
| 26 | +</ul> |
| 27 | +<p>The four mathematical operators are:</p> |
| 28 | +<ul> |
| 29 | +<li><code>+</code> addition</li> |
| 30 | +<li><code>-</code> subtraction</li> |
| 31 | +<li><code>*</code> multiplication</li> |
| 32 | +<li><code>:</code> division</li> |
| 33 | +</ul> |
| 34 | +<p>In other words:<br /> |
| 35 | +A profile can say things like:</p> |
| 36 | +<ul> |
| 37 | +<li>“Take the x-values and add a fixed number.”</li> |
| 38 | +<li>“Take the y-values and multiply them by values from another column.”</li> |
| 39 | +<li>“Take the y-values and divide them by a value found in metadata.”</li> |
| 40 | +<li>“Take the x-values and subtract a number extracted from the table header.”</li> |
| 41 | +</ul> |
| 42 | +<hr /> |
| 43 | +<h2>Data Structures Involved</h2> |
| 44 | +<p>For each output table, the converter uses these fields in the table configuration:</p> |
| 45 | +<ul> |
| 46 | +<li><code>xColumn</code><br /> |
| 47 | + The base source of the x-values.</li> |
| 48 | +<li><code>yColumn</code><br /> |
| 49 | + The base source of the y-values.</li> |
| 50 | +<li><code>xOperations</code><br /> |
| 51 | + A list of operations that are applied to all x-values.</li> |
| 52 | +<li><code>yOperations</code><br /> |
| 53 | + A list of operations that are applied to all y-values.</li> |
| 54 | +<li><code>xOperationsDescription</code> and <code>yOperationsDescription</code><br /> |
| 55 | + Auto-generated and user-editable descriptions of the configured operations.</li> |
| 56 | +</ul> |
| 57 | +<p>Each entry in <code>xOperations</code> or <code>yOperations</code> contains at least:</p> |
| 58 | +<ul> |
| 59 | +<li><code>type</code> – the operation type</li> |
| 60 | +<li><code>operator</code> – one of <code>+</code>, <code>-</code>, <code>*</code>, <code>:</code></li> |
| 61 | +</ul> |
| 62 | +<p>Depending on the type, additional fields are used, such as <code>column</code>, <code>value</code>, <code>table</code>, <code>regex</code>, <code>line</code>, and <code>ignore_missing_values</code>.</p> |
| 63 | +<hr /> |
| 64 | +<h2>Method: <code>process(self)</code></h2> |
| 65 | +<h3>Purpose</h3> |
| 66 | +<p>Prepare the raw x/y data, apply all configured x/y operations, and build the final output table.</p> |
| 67 | +<h3>Step-by-step behavior</h3> |
| 68 | +<ol> |
| 69 | +<li><strong>Read the base x and y columns</strong><br /> |
| 70 | + The converter reads the values from <code>xColumn</code> into <code>x_rows</code> and from <code>yColumn</code> into <code>y_rows</code>.</li> |
| 71 | +<li><strong>Prepare data for column-based operations</strong><br /> |
| 72 | + If an operation has type <code>column</code>, the converter preloads the referenced column values into <code>operation['rows']</code>.</li> |
| 73 | +<li><strong>Apply x-operations</strong><br /> |
| 74 | + All entries from <code>xOperations</code> are executed in their stored order.</li> |
| 75 | +<li><strong>Apply y-operations</strong><br /> |
| 76 | + All entries from <code>yOperations</code> are executed in their stored order.</li> |
| 77 | +<li><strong>Handle calculation failures</strong><br /> |
| 78 | + If an operation fails and the failure must not be ignored, the converter marks the calculation as failed and clears the output rows.</li> |
| 79 | +</ol> |
| 80 | +<h3>Important consequence</h3> |
| 81 | +<p><strong>The order of operations matters.</strong><br /> |
| 82 | +If several operations are configured, each one works on the result of the previous one. So <code>* 2</code> followed by <code>+ 5</code> is not the same as <code>+ 5</code> followed by <code>* 2</code>.</p> |
| 83 | +<hr /> |
| 84 | +<h2>Method: <code>_run_operation(self, rows, operation)</code></h2> |
| 85 | +<h3>Purpose</h3> |
| 86 | +<p>Execute one configured operation on all values of one target row set, meaning either all x-values or all y-values.</p> |
| 87 | +<p>For each row, the method first determines the right-hand operand based on <code>operation.type</code>. Then it applies the selected mathematical operator by calling <code>apply_operation</code>.</p> |
| 88 | +<hr /> |
| 89 | +<h3>The four operation types</h3> |
| 90 | +<h4>1. <code>column</code> – Value from another table column</h4> |
| 91 | +<ul> |
| 92 | +<li>The operation points to a column using <code>operation.column.tableIndex</code> and <code>operation.column.columnIndex</code>.</li> |
| 93 | +<li>During preparation, all values from that referenced column are stored in <code>operation['rows']</code>.</li> |
| 94 | +<li>During execution, the converter reads the value at the same row position from that preloaded list.</li> |
| 95 | +</ul> |
| 96 | +<p><strong>Plain-language interpretation:</strong><br /> |
| 97 | +The current x- or y-value is combined with the value from another column at the same row index.</p> |
| 98 | +<hr /> |
| 99 | +<h4>2. <code>value</code> – Fixed scalar value</h4> |
| 100 | +<ul> |
| 101 | +<li>The operation stores a direct numeric value in <code>operation.value</code>.</li> |
| 102 | +<li>The same number is used for every row.</li> |
| 103 | +</ul> |
| 104 | +<p><strong>Plain-language interpretation:</strong><br /> |
| 105 | +All x- or y-values are modified by the same fixed number, such as <code>+ 5</code> or <code>* 1000</code>.</p> |
| 106 | +<hr /> |
| 107 | +<h4>3. <code>metadata_value</code> – Value from table metadata</h4> |
| 108 | +<ul> |
| 109 | +<li>The operation stores a metadata key in <code>operation.value</code> and a reference table index in <code>operation.table</code>.</li> |
| 110 | +<li>The converter reads the metadata value from <code>self.input_tables[int(operation.table)]['metadata']</code>.</li> |
| 111 | +<li>That metadata value is then used as the right-hand operand for all rows.</li> |
| 112 | +<li>If the metadata value is missing or not numeric, behavior depends on <code>ignore_missing_values</code>.</li> |
| 113 | +</ul> |
| 114 | +<p><strong>Plain-language interpretation:</strong><br /> |
| 115 | +The current x- or y-value is combined with a numeric value taken from the metadata of a selected input table.</p> |
| 116 | +<hr /> |
| 117 | +<h4>4. <code>header_value</code> – Value extracted from the table header via regex</h4> |
| 118 | +<ul> |
| 119 | +<li>The operation stores a table index, an optional line number, and a regex pattern.</li> |
| 120 | +<li>The helper method <code>_search_regex</code> searches either a specific header line or the whole header text.</li> |
| 121 | +<li>If a regex match is found and the regex contains a capture group, the first capture group is used.</li> |
| 122 | +<li>If there is no capture group, the full match is used.</li> |
| 123 | +<li>The extracted text must be numeric to be usable in the calculation.</li> |
| 124 | +</ul> |
| 125 | +<p><strong>Plain-language interpretation:</strong><br /> |
| 126 | +The current x- or y-value is combined with a number that is found somewhere in the selected table header.</p> |
| 127 | +<hr /> |
| 128 | +<h3>How missing or invalid values are handled</h3> |
| 129 | +<p>After the right-hand value has been determined, the converter tries to convert it to a float.</p> |
| 130 | +<ul> |
| 131 | +<li>If conversion succeeds, the operation can continue.</li> |
| 132 | +<li>If conversion fails and <code>ignore_missing_values</code> is <strong>false</strong>, a <code>CalculationError</code> is raised.</li> |
| 133 | +<li>If conversion fails and <code>ignore_missing_values</code> is <strong>true</strong>, that operation is skipped.</li> |
| 134 | +</ul> |
| 135 | +<p>This special handling is mainly relevant for <code>metadata_value</code> and <code>header_value</code>, because those two types may depend on metadata or free text that is missing or non-numeric.</p> |
| 136 | +<hr /> |
| 137 | +<h2>Method: <code>apply_operation(self, value, op_value, op_operator)</code></h2> |
| 138 | +<h3>Purpose</h3> |
| 139 | +<p>Apply the mathematical operator to the current row value (<code>value</code>) and the resolved operation value (<code>op_value</code>).</p> |
| 140 | +<h3>Supported operators</h3> |
| 141 | +<pre><code class="language-python">if op_operator == '+': |
| 142 | + return float_value + float(op_value) |
| 143 | +if op_operator == '-': |
| 144 | + return float_value - float(op_value) |
| 145 | +if op_operator == '*': |
| 146 | + return float_value * float(op_value) |
| 147 | +if op_operator == ':': |
| 148 | + return float_value / float(op_value) |
| 149 | +</code></pre> |
| 150 | +<h3>Meaning of the operators</h3> |
| 151 | +<ul> |
| 152 | +<li><strong><code>+</code></strong><br /> |
| 153 | + Adds the operation value to the current x/y value.</li> |
| 154 | +<li><strong><code>-</code></strong><br /> |
| 155 | + Subtracts the operation value from the current x/y value.</li> |
| 156 | +<li><strong><code>*</code></strong><br /> |
| 157 | + Multiplies the current x/y value by the operation value.</li> |
| 158 | +<li><strong><code>:</code></strong><br /> |
| 159 | + Divides the current x/y value by the operation value.</li> |
| 160 | +</ul> |
| 161 | +<h3>Important implementation detail</h3> |
| 162 | +<p>The current implementation only applies the operation when <code>op_value</code> is truthy. This means that a right-hand value of <code>0</code> will not be applied and will leave the current row unchanged.</p> |
| 163 | +<hr /> |
| 164 | +<h2>Overall Summary</h2> |
| 165 | +<ul> |
| 166 | +<li><code>process(self)</code> prepares the x/y data and executes all configured operations in order.</li> |
| 167 | +<li><code>_run_operation(self, rows, operation)</code> determines the right-hand value depending on the operation type.</li> |
| 168 | +<li><code>apply_operation(self, value, op_value, op_operator)</code> performs the actual mathematical calculation.</li> |
| 169 | +</ul> |
| 170 | +<p>So the backend logic can be summarized like this:</p> |
| 171 | +<ol> |
| 172 | +<li>Start with the selected x/y column values.</li> |
| 173 | +<li>For each configured operation, determine where the right-hand value comes from.</li> |
| 174 | +<li>Apply one of the four mathematical operators.</li> |
| 175 | +<li>Store the updated x/y values in the final output table.</li> |
| 176 | +</ol> |
| 177 | + |
| 178 | +<hr /> |
| 179 | +<h1>Frontend</h1> |
| 180 | +<p>This part of the frontend is the visual configuration for the x/y operations you saw in the backend.</p> |
| 181 | +<p>It lets a user decide:</p> |
| 182 | +<ol> |
| 183 | +<li><strong>Which column should be used as the base x- or y-values</strong>, and</li> |
| 184 | +<li><strong>Which additional operations should be applied to those values before export.</strong></li> |
| 185 | +</ol> |
| 186 | +<hr /> |
| 187 | +<h2>1. Choosing the base x- and y-columns</h2> |
| 188 | +<pre><code class="language-jsx"><TableColumn |
| 189 | + table={table.table} |
| 190 | + label="Which column should be used as x-values?" |
| 191 | + columnKey="xColumn" |
| 192 | + operationsKey="xOperations" |
| 193 | + ... |
| 194 | +/> |
| 195 | + |
| 196 | +<TableColumn |
| 197 | + table={table.table} |
| 198 | + label="Which column should be used as y-values?" |
| 199 | + columnKey="yColumn" |
| 200 | + operationsKey="yOperations" |
| 201 | + ... |
| 202 | +/> |
| 203 | +</code></pre> |
| 204 | +<h3>What the user sees</h3> |
| 205 | +<p>A selection field for the x-column and a selection field for the y-column.</p> |
| 206 | +<p>These define the <strong>starting values</strong> before any operation is applied.</p> |
| 207 | +<h3>How this relates to the backend</h3> |
| 208 | +<p>The selections are stored as:</p> |
| 209 | +<ul> |
| 210 | +<li><code>table.xColumn</code></li> |
| 211 | +<li><code>table.yColumn</code></li> |
| 212 | +</ul> |
| 213 | +<p>The backend reads these values first, then applies <code>xOperations</code> and <code>yOperations</code> on top of them.</p> |
| 214 | +<p><strong>Special case:</strong><br /> |
| 215 | +If the table header says <code>DATA CLASS = XYDATA</code>, the x-values are configured differently in the frontend, via metadata-related identifiers such as <code>FIRSTX</code>, <code>LASTX</code>, and <code>DELTAX</code>, instead of the normal x-column operation block.</p> |
| 216 | +<hr /> |
| 217 | +<h2>2. The operator dropdown</h2> |
| 218 | +<pre><code class="language-jsx"><Form.Select size="sm" value={value} onChange={event => onChange(event.target.value)}> |
| 219 | + <option value="+">+</option> |
| 220 | + <option value="-">-</option> |
| 221 | + <option value="*">*</option> |
| 222 | + <option value=":">:</option> |
| 223 | +</Form.Select> |
| 224 | +</code></pre> |
| 225 | +<h3>What the user sees</h3> |
| 226 | +<p>Each configured operation row starts with a small dropdown containing:</p> |
| 227 | +<ul> |
| 228 | +<li><code>+</code></li> |
| 229 | +<li><code>-</code></li> |
| 230 | +<li><code>*</code></li> |
| 231 | +<li><code>:</code></li> |
| 232 | +</ul> |
| 233 | +<h3>How this relates to the backend</h3> |
| 234 | +<p>The selected symbol is stored in <code>operation.operator</code>. In the backend, this value is passed to <code>apply_operation</code>, where it determines whether the calculation becomes addition, subtraction, multiplication, or division.</p> |
| 235 | +<hr /> |
| 236 | +<h2>3. The four operation types</h2> |
| 237 | +<p>The frontend offers four buttons for creating operations. Each new operation is added either to <code>xOperations</code> or to <code>yOperations</code>.</p> |
| 238 | +<pre><code class="language-jsx"><Button onClick={() => addOperation(operationsKey, 'column')}> |
| 239 | + Add column operation |
| 240 | +</Button> |
| 241 | +<Button onClick={() => addOperation(operationsKey, 'value')}> |
| 242 | + Add scalar operation |
| 243 | +</Button> |
| 244 | +<Button onClick={() => addOperation(operationsKey, 'metadata_value')}> |
| 245 | + Add table metadata operation |
| 246 | +</Button> |
| 247 | +<Button onClick={() => addOperation(operationsKey, 'header_value')}> |
| 248 | + Add table header operation |
| 249 | +</Button> |
| 250 | +</code></pre> |
| 251 | +<p>These correspond directly to the four backend operation types.</p> |
| 252 | +<hr /> |
| 253 | +<h3>3.1 Column operation: <code>column</code></h3> |
| 254 | +<h4>What the user sees</h4> |
| 255 | +<p>After adding a column operation, the user sees:</p> |
| 256 | +<ul> |
| 257 | +<li>the operator dropdown</li> |
| 258 | +<li>a column selector listing available input columns</li> |
| 259 | +<li>a red remove button</li> |
| 260 | +</ul> |
| 261 | +<h4>How this relates to the backend</h4> |
| 262 | +<p>The selected column is stored in:</p> |
| 263 | +<pre><code class="language-js">operation = { |
| 264 | + type: 'column', |
| 265 | + operator: '+', |
| 266 | + column: { |
| 267 | + tableIndex: ..., |
| 268 | + columnIndex: ... |
| 269 | + } |
| 270 | +} |
| 271 | +</code></pre> |
| 272 | +<p>The backend then reads the referenced column values row by row and combines them with the current x- or y-values.</p> |
| 273 | +<hr /> |
| 274 | +<h3>3.2 Scalar operation: <code>value</code></h3> |
| 275 | +<h4>What the user sees</h4> |
| 276 | +<p>After adding a scalar operation, the user sees:</p> |
| 277 | +<ul> |
| 278 | +<li>the operator dropdown</li> |
| 279 | +<li>a text input for entering a fixed numeric value</li> |
| 280 | +<li>a red remove button</li> |
| 281 | +</ul> |
| 282 | +<h4>How this relates to the backend</h4> |
| 283 | +<p>The entered value is stored in:</p> |
| 284 | +<pre><code class="language-js">operation = { |
| 285 | + type: 'value', |
| 286 | + operator: '+', |
| 287 | + value: '...' |
| 288 | +} |
| 289 | +</code></pre> |
| 290 | +<p>The backend uses this same scalar for every row.</p> |
| 291 | +<hr /> |
| 292 | +<h3>3.3 Table metadata operation: <code>metadata_value</code></h3> |
| 293 | +<h4>What the user sees</h4> |
| 294 | +<p>After adding a metadata operation, the user sees:</p> |
| 295 | +<ul> |
| 296 | +<li>the operator dropdown</li> |
| 297 | +<li>a dropdown of available metadata fields</li> |
| 298 | +<li>a checkbox for <code>ignore_missing_values</code></li> |
| 299 | +<li>a preview of the current numeric value</li> |
| 300 | +<li>a warning that metadata-based calculations should be used with caution</li> |
| 301 | +<li>a red remove button</li> |
| 302 | +</ul> |
| 303 | +<h4>How this relates to the backend</h4> |
| 304 | +<p>The selected metadata field is stored using fields such as <code>metadata</code>, <code>value</code>, and <code>table</code>. The backend uses <code>value</code> as the metadata key and <code>table</code> as the table reference.</p> |
| 305 | +<p>If the metadata is missing or not numeric:</p> |
| 306 | +<ul> |
| 307 | +<li>with <code>ignore_missing_values = true</code>, the operation is skipped</li> |
| 308 | +<li>with <code>ignore_missing_values = false</code>, the calculation fails</li> |
| 309 | +</ul> |
| 310 | +<hr /> |
| 311 | +<h3>3.4 Table header operation: <code>header_value</code></h3> |
| 312 | +<h4>What the user sees</h4> |
| 313 | +<p>After adding a header operation, the user sees:</p> |
| 314 | +<ul> |
| 315 | +<li>the operator dropdown</li> |
| 316 | +<li>a dropdown to choose the input table</li> |
| 317 | +<li>a text field for <strong>Line</strong></li> |
| 318 | +<li>a text field for <strong>Regex</strong></li> |
| 319 | +<li>a checkbox for <code>ignore_missing_values</code></li> |
| 320 | +<li>a preview of the current extracted numeric value</li> |
| 321 | +<li>a warning that header-based calculations should be used with caution</li> |
| 322 | +<li>a red remove button</li> |
| 323 | +</ul> |
| 324 | +<h4>How this relates to the backend</h4> |
| 325 | +<p>The entered fields are stored in something like:</p> |
| 326 | +<pre><code class="language-js">operation = { |
| 327 | + type: 'header_value', |
| 328 | + operator: '+', |
| 329 | + table: '0', |
| 330 | + line: '...', |
| 331 | + regex: '...', |
| 332 | + ignore_missing_values: false |
| 333 | +} |
| 334 | +</code></pre> |
| 335 | +<p>The backend uses <code>_search_regex</code> to search the selected table header. If the regex returns a numeric match, that value is used in the calculation.</p> |
| 336 | +<hr /> |
| 337 | +<h2>4. Operation description</h2> |
| 338 | +<p>The frontend also shows an operation description area when operations are configured.</p> |
| 339 | +<h3>What the user sees</h3> |
| 340 | +<ul> |
| 341 | +<li>An automatically generated summary of the configured operations</li> |
| 342 | +<li>A text area where the user can add a manual explanation</li> |
| 343 | +</ul> |
| 344 | +<h3>How this relates to the backend</h3> |
| 345 | +<p>The summary is stored in:</p> |
| 346 | +<ul> |
| 347 | +<li><code>xOperationsDescription</code></li> |
| 348 | +<li><code>yOperationsDescription</code></li> |
| 349 | +</ul> |
| 350 | +<p>This description is later included in the exported JCAMP comment.</p> |
| 351 | +<hr /> |
| 352 | +<h2>Putting it all together</h2> |
| 353 | +<p>From the user’s perspective:</p> |
| 354 | +<ol> |
| 355 | +<li>Select the base x-column and y-column.</li> |
| 356 | +<li>Add zero or more operations to x and/or y.</li> |
| 357 | +<li>For each operation, choose:</li> |
| 358 | +<li>which mathematical operator should be used, and</li> |
| 359 | +<li>which kind of source should provide the right-hand value: |
| 360 | +<ul> |
| 361 | +<li>another column</li> |
| 362 | +<li>a fixed scalar</li> |
| 363 | +<li>a metadata field</li> |
| 364 | +<li>a header regex match</li> |
| 365 | +</ul> |
| 366 | +</li> |
| 367 | +<li>Optionally provide an explanation of the operation chain.</li> |
| 368 | +</ol> |
| 369 | +<p>From the backend’s perspective:</p> |
| 370 | +<ul> |
| 371 | +<li>The selected base columns fill <code>xColumn</code> and <code>yColumn</code>.</li> |
| 372 | +<li>The added operation rows fill <code>xOperations</code> and <code>yOperations</code>.</li> |
| 373 | +<li>Each operation is executed in order.</li> |
| 374 | +<li>The final transformed values are stored in the output table as <code>x</code> and <code>y</code>.</li> |
| 375 | +</ul> |
| 376 | +<p>In short:<br /> |
| 377 | +The frontend provides a user-friendly way to build a calculation chain for x- and y-values, while the backend executes that chain using one of four operation types and one of four mathematical operators.</p> |
| 378 | +</body> |
| 379 | +</html> |
0 commit comments