Coverage for python/felis/utils.py: 16%
49 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-14 02:21 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-14 02:21 -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:
48 columns = [self.visit_column(c, table_obj) for c in table_obj["columns"]]
49 primary_key = self.visit_primary_key(table_obj.get("primaryKey", []), table_obj)
50 constraints = [self.visit_constraint(c, table_obj) for c in table_obj.get("constraints", [])]
51 indexes = [self.visit_index(i, table_obj) for i in table_obj.get("indexes", [])]
52 table_obj["columns"] = columns
53 if primary_key:
54 table_obj["primaryKey"] = primary_key
55 if constraints:
56 table_obj["constraints"] = constraints
57 if indexes:
58 table_obj["indexes"] = indexes
59 if self.add_type:
60 table_obj["@type"] = table_obj.get("@type", "Table")
61 return _new_order(
62 table_obj,
63 ["name", "@id", "@type", "description", "columns", "primaryKey", "constraints", "indexes"],
64 )
66 def visit_column(self, column_obj: _MutableMapping, table_obj: _Mapping) -> _Mapping:
67 if self.add_type:
68 column_obj["@type"] = column_obj.get("@type", "Column")
69 return _new_order(column_obj, ["name", "@id", "@type", "description", "datatype"])
71 def visit_primary_key(self, primary_key_obj: _MutableMapping, table: _Mapping) -> _Mapping:
72 # FIXME: Handle Primary Keys
73 return primary_key_obj
75 def visit_constraint(self, constraint_obj: _MutableMapping, table: _Mapping) -> _Mapping:
76 # Type MUST be present... we can skip
77 return _new_order(constraint_obj, ["name", "@id", "@type", "description"])
79 def visit_index(self, index_obj: _MutableMapping, table: _Mapping) -> _Mapping:
80 if self.add_type:
81 index_obj["@type"] = index_obj.get("@type", "Index")
82 return _new_order(index_obj, ["name", "@id", "@type", "description"])
85def _new_order(obj: _Mapping, order: Iterable[str]) -> _Mapping:
86 reordered_object: _MutableMapping = {}
87 for name in order:
88 if name in obj:
89 reordered_object[name] = obj[name]
90 for key, value in obj.items():
91 if key not in reordered_object:
92 reordered_object[key] = value
93 return reordered_object