Coverage for python/felis/utils.py: 16%
49 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-23 10:44 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-23 10:44 +0000
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 Iterable, Mapping, MutableMapping
23from typing import Any
25_Mapping = Mapping[str, Any]
26_MutableMapping = MutableMapping[str, Any]
29class ReorderingVisitor:
30 """A visitor that reorders and optionally adds the "@type".
32 Parameters
33 ----------
34 add_type : `bool`
35 If true, add the "@type" if it doesn't exist.
36 """
38 def __init__(self, add_type: bool = False):
39 self.add_type = add_type
41 def visit_schema(self, schema_obj: _MutableMapping) -> _Mapping:
42 """Process schema, the input MUST be a normalized representation."""
43 # Override with default
44 tables = [self.visit_table(table_obj, schema_obj) for table_obj in schema_obj["tables"]]
45 schema_obj["tables"] = tables
46 if self.add_type:
47 schema_obj["@type"] = schema_obj.get("@type", "Schema")
48 return _new_order(
49 schema_obj, ["@context", "name", "@id", "@type", "description", "tables", "version"]
50 )
52 def visit_table(self, table_obj: _MutableMapping, schema_obj: _Mapping) -> _Mapping:
53 columns = [self.visit_column(c, table_obj) for c in table_obj["columns"]]
54 primary_key = self.visit_primary_key(table_obj.get("primaryKey", []), table_obj)
55 constraints = [self.visit_constraint(c, table_obj) for c in table_obj.get("constraints", [])]
56 indexes = [self.visit_index(i, table_obj) for i in table_obj.get("indexes", [])]
57 table_obj["columns"] = columns
58 if primary_key:
59 table_obj["primaryKey"] = primary_key
60 if constraints:
61 table_obj["constraints"] = constraints
62 if indexes:
63 table_obj["indexes"] = indexes
64 if self.add_type:
65 table_obj["@type"] = table_obj.get("@type", "Table")
66 return _new_order(
67 table_obj,
68 ["name", "@id", "@type", "description", "columns", "primaryKey", "constraints", "indexes"],
69 )
71 def visit_column(self, column_obj: _MutableMapping, table_obj: _Mapping) -> _Mapping:
72 if self.add_type:
73 column_obj["@type"] = column_obj.get("@type", "Column")
74 return _new_order(column_obj, ["name", "@id", "@type", "description", "datatype"])
76 def visit_primary_key(self, primary_key_obj: _MutableMapping, table: _Mapping) -> _Mapping:
77 # FIXME: Handle Primary Keys
78 return primary_key_obj
80 def visit_constraint(self, constraint_obj: _MutableMapping, table: _Mapping) -> _Mapping:
81 # Type MUST be present... we can skip
82 return _new_order(constraint_obj, ["name", "@id", "@type", "description"])
84 def visit_index(self, index_obj: _MutableMapping, table: _Mapping) -> _Mapping:
85 if self.add_type:
86 index_obj["@type"] = index_obj.get("@type", "Index")
87 return _new_order(index_obj, ["name", "@id", "@type", "description"])
90def _new_order(obj: _Mapping, order: Iterable[str]) -> _Mapping:
91 reordered_object: _MutableMapping = {}
92 for name in order:
93 if name in obj:
94 reordered_object[name] = obj[name]
95 for key, value in obj.items():
96 if key not in reordered_object:
97 reordered_object[key] = value
98 return reordered_object