The code contains two methods (functions inside a class):
_has_loop(index)
Checks whether a certain output table has a “loop” configuration (i.e. is meant to be repeated for multiple input tables).
_check_loop_condition(index, input_table_index)
Checks whether a specific input table matches the conditions of that loop and is therefore allowed to be used in it.
In simple terms:
You have several input tables (input_tables) and several output table configurations (profile_output_tables). Some output tables are configured so that they will be created multiple times, once for each input table that fits certain rules. These two methods help answer:
The class that contains these methods uses at least these two properties:
self.profile_output_tablesloopType – the type of loop, e.g. 'all'matchTables – which tables are targeted when loopType = 'all'table – detailed configuration, including:
loop_header – rules based on columns (table headers)loop_theader – rules based on text / pattern matchesloop_metadata – rules based on metadataself.input_tables
A list of the actual input tables.
Each input table can contain:
columns – a list of columns (with names, etc.)metadata – additional key–value information about the table_has_loop(self, index)Determine whether the output table at position index has a loop configuration, and if so, what kind of loop it is.
If there is no configuration at index, the method returns False.
Meaning: There is no output table here, so there can’t be any loop.
Case 1: loopType is 'all'
In this case, the method returns the value of matchTables.
matchTables describes which tables this loop applies to (for example “all” or a specific subset).
Case 2: loopType is not 'all'
table configuration for any of these fields:loop_headerloop_metadataloop_theaderTrue.False.False:True:matchTables):loopType = 'all', the method directly returns the configured table selection._check_loop_condition(self, index, input_table_index)Check if a specific input table (given by input_table_index) fulfills all the rules defined for the loop of the output table at position index.
Only if all conditions are satisfied, the method returns True.
Otherwise, it returns False.
loopType = 'all'If the output table’s loopType is 'all', then at the end of the method it simply returns True.
Meaning: In this mode, detailed conditions are not enforced here – all targeted tables are treated as matching.
The detailed checks described next only apply when loopType is not 'all'.
loopType ≠ 'all')loop_header – Column / header conditionsloop_header.tableIndex and columnIndex).False.input_table_index):False.Plain-language interpretation:
All the column rules in loop_header must pass.
Typically, this means that certain columns (by position) must exist and have the same names across different input tables.
loop_theader – Text / pattern conditionsloop_theader._search_regex) to check whether a particular text or pattern is found in the current input table header.False.Plain-language interpretation:
The input table must contain certain texts or patterns (for example in headers or specific rows), as defined in the configuration.
loop_metadata – Metadata conditionsloop_metadata.value (the metadata key) and a table must be provided.False.ignoreValue is set to true: input_table_index) has this metadata key at all.False.ignoreValue is not set: False.Plain-language interpretation:
The current input table’s metadata must either:
- contain certain keys, or
- contain keys with values that match those in another table,
depending on the configuration.
_has_loop(index) answers:
“Does this output table have a loop configuration, and what kind is it?”
_check_loop_condition(index, input_table_index) answers:
“Does this particular input table fulfill all conditions to be used in the loop for this output table?”
Typically, the system would:
_has_loop to see whether an output table is supposed to be repeated for multiple input tables._check_loop_condition to decide whether that table is included in the loop.Only if _has_loop identifies a loop and _check_loop_condition returns True for a certain input table will that table be processed as part of the repeated output.
This part of the frontend is the visual configuration for the loop logic you saw in the backend.
It lets a user decide:
Below is what each visible element means and how it connects to the backend behavior.
loopType)<Form.Select
id="loop_select"
aria-label="Select looping"
value={profile.tables[index].loopType}
onChange={(e) => this.handleChangeLoop(e.target.value, index)}
>
<option value="all">all input tables.</option>
<option value="header">all input tables that have the same column header.</option>
<option value="theader">all input tables that have the same table header.</option>
<option value="metadata">all input tables that have the same metadata.</option>
</Form.Select>
A dropdown with these options:
The user chooses one of these options for each output table.
The selected value is stored as loopType in profile.tables[index].loopType. In the backend, this corresponds to:
self.profile_output_tables[index].get('loopType')The options mean:
all
Backend: loopType = 'all'
→ The output table is generated for all input tables (no detailed conditions are checked in _check_loop_condition; it effectively always returns True).
header
Backend: loopType is not 'all', and the frontend uses loop_header rules.
→ The backend uses loop_header to check if input tables have matching column headers (column names and positions).
theaderloopType is not 'all', and the frontend uses loop_theader.loop_theader and _search_regex to check for matching table header text/patterns.metadataloopType is not 'all', and the frontend uses loop_metadata.loop_metadata to compare metadata values between tables.So, this dropdown directly controls which type of loop condition the backend will apply.
loop_header (for “same column header”){profile.tables[index].loopType !== "all" &&
profile.tables[index].table['loop_header'] &&
profile.tables[index].table['loop_header'].map((operation, op_index) => (
<InputGroup key={op_index}>
<InputGroup.Text>↳</InputGroup.Text>
<Button
variant="outline-danger"
onClick={() => this.removeOperation(index, 'loop_header', op_index)}
>
×
</Button>
<Select
...
value={distInputColumns.flatMap(group => group.options)
.find(col => isEqual(col.value, operation.column))}
options={distInputColumns}
onChange={selectedOption =>
this.updateOperation(index, 'loop_header', op_index, 'column', selectedOption.value)
}
/>
</InputGroup>
))}
Only shown when loopType is not "all", and there are loop_header rules.
For each rule, the user sees:
↧ style via ↳) indicating “this is part of the loop configuration”.Select) showing all available input columns, grouped by table. The user picks one column.The user can create or remove several such rules (each rule picks one column).
Each selected column is stored as one entry in:
profile.tables[index].table['loop_header'][op_index].columnIn the backend, _check_loop_condition uses:
loop_header = self.profile_output_tables[index]['table'].get('loop_header', [])
for header in loop_header:
header['column'] -> { tableIndex, columnIndex }
The backend then:
Effect:
You are telling the system:
“Only treat input tables as belonging to the same loop if these specific columns match across the tables.”
The frontend control is exactly how you specify which columns must match.
loop_metadata (for “same metadata”){profile.tables[index].loopType !== "all" &&
profile.tables[index].table['loop_metadata'] &&
profile.tables[index].table['loop_metadata'].map((operation, op_index) => (
<InputGroup key={op_index}>
<InputGroup.Text>↳</InputGroup.Text>
<Button
variant="outline-danger"
onClick={() => this.removeOperation(index, 'loop_metadata', op_index)}
>
×
</Button>
<Form.Select
size="sm"
value={operation.metadata || ''}
onChange={(event) => {
this.updateOperation(
index,
'loop_metadata',
op_index,
'metadata',
`${event.target.value}:${tableMetadataOptions[event.target.value].key}:${tableMetadataOptions[event.target.value].tableIndex}`
);
}}
>
{tableMetadataOptions.map((option, optionIndex) => (
<option key={optionIndex} value={optionIndex}>{option.label}</option>
))}
</Form.Select>
<OverlayTrigger
placement="bottom-end"
overlay={<Tooltip>Ignore Value</Tooltip>}
>
<div className="input-group-text" style={{cursor: 'pointer'}}>
<input
type="checkbox"
checked={profile.tables[index].table.loop_metadata[op_index].ignoreValue || false}
onChange={() => this.toggleMatchTables(index, op_index)}
/>
</div>
</OverlayTrigger>
</InputGroup>
))}
Again, only shown when loopType is not "all" and there are loop_metadata entries.
For each metadata rule, the user sees:
tableMetadataOptions), each with a label (for example “File Name”, “Source System”, etc.).The user configures:
Each selection builds an entry like:
profile.tables[index].table['loop_metadata'][op_index] = {
metadata: "...", // packed info: index:key:tableIndex
ignoreValue: true/false
}
In the backend, this connects to:
loop_metadata = self.profile_output_tables[index]['table'].get('loop_metadata', [])
for metadata in loop_metadata:
key = metadata.get('value')
ignoreValue = metadata.get('ignoreValue')
table = metadata.get('table')
The backend then:
key and table to identify the metadata field and (if needed) a reference table.Effect:
You are telling the system:
“Group tables in this loop based on this metadata field: either they just need to have it, or they need to have the same value.”
The checkbox determines whether you require presence of the field or exact matching value.
loop_theader (for “same table header”){profile.tables[index].loopType !== "all" &&
profile.tables[index].table['loop_theader'] &&
profile.tables[index].table['loop_theader'].map((operation, op_index) => (
<InputGroup>
<InputGroup.Text>↳</InputGroup.Text>
<Button
variant="outline-danger"
onClick={() => this.removeOperation(index, 'loop_theader', op_index)}
>
×
</Button>
<Form.Control
value={operation.line || ''}
placeholder='Line'
onChange={(event) => {
this.updateOperation(index, 'loop_theader', op_index, 'line', event.target.value)
}}
/>
<Form.Control
value={operation.regex || ''}
placeholder='Regex'
onChange={(event) => {
this.updateOperation(index, 'loop_theader', op_index, 'regex', event.target.value)
}}
/>
</InputGroup>
))}
Only shown when loopType is not "all" and there are loop_theader rules.
For each rule, the user sees:
The user enters:
Each rule is stored as something like:
profile.tables[index].table['loop_theader'][op_index] = {
line: '...',
regex: '...'
}
In the backend, _check_loop_condition does:
loop_theader = self.profile_output_tables[index]['table'].get('loop_theader', [])
for theader in loop_theader:
match, _ = self._search_regex(theader, input_table_index)
if match is None:
return False
This means:
_search_regex) on the current input table.Effect:
You are telling the system:
“Only use input tables in this loop if their header text matches this pattern at this line/position.”
This is a flexible way to match based on specific text in the table header.
From the user’s perspective:
Or share certain metadata.
If you choose anything other than “all”:
From the backend’s perspective:
loopType, loop_header, loop_metadata, and loop_theader inside profile_output_tables._has_loop and _check_loop_condition then use exactly those fields to decide:In short:
The frontend elements you see are a user-friendly way to configure the rules that the backend code enforces when grouping input tables into repeated output tables.