Coverage for gws-app/gws/lib/misc/tree_viewer/__init__.py: 0%

112 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-17 01:37 +0200

1"""Take a pickled GWS tree and output a browsable HTML page.""" 

2 

3import builtins 

4import json 

5import os 

6import pickle 

7import sys 

8import re 

9 

10 

11def load_pickle(path): 

12 class Unpickler(pickle.Unpickler): 

13 def find_class(self, module, name): 

14 if module == 'builtins': 

15 return getattr(builtins, name) 

16 

17 class T: 

18 KLASS = module + '.' + name 

19 

20 def __init__(self, *args): 

21 pass 

22 

23 def __setstate__(self, state): 

24 if not isinstance(state, dict): 

25 state = {'?': state} 

26 self.STATE = state 

27 

28 def __getattr__(self, item): 

29 return '?' + item 

30 

31 def __call__(self, *args, **kwargs): 

32 pass 

33 

34 def __setitem__(self, *args, **kwargs): 

35 pass 

36 

37 def _unpickle(self, *args): 

38 pass 

39 

40 return T 

41 

42 hash_map = {} 

43 obj_list = [] 

44 

45 def transform(obj): 

46 if obj is None: 

47 return obj 

48 if isinstance(obj, (str, int, float, bool)): 

49 return obj 

50 if isinstance(obj, (list, tuple, set)): 

51 return [transform(x) for x in obj] 

52 if isinstance(obj, dict): 

53 return {str(k): transform(v) for k, v in obj.items()} 

54 if hasattr(obj, 'KLASS'): 

55 h = hash(obj) 

56 if h in hash_map: 

57 return hash_map[h] 

58 

59 idx = len(obj_list) 

60 obj_list.append('dummy value') 

61 

62 cls = obj.KLASS 

63 if isinstance(obj, type): 

64 cls = 'CLASS:' + cls 

65 

66 state = getattr(obj, 'STATE', {}) 

67 if not isinstance(state, dict): 

68 state = {'state': repr(state)} 

69 

70 name = '$.' + cls + ':' + str(idx) 

71 if state.get('uid'): 

72 name += ':uid=' + str(state['uid']) 

73 hash_map[h] = name 

74 

75 d = {str(k): transform(v) for k, v in state.items()} 

76 d['$'] = name 

77 obj_list[idx] = d 

78 

79 return name 

80 

81 return 'UNKNOWN: ' + repr(obj) 

82 

83 def flat(val): 

84 if isinstance(val, str): 

85 return [val] 

86 if isinstance(val, (list, tuple, set)): 

87 return [v for e in val for v in flat(e)] 

88 if isinstance(val, dict): 

89 return [v for e in val.values() for v in flat(e)] 

90 return [] 

91 

92 def make_refs(): 

93 ref_map = {d['$']: d for d in obj_list} 

94 for d in obj_list: 

95 for prop, v in d.items(): 

96 if prop == '$': 

97 continue 

98 for v2 in flat(v): 

99 if v2 in ref_map and v2 != d['$']: 

100 ref_map[v2].setdefault('$REFS', []).append(d['$'] + ' @ ' + prop) 

101 

102 with open(path, 'rb') as fp: 

103 tree = Unpickler(fp).load() 

104 

105 transform(tree) 

106 make_refs() 

107 

108 return obj_list 

109 

110 

111path = sys.argv[1] 

112obj_list = load_pickle(path) 

113 

114try: 

115 mode = sys.argv[2] 

116except: 

117 mode = 'json' 

118 

119path = json.dumps(path) 

120data = json.dumps(obj_list, indent=4, sort_keys=True).replace('</script>', '<\\/script>') 

121 

122if mode == 'html': 

123 cdir = os.path.dirname(__file__) 

124 

125 with open(cdir + '/css.css') as fp: 

126 css = f'<style>\n{fp.read()}\n</style>' 

127 with open(cdir + '/js.js') as fp: 

128 js = f'<script>\n{fp.read()}\n</script>' 

129 

130 # for debugging 

131 # css = f'<link rel="stylesheet" href="{cdir}/css.css">' 

132 # js = f'<script src="{cdir}/js.js"></script>' 

133 

134 content = f"""<!doctype html> 

135 <html> 

136 <head> 

137 <meta charset="utf-8"> 

138 {css} 

139 </head> 

140 <body> 

141 <script>PATH={path}</script> 

142 <script>DATA={data}</script> 

143 {js} 

144 </body> 

145 </html> 

146 """ 

147 

148 print(content) 

149 

150if mode == 'json': 

151 print(data) 

152 

153if mode == 'bounds': 

154 obj_map = {d['$']: d for d in obj_list} 

155 

156 for d in obj_list: 

157 b = d.get('wgsExtent') 

158 if not b: 

159 continue 

160 x1, y1, x2, y2 = b 

161 feature = { 

162 "type": "Feature", 

163 "properties": { 

164 "id": d['$'], 

165 "name": d.get('name') or d.get('title') or '', 

166 }, 

167 "geometry": { 

168 "type": "Polygon", 

169 "coordinates": [ 

170 [[x1, y1], [x1, y2], [x2, y2], [x2, y1], [x1, y1]] 

171 ] 

172 } 

173 } 

174 file_name = re.sub( 

175 r'\W+', 

176 '_', 

177 feature['properties']['name'] + '_' + feature['properties']['id'], 

178 ) 

179 fc = { 

180 "type": "FeatureCollection", 

181 "features": [feature], 

182 } 

183 with open(f'{file_name}.geojson', 'w') as fp: 

184 json.dump(fc, fp, indent=4)