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

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/>. 

21 

22from collections.abc import Mapping, MutableMapping 

23from typing import Any, Iterable 

24 

25_Mapping = Mapping[str, Any] 

26_MutableMapping = MutableMapping[str, Any] 

27 

28 

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 

36 

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"]) 

45 

46 def visit_table(self, table_obj: _MutableMapping, schema_obj: _Mapping) -> _Mapping: 

47 

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 ) 

65 

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"]) 

70 

71 def visit_primary_key(self, primary_key_obj: _MutableMapping, table: _Mapping) -> _Mapping: 

72 # FIXME: Handle Primary Keys 

73 return primary_key_obj 

74 

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"]) 

78 

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"]) 

83 

84 

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