Coverage for python/felis/utils.py: 16%
49 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-10 02:14 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-10 02:14 -0800
1# This file is part of felis.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22from collections.abc import Mapping, MutableMapping
23from typing import Any, Iterable
25_Mapping = Mapping[str, Any]
26_MutableMapping = MutableMapping[str, Any]
29class ReorderingVisitor:
30 def __init__(self, add_type: bool = False):
31 """
32 A visitor that reorders and optionall adds the "@type"
33 :param add_type: If true, add the "@type" if it doesn't exist
34 """
35 self.add_type = add_type
37 def visit_schema(self, schema_obj: _MutableMapping) -> _Mapping:
38 """The input MUST be a normalized representation"""
39 # Override with default
40 tables = [self.visit_table(table_obj, schema_obj) for table_obj in schema_obj["tables"]]
41 schema_obj["tables"] = tables
42 if self.add_type:
43 schema_obj["@type"] = schema_obj.get("@type", "Schema")
44 return _new_order(schema_obj, ["@context", "name", "@id", "@type", "description", "tables"])
46 def visit_table(self, table_obj: _MutableMapping, schema_obj: _Mapping) -> _Mapping:
47 columns = [self.visit_column(c, table_obj) for c in table_obj["columns"]]
48 primary_key = self.visit_primary_key(table_obj.get("primaryKey", []), table_obj)
49 constraints = [self.visit_constraint(c, table_obj) for c in table_obj.get("constraints", [])]
50 indexes = [self.visit_index(i, table_obj) for i in table_obj.get("indexes", [])]
51 table_obj["columns"] = columns
52 if primary_key:
53 table_obj["primaryKey"] = primary_key
54 if constraints:
55 table_obj["constraints"] = constraints
56 if indexes:
57 table_obj["indexes"] = indexes
58 if self.add_type:
59 table_obj["@type"] = table_obj.get("@type", "Table")
60 return _new_order(
61 table_obj,
62 ["name", "@id", "@type", "description", "columns", "primaryKey", "constraints", "indexes"],
63 )
65 def visit_column(self, column_obj: _MutableMapping, table_obj: _Mapping) -> _Mapping:
66 if self.add_type:
67 column_obj["@type"] = column_obj.get("@type", "Column")
68 return _new_order(column_obj, ["name", "@id", "@type", "description", "datatype"])
70 def visit_primary_key(self, primary_key_obj: _MutableMapping, table: _Mapping) -> _Mapping:
71 # FIXME: Handle Primary Keys
72 return primary_key_obj
74 def visit_constraint(self, constraint_obj: _MutableMapping, table: _Mapping) -> _Mapping:
75 # Type MUST be present... we can skip
76 return _new_order(constraint_obj, ["name", "@id", "@type", "description"])
78 def visit_index(self, index_obj: _MutableMapping, table: _Mapping) -> _Mapping:
79 if self.add_type:
80 index_obj["@type"] = index_obj.get("@type", "Index")
81 return _new_order(index_obj, ["name", "@id", "@type", "description"])
84def _new_order(obj: _Mapping, order: Iterable[str]) -> _Mapping:
85 reordered_object: _MutableMapping = {}
86 for name in order:
87 if name in obj:
88 reordered_object[name] = obj[name]
89 for key, value in obj.items():
90 if key not in reordered_object:
91 reordered_object[key] = value
92 return reordered_object