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