informatica-python 1.9.9__tar.gz → 1.10.1__tar.gz
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.
- {informatica_python-1.9.9 → informatica_python-1.10.1}/PKG-INFO +10 -2
- {informatica_python-1.9.9 → informatica_python-1.10.1}/README.md +9 -1
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/mapping_gen.py +65 -13
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/models.py +3 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/parser.py +3 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/PKG-INFO +10 -2
- {informatica_python-1.9.9 → informatica_python-1.10.1}/pyproject.toml +1 -1
- {informatica_python-1.9.9 → informatica_python-1.10.1}/tests/test_integration.py +299 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/LICENSE +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/__init__.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/cli.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/converter.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/__init__.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/config_gen.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/error_log_gen.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/helper_gen.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/sql_gen.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/workflow_gen.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/utils/__init__.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/utils/datatype_map.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/utils/expression_converter.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/utils/lib_adapters.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/utils/sql_dialect.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/SOURCES.txt +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/dependency_links.txt +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/entry_points.txt +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/requires.txt +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/top_level.txt +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/setup.cfg +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/tests/test_converter.py +0 -0
- {informatica_python-1.9.9 → informatica_python-1.10.1}/tests/test_expressions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: informatica-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.1
|
|
4
4
|
Summary: Convert Informatica PowerCenter workflow XML to Python/PySpark code
|
|
5
5
|
Author: Nick
|
|
6
6
|
License: MIT
|
|
@@ -414,7 +414,15 @@ The generated `helper_functions.py` provides a complete runtime library:
|
|
|
414
414
|
|
|
415
415
|
## Changelog
|
|
416
416
|
|
|
417
|
-
### v1.
|
|
417
|
+
### v1.10.1 (Current)
|
|
418
|
+
- **Router multi-group output support**: Router transformations now properly handle `<GROUP>` elements with `EXPRESSION` attributes — generates separate filtered DataFrames for each named output group (e.g., `df_rtr_rest_type_per`, `df_rtr_rest_value_per`), not just the DEFAULT group
|
|
419
|
+
- **Connector group routing**: `FROMINSTANCEGROUP` / `TOINSTANCEGROUP` attributes on `CONNECTOR` elements are now parsed and used to wire downstream transforms/targets to the correct Router output group
|
|
420
|
+
- **Joiner group-aware routing**: Joiner transformations now correctly identify master/detail sources through Router group connectors — two different Router output groups feeding a Joiner are properly resolved to their distinct DataFrames
|
|
421
|
+
- **GroupDef expression field**: `GroupDef` model now stores the `EXPRESSION` attribute from `<GROUP>` XML elements
|
|
422
|
+
- **Backward-compatible Router fallback**: Existing `TABLEATTRIBUTE`-based Router group conditions (older XML format) continue to work — the code checks `<GROUP>` elements first, then falls back to `TABLEATTRIBUTE` entries
|
|
423
|
+
- **223 tests** passing
|
|
424
|
+
|
|
425
|
+
### v1.9.8
|
|
418
426
|
- **NOT(expr) function-call form**: `NOT(ISNULL(x))` now correctly converts to `~(df["x"].isna())` — handles both `NOT ` (with space) and `NOT(` (without space) forms
|
|
419
427
|
- **AND/OR/NOT as field names fix**: Logical operators no longer mangled into `df["AND"]` / `df["OR"]` — conversion moved before field substitution in both `_vec_recursive` fallback and `_vectorize_simple`
|
|
420
428
|
- **Condition tokenizer word-boundary fix**: `_split_condition_tokens` no longer splits on `OR` inside field names like `DeletedIndicator` — verifies preceding character is a real word boundary
|
|
@@ -387,7 +387,15 @@ The generated `helper_functions.py` provides a complete runtime library:
|
|
|
387
387
|
|
|
388
388
|
## Changelog
|
|
389
389
|
|
|
390
|
-
### v1.
|
|
390
|
+
### v1.10.1 (Current)
|
|
391
|
+
- **Router multi-group output support**: Router transformations now properly handle `<GROUP>` elements with `EXPRESSION` attributes — generates separate filtered DataFrames for each named output group (e.g., `df_rtr_rest_type_per`, `df_rtr_rest_value_per`), not just the DEFAULT group
|
|
392
|
+
- **Connector group routing**: `FROMINSTANCEGROUP` / `TOINSTANCEGROUP` attributes on `CONNECTOR` elements are now parsed and used to wire downstream transforms/targets to the correct Router output group
|
|
393
|
+
- **Joiner group-aware routing**: Joiner transformations now correctly identify master/detail sources through Router group connectors — two different Router output groups feeding a Joiner are properly resolved to their distinct DataFrames
|
|
394
|
+
- **GroupDef expression field**: `GroupDef` model now stores the `EXPRESSION` attribute from `<GROUP>` XML elements
|
|
395
|
+
- **Backward-compatible Router fallback**: Existing `TABLEATTRIBUTE`-based Router group conditions (older XML format) continue to work — the code checks `<GROUP>` elements first, then falls back to `TABLEATTRIBUTE` entries
|
|
396
|
+
- **223 tests** passing
|
|
397
|
+
|
|
398
|
+
### v1.9.8
|
|
391
399
|
- **NOT(expr) function-call form**: `NOT(ISNULL(x))` now correctly converts to `~(df["x"].isna())` — handles both `NOT ` (with space) and `NOT(` (without space) forms
|
|
392
400
|
- **AND/OR/NOT as field names fix**: Logical operators no longer mangled into `df["AND"]` / `df["OR"]` — conversion moved before field substitution in both `_vec_recursive` fallback and `_vectorize_simple`
|
|
393
401
|
- **Condition tokenizer word-boundary fix**: `_split_condition_tokens` no longer splits on `OR` inside field names like `DeletedIndicator` — verifies preceding character is a real word boundary
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/mapping_gen.py
RENAMED
|
@@ -54,6 +54,8 @@ def _expand_mapplet_recursive(mapplet, mapplet_map, prefix, depth=0, max_depth=1
|
|
|
54
54
|
to_instance=new_to,
|
|
55
55
|
to_field=conn.to_field,
|
|
56
56
|
to_instance_type=conn.to_instance_type,
|
|
57
|
+
from_instance_group=conn.from_instance_group,
|
|
58
|
+
to_instance_group=conn.to_instance_group,
|
|
57
59
|
))
|
|
58
60
|
|
|
59
61
|
for inst in getattr(mapplet, 'instances', []):
|
|
@@ -138,6 +140,8 @@ def _inline_mapplets(mapping, folder):
|
|
|
138
140
|
to_instance=first_tx,
|
|
139
141
|
to_field=conn.to_field,
|
|
140
142
|
to_instance_type=conn.to_instance_type,
|
|
143
|
+
from_instance_group=conn.from_instance_group,
|
|
144
|
+
to_instance_group=conn.to_instance_group,
|
|
141
145
|
))
|
|
142
146
|
else:
|
|
143
147
|
rewired_connectors.append(conn)
|
|
@@ -167,6 +171,8 @@ def _inline_mapplets(mapping, folder):
|
|
|
167
171
|
to_instance=conn.to_instance,
|
|
168
172
|
to_field=conn.to_field,
|
|
169
173
|
to_instance_type=conn.to_instance_type,
|
|
174
|
+
from_instance_group=conn.from_instance_group,
|
|
175
|
+
to_instance_group=conn.to_instance_group,
|
|
170
176
|
))
|
|
171
177
|
else:
|
|
172
178
|
rewired_connectors.append(conn)
|
|
@@ -730,7 +736,11 @@ def _generate_transformation(lines, tx, connector_graph, source_dfs, transform_m
|
|
|
730
736
|
input_conns = connector_graph.get("to", {}).get(tx.name, [])
|
|
731
737
|
input_sources = set()
|
|
732
738
|
for c in input_conns:
|
|
733
|
-
|
|
739
|
+
if c.from_instance_group:
|
|
740
|
+
group_key = f"{c.from_instance}:{c.from_instance_group}"
|
|
741
|
+
input_sources.add(group_key)
|
|
742
|
+
else:
|
|
743
|
+
input_sources.add(c.from_instance)
|
|
734
744
|
|
|
735
745
|
input_df = None
|
|
736
746
|
for src in input_sources:
|
|
@@ -739,7 +749,14 @@ def _generate_transformation(lines, tx, connector_graph, source_dfs, transform_m
|
|
|
739
749
|
break
|
|
740
750
|
if not input_df:
|
|
741
751
|
for src in input_sources:
|
|
742
|
-
|
|
752
|
+
base = src.split(":")[0] if ":" in src else src
|
|
753
|
+
if base in source_dfs:
|
|
754
|
+
input_df = source_dfs[base]
|
|
755
|
+
break
|
|
756
|
+
if not input_df:
|
|
757
|
+
for src in input_sources:
|
|
758
|
+
base = src.split(":")[0] if ":" in src else src
|
|
759
|
+
input_df = f"df_{_safe_name(base)}"
|
|
743
760
|
break
|
|
744
761
|
if not input_df:
|
|
745
762
|
input_df = "df_input"
|
|
@@ -946,10 +963,11 @@ def _gen_joiner_transform(lines, tx, tx_safe, input_df, input_sources, source_df
|
|
|
946
963
|
to_field = conn.to_field
|
|
947
964
|
port_to_col[to_field] = conn.from_field
|
|
948
965
|
port_to_col[to_field.lower()] = conn.from_field
|
|
966
|
+
src_key = f"{conn.from_instance}:{conn.from_instance_group}" if conn.from_instance_group else conn.from_instance
|
|
949
967
|
if to_field in master_fields or to_field.lower() in master_fields_lower:
|
|
950
|
-
master_src =
|
|
968
|
+
master_src = src_key
|
|
951
969
|
elif to_field in detail_fields or to_field.lower() in detail_fields_lower:
|
|
952
|
-
detail_src =
|
|
970
|
+
detail_src = src_key
|
|
953
971
|
|
|
954
972
|
if left_keys and right_keys and port_to_col:
|
|
955
973
|
left_keys = [port_to_col.get(k, port_to_col.get(k.lower(), k)) for k in left_keys]
|
|
@@ -967,9 +985,17 @@ def _gen_joiner_transform(lines, tx, tx_safe, input_df, input_sources, source_df
|
|
|
967
985
|
detail_src = s
|
|
968
986
|
break
|
|
969
987
|
|
|
988
|
+
def _resolve_src(key):
|
|
989
|
+
if key in source_dfs:
|
|
990
|
+
return source_dfs[key]
|
|
991
|
+
base = key.split(":")[0] if ":" in key else key
|
|
992
|
+
if base in source_dfs:
|
|
993
|
+
return source_dfs[base]
|
|
994
|
+
return f"df_{_safe_name(base)}"
|
|
995
|
+
|
|
970
996
|
if master_src and detail_src:
|
|
971
|
-
df_master =
|
|
972
|
-
df_detail =
|
|
997
|
+
df_master = _resolve_src(master_src)
|
|
998
|
+
df_detail = _resolve_src(detail_src)
|
|
973
999
|
|
|
974
1000
|
lines.append(f" # Join ({join_type}): {join_condition or 'auto'}")
|
|
975
1001
|
if left_keys and right_keys:
|
|
@@ -1095,9 +1121,24 @@ def _gen_lookup_transform(lines, tx, tx_safe, input_df, source_dfs, connector_gr
|
|
|
1095
1121
|
def _gen_router_transform(lines, tx, tx_safe, input_df, source_dfs):
|
|
1096
1122
|
lines.append(f" # Router groups:")
|
|
1097
1123
|
group_conditions = {}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1124
|
+
|
|
1125
|
+
output_groups = [
|
|
1126
|
+
g for g in tx.groups
|
|
1127
|
+
if g.type.upper() not in ("INPUT", "") and "DEFAULT" not in g.type.upper()
|
|
1128
|
+
]
|
|
1129
|
+
output_groups.sort(key=lambda g: g.order)
|
|
1130
|
+
|
|
1131
|
+
if output_groups:
|
|
1132
|
+
for g in output_groups:
|
|
1133
|
+
if g.expression and g.expression.strip():
|
|
1134
|
+
group_conditions[g.name] = g.expression
|
|
1135
|
+
else:
|
|
1136
|
+
group_conditions[g.name] = ""
|
|
1137
|
+
|
|
1138
|
+
if not group_conditions:
|
|
1139
|
+
for attr in tx.attributes:
|
|
1140
|
+
if "Group Filter Condition" in attr.name:
|
|
1141
|
+
group_conditions[attr.name] = attr.value
|
|
1101
1142
|
|
|
1102
1143
|
remaining_mask_parts = []
|
|
1103
1144
|
if group_conditions:
|
|
@@ -1108,15 +1149,21 @@ def _gen_router_transform(lines, tx, tx_safe, input_df, source_dfs):
|
|
|
1108
1149
|
expr_py = f"pd.Series(True, index={input_df}.index)"
|
|
1109
1150
|
mask_var = f"_router_mask_{tx_safe}_{i}"
|
|
1110
1151
|
lines.append(f" {mask_var} = {expr_py} # {gname}")
|
|
1111
|
-
|
|
1112
|
-
|
|
1152
|
+
group_df_name = f"df_{tx_safe}_{_safe_name(gname)}"
|
|
1153
|
+
lines.append(f" {group_df_name} = {input_df}[{mask_var}].copy()")
|
|
1154
|
+
source_dfs[f"{tx.name}:{gname}"] = group_df_name
|
|
1113
1155
|
remaining_mask_parts.append(f"~{mask_var}")
|
|
1156
|
+
|
|
1157
|
+
default_groups = [g for g in tx.groups if "DEFAULT" in g.type.upper()]
|
|
1158
|
+
default_name = default_groups[0].name if default_groups else "DEFAULT"
|
|
1159
|
+
|
|
1114
1160
|
if remaining_mask_parts:
|
|
1115
1161
|
lines.append(f" _router_default_mask = {' & '.join(remaining_mask_parts)}")
|
|
1116
|
-
lines.append(f" df_{tx_safe} = {input_df}[_router_default_mask].copy() # Default group")
|
|
1162
|
+
lines.append(f" df_{tx_safe} = {input_df}[_router_default_mask].copy() # Default group ({default_name})")
|
|
1117
1163
|
else:
|
|
1118
|
-
lines.append(f" df_{tx_safe} = {input_df}.copy() # Default group")
|
|
1164
|
+
lines.append(f" df_{tx_safe} = {input_df}.copy() # Default group ({default_name})")
|
|
1119
1165
|
source_dfs[tx.name] = f"df_{tx_safe}"
|
|
1166
|
+
source_dfs[f"{tx.name}:{default_name}"] = f"df_{tx_safe}"
|
|
1120
1167
|
|
|
1121
1168
|
|
|
1122
1169
|
def _gen_union_transform(lines, tx, tx_safe, input_sources, source_dfs, data_lib="pandas"):
|
|
@@ -1448,6 +1495,11 @@ def _generate_target_write(lines, tgt_name, tgt_def, connector_graph, source_dfs
|
|
|
1448
1495
|
to_conns = connector_graph.get("to", {}).get(tgt_name, [])
|
|
1449
1496
|
input_df = None
|
|
1450
1497
|
for c in to_conns:
|
|
1498
|
+
if c.from_instance_group:
|
|
1499
|
+
group_key = f"{c.from_instance}:{c.from_instance_group}"
|
|
1500
|
+
if group_key in source_dfs:
|
|
1501
|
+
input_df = source_dfs[group_key]
|
|
1502
|
+
break
|
|
1451
1503
|
if c.from_instance in source_dfs:
|
|
1452
1504
|
input_df = source_dfs[c.from_instance]
|
|
1453
1505
|
break
|
|
@@ -80,6 +80,7 @@ class GroupDef:
|
|
|
80
80
|
type: str = ""
|
|
81
81
|
description: str = ""
|
|
82
82
|
order: int = 0
|
|
83
|
+
expression: str = ""
|
|
83
84
|
fields: List[FieldDef] = field(default_factory=list)
|
|
84
85
|
|
|
85
86
|
|
|
@@ -274,6 +275,8 @@ class ConnectorDef:
|
|
|
274
275
|
to_field: str
|
|
275
276
|
to_instance: str
|
|
276
277
|
to_instance_type: str
|
|
278
|
+
from_instance_group: str = ""
|
|
279
|
+
to_instance_group: str = ""
|
|
277
280
|
|
|
278
281
|
|
|
279
282
|
@dataclass
|
|
@@ -240,6 +240,7 @@ class InformaticaParser:
|
|
|
240
240
|
type=self._attr(elem, "TYPE"),
|
|
241
241
|
description=self._attr(elem, "DESCRIPTION"),
|
|
242
242
|
order=self._int_attr(elem, "ORDER"),
|
|
243
|
+
expression=self._attr(elem, "EXPRESSION"),
|
|
243
244
|
)
|
|
244
245
|
for fld in elem.findall("SOURCEFIELD"):
|
|
245
246
|
grp.fields.append(self._parse_source_field(fld))
|
|
@@ -580,6 +581,8 @@ class InformaticaParser:
|
|
|
580
581
|
to_field=self._attr(elem, "TOFIELD"),
|
|
581
582
|
to_instance=self._attr(elem, "TOINSTANCE"),
|
|
582
583
|
to_instance_type=self._attr(elem, "TOINSTANCETYPE"),
|
|
584
|
+
from_instance_group=self._attr(elem, "FROMINSTANCEGROUP"),
|
|
585
|
+
to_instance_group=self._attr(elem, "TOINSTANCEGROUP"),
|
|
583
586
|
)
|
|
584
587
|
|
|
585
588
|
def _parse_instance(self, elem) -> InstanceDef:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: informatica-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.1
|
|
4
4
|
Summary: Convert Informatica PowerCenter workflow XML to Python/PySpark code
|
|
5
5
|
Author: Nick
|
|
6
6
|
License: MIT
|
|
@@ -414,7 +414,15 @@ The generated `helper_functions.py` provides a complete runtime library:
|
|
|
414
414
|
|
|
415
415
|
## Changelog
|
|
416
416
|
|
|
417
|
-
### v1.
|
|
417
|
+
### v1.10.1 (Current)
|
|
418
|
+
- **Router multi-group output support**: Router transformations now properly handle `<GROUP>` elements with `EXPRESSION` attributes — generates separate filtered DataFrames for each named output group (e.g., `df_rtr_rest_type_per`, `df_rtr_rest_value_per`), not just the DEFAULT group
|
|
419
|
+
- **Connector group routing**: `FROMINSTANCEGROUP` / `TOINSTANCEGROUP` attributes on `CONNECTOR` elements are now parsed and used to wire downstream transforms/targets to the correct Router output group
|
|
420
|
+
- **Joiner group-aware routing**: Joiner transformations now correctly identify master/detail sources through Router group connectors — two different Router output groups feeding a Joiner are properly resolved to their distinct DataFrames
|
|
421
|
+
- **GroupDef expression field**: `GroupDef` model now stores the `EXPRESSION` attribute from `<GROUP>` XML elements
|
|
422
|
+
- **Backward-compatible Router fallback**: Existing `TABLEATTRIBUTE`-based Router group conditions (older XML format) continue to work — the code checks `<GROUP>` elements first, then falls back to `TABLEATTRIBUTE` entries
|
|
423
|
+
- **223 tests** passing
|
|
424
|
+
|
|
425
|
+
### v1.9.8
|
|
418
426
|
- **NOT(expr) function-call form**: `NOT(ISNULL(x))` now correctly converts to `~(df["x"].isna())` — handles both `NOT ` (with space) and `NOT(` (without space) forms
|
|
419
427
|
- **AND/OR/NOT as field names fix**: Logical operators no longer mangled into `df["AND"]` / `df["OR"]` — conversion moved before field substitution in both `_vec_recursive` fallback and `_vectorize_simple`
|
|
420
428
|
- **Condition tokenizer word-boundary fix**: `_split_condition_tokens` no longer splits on `OR` inside field names like `DeletedIndicator` — verifies preceding character is a real word boundary
|
|
@@ -2911,3 +2911,302 @@ class TestConcatWithLtrimRtrim(unittest.TestCase):
|
|
|
2911
2911
|
result = convert_expression_vectorized(expr)
|
|
2912
2912
|
assert "+" in result
|
|
2913
2913
|
assert "||" not in result
|
|
2914
|
+
|
|
2915
|
+
|
|
2916
|
+
class TestRouterGroupElements(unittest.TestCase):
|
|
2917
|
+
|
|
2918
|
+
ROUTER_GROUP_XML = '''<?xml version="1.0" encoding="UTF-8"?>
|
|
2919
|
+
<!DOCTYPE POWERMART SYSTEM "powrmart.dtd">
|
|
2920
|
+
<POWERMART CREATION_DATE="01/01/2025" REPOSITORY_VERSION="1">
|
|
2921
|
+
<REPOSITORY NAME="repo" VERSION="1" CODEPAGE="UTF-8" DATABASETYPE="Oracle">
|
|
2922
|
+
<FOLDER NAME="TEST" OWNER="admin">
|
|
2923
|
+
<SOURCE NAME="SRC" DATABASETYPE="Flat File" DBDNAME="SRC">
|
|
2924
|
+
<FLATFILE DELIMITEDBY="COMMA" HEADERROWPRESENT="YES" PADBYTES="NO" ROWDELIMITER="\\n"/>
|
|
2925
|
+
<SOURCEFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" NULLABLE="NOTNULL" KEYTYPE="PRIMARY KEY" FIELDNUMBER="1"/>
|
|
2926
|
+
<SOURCEFIELD NAME="Party_Type" DATATYPE="string" PRECISION="10" SCALE="0" NULLABLE="NULL" KEYTYPE="NOT A KEY" FIELDNUMBER="2"/>
|
|
2927
|
+
<SOURCEFIELD NAME="Attrib_Name" DATATYPE="string" PRECISION="50" SCALE="0" NULLABLE="NULL" KEYTYPE="NOT A KEY" FIELDNUMBER="3"/>
|
|
2928
|
+
</SOURCE>
|
|
2929
|
+
<TARGET NAME="TGT_PER_TYPE" DATABASETYPE="Flat File">
|
|
2930
|
+
<TARGETFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" NULLABLE="NULL" KEYTYPE="NOT A KEY" FIELDNUMBER="1"/>
|
|
2931
|
+
</TARGET>
|
|
2932
|
+
<TARGET NAME="TGT_PER_VALUE" DATABASETYPE="Flat File">
|
|
2933
|
+
<TARGETFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" NULLABLE="NULL" KEYTYPE="NOT A KEY" FIELDNUMBER="1"/>
|
|
2934
|
+
</TARGET>
|
|
2935
|
+
<TARGET NAME="TGT_ORG_TYPE" DATABASETYPE="Flat File">
|
|
2936
|
+
<TARGETFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" NULLABLE="NULL" KEYTYPE="NOT A KEY" FIELDNUMBER="1"/>
|
|
2937
|
+
</TARGET>
|
|
2938
|
+
<TARGET NAME="TGT_DEFAULT" DATABASETYPE="Flat File">
|
|
2939
|
+
<TARGETFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" NULLABLE="NULL" KEYTYPE="NOT A KEY" FIELDNUMBER="1"/>
|
|
2940
|
+
</TARGET>
|
|
2941
|
+
<MAPPING NAME="m_router_groups_test" ISVALID="YES">
|
|
2942
|
+
<TRANSFORMATION NAME="SQ_SRC" TYPE="Source Qualifier" REUSABLE="NO">
|
|
2943
|
+
<TRANSFORMFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" PORTTYPE="OUTPUT"/>
|
|
2944
|
+
<TRANSFORMFIELD NAME="Party_Type" DATATYPE="string" PRECISION="10" SCALE="0" PORTTYPE="OUTPUT"/>
|
|
2945
|
+
<TRANSFORMFIELD NAME="Attrib_Name" DATATYPE="string" PRECISION="50" SCALE="0" PORTTYPE="OUTPUT"/>
|
|
2946
|
+
</TRANSFORMATION>
|
|
2947
|
+
<TRANSFORMATION NAME="RTR_SPLIT" TYPE="Router" REUSABLE="NO">
|
|
2948
|
+
<TRANSFORMFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" PORTTYPE="INPUT/OUTPUT"/>
|
|
2949
|
+
<TRANSFORMFIELD NAME="Party_Type" DATATYPE="string" PRECISION="10" SCALE="0" PORTTYPE="INPUT/OUTPUT"/>
|
|
2950
|
+
<TRANSFORMFIELD NAME="Attrib_Name" DATATYPE="string" PRECISION="50" SCALE="0" PORTTYPE="INPUT/OUTPUT"/>
|
|
2951
|
+
<GROUP DESCRIPTION="" NAME="INPUT" ORDER="1" TYPE="INPUT"/>
|
|
2952
|
+
<GROUP DESCRIPTION="" EXPRESSION="Party_Type = 'PER' AND Attrib_Name = 'RESTRICTION_TYPE'" NAME="Rest_Type_PER" ORDER="2" TYPE="OUTPUT"/>
|
|
2953
|
+
<GROUP DESCRIPTION="" EXPRESSION="Party_Type = 'PER' AND Attrib_Name = 'RESTRICTION_VALUE'" NAME="Rest_Value_PER" ORDER="3" TYPE="OUTPUT"/>
|
|
2954
|
+
<GROUP DESCRIPTION="" EXPRESSION="Party_Type = 'ORG' AND Attrib_Name = 'RESTRICTION_TYPE'" NAME="Rest_Type_ORG" ORDER="4" TYPE="OUTPUT"/>
|
|
2955
|
+
<GROUP DESCRIPTION="" NAME="DEFAULT1" ORDER="5" TYPE="OUTPUT/DEFAULT"/>
|
|
2956
|
+
</TRANSFORMATION>
|
|
2957
|
+
<INSTANCE NAME="SRC" TYPE="Source Definition" TRANSFORMATION_NAME="SRC"/>
|
|
2958
|
+
<INSTANCE NAME="SQ_SRC" TYPE="Source Qualifier" TRANSFORMATION_NAME="SQ_SRC"/>
|
|
2959
|
+
<INSTANCE NAME="RTR_SPLIT" TYPE="Router" TRANSFORMATION_NAME="RTR_SPLIT"/>
|
|
2960
|
+
<INSTANCE NAME="TGT_PER_TYPE" TYPE="Target Definition" TRANSFORMATION_NAME="TGT_PER_TYPE"/>
|
|
2961
|
+
<INSTANCE NAME="TGT_PER_VALUE" TYPE="Target Definition" TRANSFORMATION_NAME="TGT_PER_VALUE"/>
|
|
2962
|
+
<INSTANCE NAME="TGT_ORG_TYPE" TYPE="Target Definition" TRANSFORMATION_NAME="TGT_ORG_TYPE"/>
|
|
2963
|
+
<INSTANCE NAME="TGT_DEFAULT" TYPE="Target Definition" TRANSFORMATION_NAME="TGT_DEFAULT"/>
|
|
2964
|
+
<CONNECTOR FROMINSTANCE="SRC" FROMFIELD="ID" TOINSTANCE="SQ_SRC" TOFIELD="ID"/>
|
|
2965
|
+
<CONNECTOR FROMINSTANCE="SRC" FROMFIELD="Party_Type" TOINSTANCE="SQ_SRC" TOFIELD="Party_Type"/>
|
|
2966
|
+
<CONNECTOR FROMINSTANCE="SRC" FROMFIELD="Attrib_Name" TOINSTANCE="SQ_SRC" TOFIELD="Attrib_Name"/>
|
|
2967
|
+
<CONNECTOR FROMINSTANCE="SQ_SRC" FROMFIELD="ID" TOINSTANCE="RTR_SPLIT" TOFIELD="ID"/>
|
|
2968
|
+
<CONNECTOR FROMINSTANCE="SQ_SRC" FROMFIELD="Party_Type" TOINSTANCE="RTR_SPLIT" TOFIELD="Party_Type"/>
|
|
2969
|
+
<CONNECTOR FROMINSTANCE="SQ_SRC" FROMFIELD="Attrib_Name" TOINSTANCE="RTR_SPLIT" TOFIELD="Attrib_Name"/>
|
|
2970
|
+
<CONNECTOR FROMINSTANCE="RTR_SPLIT" FROMINSTANCEGROUP="Rest_Type_PER" FROMFIELD="ID" TOINSTANCE="TGT_PER_TYPE" TOFIELD="ID"/>
|
|
2971
|
+
<CONNECTOR FROMINSTANCE="RTR_SPLIT" FROMINSTANCEGROUP="Rest_Value_PER" FROMFIELD="ID" TOINSTANCE="TGT_PER_VALUE" TOFIELD="ID"/>
|
|
2972
|
+
<CONNECTOR FROMINSTANCE="RTR_SPLIT" FROMINSTANCEGROUP="Rest_Type_ORG" FROMFIELD="ID" TOINSTANCE="TGT_ORG_TYPE" TOFIELD="ID"/>
|
|
2973
|
+
<CONNECTOR FROMINSTANCE="RTR_SPLIT" FROMINSTANCEGROUP="DEFAULT1" FROMFIELD="ID" TOINSTANCE="TGT_DEFAULT" TOFIELD="ID"/>
|
|
2974
|
+
</MAPPING>
|
|
2975
|
+
<CONFIG NAME="default_session_config"/>
|
|
2976
|
+
<WORKFLOW NAME="wf_router_groups_test" ISVALID="YES">
|
|
2977
|
+
<TASK NAME="Start" REUSABLE="NO" TYPE="Start"/>
|
|
2978
|
+
<SESSION NAME="s_m_router_groups_test" ISVALID="YES" REUSABLE="NO" MAPPINGNAME="m_router_groups_test">
|
|
2979
|
+
<CONFIGREFERENCE REFOBJECTNAME="default_session_config" TYPE="Session config"/>
|
|
2980
|
+
</SESSION>
|
|
2981
|
+
<TASKINSTANCE NAME="Start" TASKNAME="Start" TASKTYPE="Start"/>
|
|
2982
|
+
<TASKINSTANCE NAME="s_m_router_groups_test" TASKNAME="s_m_router_groups_test" TASKTYPE="Session"/>
|
|
2983
|
+
<WORKFLOWLINK FROMTASK="Start" TOTASK="s_m_router_groups_test"/>
|
|
2984
|
+
</WORKFLOW>
|
|
2985
|
+
</FOLDER>
|
|
2986
|
+
</REPOSITORY>
|
|
2987
|
+
</POWERMART>'''
|
|
2988
|
+
|
|
2989
|
+
def test_router_generates_all_named_groups(self):
|
|
2990
|
+
converter = InformaticaConverter()
|
|
2991
|
+
tmpdir = tempfile.mkdtemp()
|
|
2992
|
+
try:
|
|
2993
|
+
converter.convert_string(self.ROUTER_GROUP_XML, output_dir=tmpdir)
|
|
2994
|
+
for fn in os.listdir(tmpdir):
|
|
2995
|
+
if fn.startswith("mapping_") and fn.endswith(".py"):
|
|
2996
|
+
with open(os.path.join(tmpdir, fn)) as f:
|
|
2997
|
+
code = f.read()
|
|
2998
|
+
assert "Rest_Type_PER" in code, "Should have Rest_Type_PER group"
|
|
2999
|
+
assert "Rest_Value_PER" in code, "Should have Rest_Value_PER group"
|
|
3000
|
+
assert "Rest_Type_ORG" in code, "Should have Rest_Type_ORG group"
|
|
3001
|
+
assert "_router_mask_" in code, "Should have router masks"
|
|
3002
|
+
assert "Default group" in code, "Should have default group"
|
|
3003
|
+
assert "_router_default_mask" in code, "Should have default mask"
|
|
3004
|
+
break
|
|
3005
|
+
finally:
|
|
3006
|
+
shutil.rmtree(tmpdir)
|
|
3007
|
+
|
|
3008
|
+
def test_router_group_creates_separate_dataframes(self):
|
|
3009
|
+
converter = InformaticaConverter()
|
|
3010
|
+
tmpdir = tempfile.mkdtemp()
|
|
3011
|
+
try:
|
|
3012
|
+
converter.convert_string(self.ROUTER_GROUP_XML, output_dir=tmpdir)
|
|
3013
|
+
for fn in os.listdir(tmpdir):
|
|
3014
|
+
if fn.startswith("mapping_") and fn.endswith(".py"):
|
|
3015
|
+
with open(os.path.join(tmpdir, fn)) as f:
|
|
3016
|
+
code = f.read()
|
|
3017
|
+
assert "df_rtr_split_rest_type_per" in code, "Should create df for Rest_Type_PER"
|
|
3018
|
+
assert "df_rtr_split_rest_value_per" in code, "Should create df for Rest_Value_PER"
|
|
3019
|
+
assert "df_rtr_split_rest_type_org" in code, "Should create df for Rest_Type_ORG"
|
|
3020
|
+
assert ".copy()" in code, "Should copy DataFrames"
|
|
3021
|
+
break
|
|
3022
|
+
finally:
|
|
3023
|
+
shutil.rmtree(tmpdir)
|
|
3024
|
+
|
|
3025
|
+
def test_router_group_connector_resolution(self):
|
|
3026
|
+
converter = InformaticaConverter()
|
|
3027
|
+
tmpdir = tempfile.mkdtemp()
|
|
3028
|
+
try:
|
|
3029
|
+
converter.convert_string(self.ROUTER_GROUP_XML, output_dir=tmpdir)
|
|
3030
|
+
for fn in os.listdir(tmpdir):
|
|
3031
|
+
if fn.startswith("mapping_") and fn.endswith(".py"):
|
|
3032
|
+
with open(os.path.join(tmpdir, fn)) as f:
|
|
3033
|
+
code = f.read()
|
|
3034
|
+
assert "df_rtr_split_rest_type_per" in code
|
|
3035
|
+
assert "df_rtr_split_rest_value_per" in code
|
|
3036
|
+
break
|
|
3037
|
+
finally:
|
|
3038
|
+
shutil.rmtree(tmpdir)
|
|
3039
|
+
|
|
3040
|
+
def test_router_default_excludes_all_groups(self):
|
|
3041
|
+
converter = InformaticaConverter()
|
|
3042
|
+
tmpdir = tempfile.mkdtemp()
|
|
3043
|
+
try:
|
|
3044
|
+
converter.convert_string(self.ROUTER_GROUP_XML, output_dir=tmpdir)
|
|
3045
|
+
for fn in os.listdir(tmpdir):
|
|
3046
|
+
if fn.startswith("mapping_") and fn.endswith(".py"):
|
|
3047
|
+
with open(os.path.join(tmpdir, fn)) as f:
|
|
3048
|
+
code = f.read()
|
|
3049
|
+
assert "~_router_mask_rtr_split_0" in code, "Default should negate first group mask"
|
|
3050
|
+
assert "~_router_mask_rtr_split_1" in code, "Default should negate second group mask"
|
|
3051
|
+
assert "~_router_mask_rtr_split_2" in code, "Default should negate third group mask"
|
|
3052
|
+
break
|
|
3053
|
+
finally:
|
|
3054
|
+
shutil.rmtree(tmpdir)
|
|
3055
|
+
|
|
3056
|
+
|
|
3057
|
+
class TestRouterGroupParsing(unittest.TestCase):
|
|
3058
|
+
|
|
3059
|
+
def test_group_expression_parsed(self):
|
|
3060
|
+
xml = '''<?xml version="1.0" encoding="UTF-8"?>
|
|
3061
|
+
<!DOCTYPE POWERMART SYSTEM "powrmart.dtd">
|
|
3062
|
+
<POWERMART CREATION_DATE="01/01/2025" REPOSITORY_VERSION="1">
|
|
3063
|
+
<REPOSITORY NAME="repo" VERSION="1" CODEPAGE="UTF-8" DATABASETYPE="Oracle">
|
|
3064
|
+
<FOLDER NAME="TEST" OWNER="admin">
|
|
3065
|
+
<MAPPING NAME="m_test" ISVALID="YES">
|
|
3066
|
+
<TRANSFORMATION NAME="RTR" TYPE="Router" REUSABLE="NO">
|
|
3067
|
+
<TRANSFORMFIELD NAME="X" DATATYPE="string" PRECISION="10" SCALE="0" PORTTYPE="INPUT/OUTPUT"/>
|
|
3068
|
+
<GROUP NAME="INPUT" ORDER="1" TYPE="INPUT"/>
|
|
3069
|
+
<GROUP NAME="GRP_A" ORDER="2" TYPE="OUTPUT" EXPRESSION="X = 'A'"/>
|
|
3070
|
+
<GROUP NAME="DEFAULT1" ORDER="3" TYPE="OUTPUT/DEFAULT"/>
|
|
3071
|
+
</TRANSFORMATION>
|
|
3072
|
+
</MAPPING>
|
|
3073
|
+
</FOLDER>
|
|
3074
|
+
</REPOSITORY>
|
|
3075
|
+
</POWERMART>'''
|
|
3076
|
+
from informatica_python.parser import InformaticaParser
|
|
3077
|
+
parser = InformaticaParser()
|
|
3078
|
+
pm = parser.parse_string(xml)
|
|
3079
|
+
mapping = pm.repositories[0].folders[0].mappings[0]
|
|
3080
|
+
rtr = mapping.transformations[0]
|
|
3081
|
+
assert len(rtr.groups) == 3
|
|
3082
|
+
grp_a = [g for g in rtr.groups if g.name == "GRP_A"][0]
|
|
3083
|
+
assert "X = " in grp_a.expression
|
|
3084
|
+
assert grp_a.type == "OUTPUT"
|
|
3085
|
+
default_g = [g for g in rtr.groups if g.name == "DEFAULT1"][0]
|
|
3086
|
+
assert "DEFAULT" in default_g.type.upper()
|
|
3087
|
+
|
|
3088
|
+
def test_connector_group_attributes_parsed(self):
|
|
3089
|
+
xml = '''<?xml version="1.0" encoding="UTF-8"?>
|
|
3090
|
+
<!DOCTYPE POWERMART SYSTEM "powrmart.dtd">
|
|
3091
|
+
<POWERMART CREATION_DATE="01/01/2025" REPOSITORY_VERSION="1">
|
|
3092
|
+
<REPOSITORY NAME="repo" VERSION="1" CODEPAGE="UTF-8" DATABASETYPE="Oracle">
|
|
3093
|
+
<FOLDER NAME="TEST" OWNER="admin">
|
|
3094
|
+
<MAPPING NAME="m_test" ISVALID="YES">
|
|
3095
|
+
<TRANSFORMATION NAME="RTR" TYPE="Router" REUSABLE="NO">
|
|
3096
|
+
<TRANSFORMFIELD NAME="X" DATATYPE="string" PRECISION="10" SCALE="0" PORTTYPE="INPUT/OUTPUT"/>
|
|
3097
|
+
</TRANSFORMATION>
|
|
3098
|
+
<CONNECTOR FROMINSTANCE="RTR" FROMINSTANCEGROUP="GRP_A" FROMFIELD="X" TOINSTANCE="TGT" TOFIELD="X"/>
|
|
3099
|
+
</MAPPING>
|
|
3100
|
+
</FOLDER>
|
|
3101
|
+
</REPOSITORY>
|
|
3102
|
+
</POWERMART>'''
|
|
3103
|
+
from informatica_python.parser import InformaticaParser
|
|
3104
|
+
parser = InformaticaParser()
|
|
3105
|
+
pm = parser.parse_string(xml)
|
|
3106
|
+
mapping = pm.repositories[0].folders[0].mappings[0]
|
|
3107
|
+
conn = mapping.connectors[0]
|
|
3108
|
+
assert conn.from_instance_group == "GRP_A"
|
|
3109
|
+
|
|
3110
|
+
|
|
3111
|
+
class TestNotInsideIIF(unittest.TestCase):
|
|
3112
|
+
|
|
3113
|
+
def test_not_isnull_in_iif(self):
|
|
3114
|
+
expr = "IIF(NOT(ISNULL(STATUS)), 'HAS_STATUS', 'NO_STATUS')"
|
|
3115
|
+
result = convert_expression_vectorized(expr)
|
|
3116
|
+
assert "isna" in result or "isnull" in result
|
|
3117
|
+
assert "~" in result
|
|
3118
|
+
assert "np.where" in result
|
|
3119
|
+
|
|
3120
|
+
def test_not_isnull_standalone(self):
|
|
3121
|
+
from informatica_python.utils.expression_converter import convert_filter_vectorized
|
|
3122
|
+
result = convert_filter_vectorized("NOT(ISNULL(FIELD1))", "df")
|
|
3123
|
+
assert "~" in result
|
|
3124
|
+
assert "isna" in result
|
|
3125
|
+
|
|
3126
|
+
def test_not_equals_in_iif(self):
|
|
3127
|
+
expr = "IIF(NOT(STATUS = 'ACTIVE'), 'INACTIVE', 'ACTIVE')"
|
|
3128
|
+
result = convert_expression_vectorized(expr)
|
|
3129
|
+
assert "np.where" in result
|
|
3130
|
+
assert "~" in result
|
|
3131
|
+
|
|
3132
|
+
|
|
3133
|
+
class TestLookupSqlOverride(unittest.TestCase):
|
|
3134
|
+
|
|
3135
|
+
LOOKUP_SQL_XML = '''<?xml version="1.0" encoding="UTF-8"?>
|
|
3136
|
+
<!DOCTYPE POWERMART SYSTEM "powrmart.dtd">
|
|
3137
|
+
<POWERMART CREATION_DATE="01/01/2025" REPOSITORY_VERSION="1">
|
|
3138
|
+
<REPOSITORY NAME="repo" VERSION="1" CODEPAGE="UTF-8" DATABASETYPE="Oracle">
|
|
3139
|
+
<FOLDER NAME="TEST" OWNER="admin">
|
|
3140
|
+
<SOURCE NAME="SRC" DATABASETYPE="Flat File" DBDNAME="SRC">
|
|
3141
|
+
<FLATFILE DELIMITEDBY="COMMA" HEADERROWPRESENT="YES" PADBYTES="NO" ROWDELIMITER="\\n"/>
|
|
3142
|
+
<SOURCEFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" NULLABLE="NOTNULL" KEYTYPE="PRIMARY KEY" FIELDNUMBER="1"/>
|
|
3143
|
+
<SOURCEFIELD NAME="CODE" DATATYPE="string" PRECISION="10" SCALE="0" NULLABLE="NULL" KEYTYPE="NOT A KEY" FIELDNUMBER="2"/>
|
|
3144
|
+
</SOURCE>
|
|
3145
|
+
<TARGET NAME="TGT" DATABASETYPE="Flat File">
|
|
3146
|
+
<TARGETFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" NULLABLE="NULL" KEYTYPE="NOT A KEY" FIELDNUMBER="1"/>
|
|
3147
|
+
<TARGETFIELD NAME="DESC" DATATYPE="string" PRECISION="50" SCALE="0" NULLABLE="NULL" KEYTYPE="NOT A KEY" FIELDNUMBER="2"/>
|
|
3148
|
+
</TARGET>
|
|
3149
|
+
<MAPPING NAME="m_lookup_sql_test" ISVALID="YES">
|
|
3150
|
+
<TRANSFORMATION NAME="SQ_SRC" TYPE="Source Qualifier" REUSABLE="NO">
|
|
3151
|
+
<TRANSFORMFIELD NAME="ID" DATATYPE="integer" PRECISION="10" SCALE="0" PORTTYPE="OUTPUT"/>
|
|
3152
|
+
<TRANSFORMFIELD NAME="CODE" DATATYPE="string" PRECISION="10" SCALE="0" PORTTYPE="OUTPUT"/>
|
|
3153
|
+
</TRANSFORMATION>
|
|
3154
|
+
<TRANSFORMATION NAME="LKP_CODES" TYPE="Lookup Procedure" REUSABLE="NO">
|
|
3155
|
+
<TRANSFORMFIELD NAME="CODE_IN" DATATYPE="string" PRECISION="10" SCALE="0" PORTTYPE="INPUT"/>
|
|
3156
|
+
<TRANSFORMFIELD NAME="DESC_OUT" DATATYPE="string" PRECISION="50" SCALE="0" PORTTYPE="OUTPUT"/>
|
|
3157
|
+
<TABLEATTRIBUTE NAME="Lookup Sql Override" VALUE="SELECT CODE, DESCRIPTION FROM REF_CODES WHERE ACTIVE_FLAG = 'Y'"/>
|
|
3158
|
+
<TABLEATTRIBUTE NAME="Lookup table name" VALUE="REF_CODES"/>
|
|
3159
|
+
</TRANSFORMATION>
|
|
3160
|
+
<INSTANCE NAME="SRC" TYPE="Source Definition" TRANSFORMATION_NAME="SRC"/>
|
|
3161
|
+
<INSTANCE NAME="SQ_SRC" TYPE="Source Qualifier" TRANSFORMATION_NAME="SQ_SRC"/>
|
|
3162
|
+
<INSTANCE NAME="LKP_CODES" TYPE="Lookup Procedure" TRANSFORMATION_NAME="LKP_CODES"/>
|
|
3163
|
+
<INSTANCE NAME="TGT" TYPE="Target Definition" TRANSFORMATION_NAME="TGT"/>
|
|
3164
|
+
<CONNECTOR FROMINSTANCE="SRC" FROMFIELD="ID" TOINSTANCE="SQ_SRC" TOFIELD="ID"/>
|
|
3165
|
+
<CONNECTOR FROMINSTANCE="SRC" FROMFIELD="CODE" TOINSTANCE="SQ_SRC" TOFIELD="CODE"/>
|
|
3166
|
+
<CONNECTOR FROMINSTANCE="SQ_SRC" FROMFIELD="ID" TOINSTANCE="LKP_CODES" TOFIELD="CODE_IN"/>
|
|
3167
|
+
<CONNECTOR FROMINSTANCE="LKP_CODES" FROMFIELD="DESC_OUT" TOINSTANCE="TGT" TOFIELD="DESC"/>
|
|
3168
|
+
<CONNECTOR FROMINSTANCE="SQ_SRC" FROMFIELD="ID" TOINSTANCE="TGT" TOFIELD="ID"/>
|
|
3169
|
+
</MAPPING>
|
|
3170
|
+
<CONFIG NAME="default_session_config"/>
|
|
3171
|
+
<WORKFLOW NAME="wf_lookup_sql_test" ISVALID="YES">
|
|
3172
|
+
<TASK NAME="Start" REUSABLE="NO" TYPE="Start"/>
|
|
3173
|
+
<SESSION NAME="s_m_lookup_sql_test" ISVALID="YES" REUSABLE="NO" MAPPINGNAME="m_lookup_sql_test">
|
|
3174
|
+
<CONFIGREFERENCE REFOBJECTNAME="default_session_config" TYPE="Session config"/>
|
|
3175
|
+
</SESSION>
|
|
3176
|
+
<TASKINSTANCE NAME="Start" TASKNAME="Start" TASKTYPE="Start"/>
|
|
3177
|
+
<TASKINSTANCE NAME="s_m_lookup_sql_test" TASKNAME="s_m_lookup_sql_test" TASKTYPE="Session"/>
|
|
3178
|
+
<WORKFLOWLINK FROMTASK="Start" TOTASK="s_m_lookup_sql_test"/>
|
|
3179
|
+
</WORKFLOW>
|
|
3180
|
+
</FOLDER>
|
|
3181
|
+
</REPOSITORY>
|
|
3182
|
+
</POWERMART>'''
|
|
3183
|
+
|
|
3184
|
+
def test_lookup_sql_override_applied(self):
|
|
3185
|
+
converter = InformaticaConverter()
|
|
3186
|
+
tmpdir = tempfile.mkdtemp()
|
|
3187
|
+
try:
|
|
3188
|
+
converter.convert_string(self.LOOKUP_SQL_XML, output_dir=tmpdir)
|
|
3189
|
+
for fn in os.listdir(tmpdir):
|
|
3190
|
+
if fn.startswith("mapping_") and fn.endswith(".py"):
|
|
3191
|
+
with open(os.path.join(tmpdir, fn)) as f:
|
|
3192
|
+
code = f.read()
|
|
3193
|
+
assert "SELECT CODE, DESCRIPTION FROM REF_CODES" in code, \
|
|
3194
|
+
"Lookup SQL Override should appear in generated code"
|
|
3195
|
+
assert "read_from_db" in code, \
|
|
3196
|
+
"Should use read_from_db with the override SQL"
|
|
3197
|
+
break
|
|
3198
|
+
finally:
|
|
3199
|
+
shutil.rmtree(tmpdir)
|
|
3200
|
+
|
|
3201
|
+
def test_lookup_sql_in_all_sql_queries(self):
|
|
3202
|
+
converter = InformaticaConverter()
|
|
3203
|
+
tmpdir = tempfile.mkdtemp()
|
|
3204
|
+
try:
|
|
3205
|
+
converter.convert_string(self.LOOKUP_SQL_XML, output_dir=tmpdir)
|
|
3206
|
+
sql_file = os.path.join(tmpdir, "all_sql_queries.sql")
|
|
3207
|
+
if os.path.exists(sql_file):
|
|
3208
|
+
with open(sql_file) as f:
|
|
3209
|
+
sql = f.read()
|
|
3210
|
+
assert "REF_CODES" in sql, "SQL file should contain lookup SQL"
|
|
3211
|
+
finally:
|
|
3212
|
+
shutil.rmtree(tmpdir)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/__init__.py
RENAMED
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/config_gen.py
RENAMED
|
File without changes
|
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/helper_gen.py
RENAMED
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/sql_gen.py
RENAMED
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/generators/workflow_gen.py
RENAMED
|
File without changes
|
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/utils/datatype_map.py
RENAMED
|
File without changes
|
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/utils/lib_adapters.py
RENAMED
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python/utils/sql_dialect.py
RENAMED
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{informatica_python-1.9.9 → informatica_python-1.10.1}/informatica_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|