20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 | @dataclass
class Session:
"""
A wrapper object for a transformer session.
Todo:
- rename to Manager?
- consolidate configuration
- include source and target database
- current spec, src_sv, tgt_sv all live in both this class and transformer
"""
transformer_specification: TransformationSpecification | None = None
source_schemaview: SchemaView | None = None
transformer: Transformer | None = None
object_transformer: ObjectTransformer | None = None
schema_mapper: SchemaMapper | None = None
_target_schema: SchemaDefinition | None = None
_target_schemaview: SchemaView | None = None
def set_transformer_specification(
self, specification: TransformationSpecification | dict | str | Path | None = None
) -> None:
if isinstance(specification, Path):
specification = str(specification)
if isinstance(specification, TransformationSpecification):
self.transformer_specification = specification
elif isinstance(specification, dict):
Transformer._normalize_spec_dict(specification)
self.transformer_specification = TransformationSpecification(**specification)
elif isinstance(specification, str):
if "\n" in specification:
obj = yaml.safe_load(specification)
else:
obj = yaml.safe_load(open(specification))
self.set_transformer_specification(obj)
def set_source_schema(self, schema: str | Path | dict | SchemaView | SchemaDefinition) -> None:
"""
Set the schema from a path or SchemaView object.
"""
if isinstance(schema, str):
sv = SchemaView(schema)
elif isinstance(schema, Path):
sv = SchemaView(str(schema))
elif isinstance(schema, dict):
sv = SchemaView(yaml_dumper.dumps(schema))
elif isinstance(schema, SchemaView):
sv = schema
elif isinstance(schema, SchemaDefinition):
sv = SchemaView(schema)
else:
msg = f"Unsupported schema type: {type(schema)}"
raise ValueError(msg)
self.source_schemaview = sv
self._target_schema = None
def set_transformer(
self,
transformer: Transformer | type[Transformer] | None,
**kwargs,
) -> None:
if isinstance(transformer, type):
transformer = transformer()
transformer.specification = self.transformer_specification
self.transformer = transformer
def set_object_transformer(
self,
transformer: ObjectTransformer | TransformationSpecification | dict | str | Path | None = None,
) -> None:
if transformer is None:
if self.object_transformer is not None:
logger.info("No change")
return
logger.warning("No transformer specified")
return
if transformer is not None:
if isinstance(transformer, ObjectTransformer):
self.object_transformer = transformer
else:
self.set_transformer_specification(transformer)
self.object_transformer = ObjectTransformer()
self.object_transformer.specification = self.transformer_specification
self._target_schema = None
@property
def target_schema(self) -> SchemaDefinition:
if self._target_schema is None:
if not self.schema_mapper:
self.schema_mapper = SchemaMapper(source_schemaview=self.source_schemaview)
self._target_schema = self.schema_mapper.derive_schema(self.transformer_specification)
return self._target_schema
@property
def target_schemaview(self) -> SchemaView:
if self._target_schemaview is None:
# TODO: simplify
self._target_schemaview = SchemaView(yaml_dumper.dumps(self.target_schema))
return self._target_schemaview
def transform(self, obj: dict, **kwargs) -> dict:
if self.object_transformer is None:
msg = "No transformer specified"
raise ValueError(msg)
if not self.object_transformer.source_schemaview:
self.object_transformer.source_schemaview = self.source_schemaview
return self.object_transformer.map_object(obj, **kwargs)
def reverse_transform(self, obj: dict, **kwargs) -> dict:
inv_spec = self.invert()
reverse_transformer = ObjectTransformer()
reverse_transformer.specification = inv_spec
reverse_transformer.source_schemaview = SchemaView(yaml_dumper.dumps(self.target_schema))
return reverse_transformer.map_object(obj, **kwargs)
def invert(self, in_place=False) -> TransformationSpecification:
"""
Invert the transformer specification.
"""
inverter = TransformationSpecificationInverter(
source_schemaview=self.source_schemaview,
target_schemaview=SchemaView(yaml_dumper.dumps(self.target_schema)),
)
inv_spec = inverter.invert(self.transformer_specification)
if in_place:
raise NotImplementedError
return inv_spec
def graphviz(self, **kwargs) -> Any:
"""
Return a graphviz representation of the schema.
"""
from linkml_map.compiler.graphviz_compiler import GraphvizCompiler
gc = GraphvizCompiler(source_schemaview=self.source_schemaview)
compiled = gc.compile(self.transformer_specification)
return compiled.digraph
|