Coverage for C: \ Users \ peaco \ OneDrive \ Documents \ GitHub \ mt_metadata \ mt_metadata \ timeseries \ stationxml \ utils.py: 91%

106 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-10 00:11 -0800

1# -*- coding: utf-8 -*- 

2""" 

3Created on Tue Feb 16 10:33:27 2021 

4 

5:copyright: 

6 Jared Peacock (jpeacock@usgs.gov) 

7 

8:license: MIT 

9 

10""" 

11from loguru import logger 

12from obspy.core.inventory import Comment 

13 

14 

15# ============================================================================= 

16# Translate between metadata and inventory: mapping dictionaries 

17# ============================================================================= 

18class BaseTranslator: 

19 """ 

20 Base translator for StationXML <--> MT Metadata 

21 

22 """ 

23 

24 def __init__(self): 

25 self.logger = logger 

26 self.xml_translator = { 

27 "alternate_code": None, 

28 "code": None, 

29 "comments": None, 

30 "data_availability": None, 

31 "description": None, 

32 "historical_code": None, 

33 "identifiers": None, 

34 "restricted_status": None, 

35 "source_id": None, 

36 } 

37 

38 self.mt_translator = self.flip_dict(self.xml_translator) 

39 self.mt_comments_list = [] 

40 

41 @staticmethod 

42 def flip_dict(original_dict): 

43 """ 

44 Flip keys and values of the dictionary 

45 

46 Need to take care of duplicate names and lists of names 

47 

48 :param original_dict: original dictionary 

49 :type original_dict: dict 

50 :return: reversed dictionary 

51 :rtype: dictionary 

52 

53 """ 

54 flipped_dict = {} 

55 

56 for k, v in original_dict.items(): 

57 if v in [None, "special"]: 

58 continue 

59 if k in [None]: 

60 continue 

61 if isinstance(v, (list, tuple)): 

62 # bit of a hack, needs to be more unique. 

63 for value in v: 

64 flipped_dict[value] = k 

65 else: 

66 flipped_dict[str(v)] = k 

67 

68 return flipped_dict 

69 

70 @staticmethod 

71 def read_xml_comment(comment): 

72 """ 

73 read stationxml comment 

74 

75 Assuming that separate comments are split by ':' and separated 

76 by a comma. 

77 

78 """ 

79 if comment.subject is not None: 

80 key = comment.subject.strip().replace(" ", "_").lower() 

81 else: 

82 key = "mt" 

83 

84 def parse(comment_string, filled=None, depth=0): 

85 """ 

86 Recursively parse a comment string trying to adhere to the 

87 original syntax of the comment. Expecting a dictionary type 

88 string 

89 

90 'a: b, c:d' -> {'a': 'b', 'c':'d'} 

91 

92 but sometimes looks like 

93 

94 'a: b:c, d:e' -> {'a': 'b:c', 'd':'e'} 

95 

96 or 

97 

98 'a: b, b2, c: d:e' -> {'a': 'b:c', 'd':'e'} 

99 

100 """ 

101 if filled is None: 

102 filled = {} 

103 

104 # Add recursion depth limit 

105 if depth > 20: # Arbitrary limit to prevent stack overflow 

106 return filled 

107 

108 if hasattr(comment_string, "value"): 

109 comment_string = comment_string.value 

110 if "author:" in comment_string and "comments:" in comment_string: 

111 author, comments = [ 

112 s.strip() 

113 for s in comment_string.split("author:", 1)[1].split("comments:", 1) 

114 ] 

115 

116 if author.endswith(","): 

117 author = author[:-1] 

118 

119 return {"author": author, "comments": comments} 

120 

121 else: 

122 k, *other = comment_string.split(":", 1) 

123 if other: 

124 other = other[0] 

125 key = k 

126 if other.find(":") >= 0 and other.find(",") >= 0: 

127 if other.find(":") < other.find(","): 

128 if other.count(":") > 1: 

129 value, *maybe = other.split(",", 1) 

130 filled[key] = value.strip().replace(":", "--") 

131 if maybe: 

132 filled = parse(maybe[0].strip(), filled, depth + 1) 

133 else: 

134 filled[key] = other.replace(":", "--").strip() 

135 else: 

136 value, *maybe = other.split(",", 1) 

137 filled[key] = value.strip() 

138 if maybe: 

139 filled = parse(maybe[0].strip(), filled, depth + 1) 

140 elif other.find(":") > 0: 

141 value, *maybe = other.split(":", 1) 

142 filled[key] = value.strip() 

143 else: 

144 filled[key] = other.strip() 

145 

146 else: 

147 filled[k] = None 

148 return filled 

149 

150 # if the string is dictionary like, parse, otherwise skip 

151 if ":" in comment.value: 

152 value = parse(comment.value) 

153 else: 

154 value = comment.value 

155 

156 return key, value 

157 

158 @staticmethod 

159 def read_xml_identifier(identifiers): 

160 """ 

161 Read stationxml idenfier, which is a list of doi numbers, make 

162 it into a string without the doi 

163 

164 :param doi: DESCRIPTION 

165 :type doi: TYPE 

166 :return: DESCRIPTION 

167 :rtype: TYPE 

168 

169 """ 

170 return ", ".join( 

171 [ii.strip().replace("DOI:", "https://doi.org/") for ii in identifiers] 

172 ) 

173 

174 def get_comment(self, comments, subject): 

175 """ 

176 Get the correct comment from a list of comments 

177 

178 :param comments: list of :class:`obspy.core.inventory.Comments` 

179 :type comments: list 

180 :param subject: subject heading to get 

181 :type subject: string 

182 :return: the corresponding comment 

183 :rtype: :class:`obspy.core.inventory.Comments` 

184 

185 """ 

186 

187 for comment in comments: 

188 if comment.subject == subject: 

189 return comment 

190 

191 self.logger.info(f"Could not find {subject} in the given list of comments.") 

192 return None 

193 

194 def make_mt_comments(self, mt_element, mt_key_base="mt"): 

195 """ 

196 make comments from an MT element from self.mt_comments_list 

197 

198 :param mt_element: MT metadata element 

199 """ 

200 comments = [] 

201 # add comments for MT specific information 

202 try: 

203 key_list = sorted(self.mt_comments_list) 

204 except TypeError: 

205 key_list = self.mt_comments_list 

206 for key in key_list: 

207 if isinstance(key, dict): 

208 values = [] 

209 comment_key = list(key.keys())[0] 

210 for part in key[comment_key]: 

211 info = part.split(".")[-1] 

212 value = mt_element.get_attr_from_name(part) 

213 if hasattr(value, "value"): 

214 value = value.value 

215 if value: 

216 values.append(f"{info}: {value}") 

217 value = ", ".join(values) 

218 comment = Comment(value, subject=f"{mt_key_base}.{comment_key}") 

219 comments.append(comment) 

220 else: 

221 value = mt_element.get_attr_from_name(key) 

222 if hasattr(value, "value"): 

223 value = value.value 

224 if value: 

225 if isinstance(value, (list, tuple)): 

226 value = ", ".join(value) 

227 comment = Comment(value, subject=f"{mt_key_base}.{key}") 

228 comments.append(comment) 

229 return comments 

230 

231 def xml_to_mt(self, value): 

232 pass 

233 

234 def mt_to_xml(self, value): 

235 pass