1# =====================================================
2# Imports
3# =====================================================
4from typing import Annotated
5from xml.etree import cElementTree as et
6
7from loguru import logger
8from pydantic import Field, field_validator
9
10from mt_metadata.base import MetadataBase
11from mt_metadata.transfer_functions.io.emtfxml.metadata import helpers
12
13from . import Electrode
14
15
16# =====================================================
17class Dipole(MetadataBase):
18 manufacturer: Annotated[
19 str | None,
20 Field(
21 default=None,
22 description="Name of the manufacturer of the instrument",
23 json_schema_extra={
24 "units": None,
25 "required": False,
26 "examples": ["MT Gurus"],
27 },
28 ),
29 ]
30
31 length: Annotated[
32 float | None,
33 Field(
34 default=None,
35 description="Dipole length",
36 json_schema_extra={
37 "units": "meters",
38 "required": False,
39 "examples": ["100.0"],
40 },
41 ),
42 ]
43
44 azimuth: Annotated[
45 float | None,
46 Field(
47 default=None,
48 description="Azimuth of the dipole relative to coordinate system",
49 json_schema_extra={
50 "units": "degrees",
51 "required": False,
52 "examples": ["90"],
53 },
54 ),
55 ]
56
57 name: Annotated[
58 str | None,
59 Field(
60 default=None,
61 description="Name of the dipole",
62 json_schema_extra={
63 "units": None,
64 "required": False,
65 "examples": ["dipole1"],
66 },
67 ),
68 ]
69
70 type: Annotated[
71 str | None,
72 Field(
73 default=None,
74 description="type of dipole",
75 json_schema_extra={
76 "units": None,
77 "required": False,
78 "examples": ["wire"],
79 },
80 ),
81 ]
82
83 electrode: Annotated[
84 list[Electrode],
85 Field(
86 default_factory=list,
87 description="List of electrodes that make up the dipole",
88 alias=None,
89 json_schema_extra={
90 "units": None,
91 "required": False,
92 "examples": [{"name": "ex", "type": "wire"}],
93 },
94 ),
95 ]
96
97 @field_validator("electrode", mode="before")
98 def validate_electrode(cls, value: list[Electrode] | list[dict]) -> list[Electrode]:
99 """
100 Validate that the value is a list of Electrode objects.
101 """
102 if not isinstance(value, list):
103 value = [value]
104 value_list = []
105 for item in value:
106 if isinstance(item, dict):
107 e_obj = Electrode() # type: ignore
108 e_obj.from_dict(item)
109 value_list.append(e_obj)
110 elif isinstance(item, Electrode):
111 value_list.append(item)
112 else:
113 raise TypeError(
114 "Electrode must be an instance of Electrode class or a dict"
115 )
116 return value_list
117
118 def to_xml(self, string: bool = False, required: bool = True) -> str | et.Element:
119 """
120
121 :param string: DESCRIPTION, defaults to False
122 :type string: TYPE, optional
123 :param required: DESCRIPTION, defaults to True
124 :type required: TYPE, optional
125 :return: DESCRIPTION
126 :rtype: TYPE
127
128 """
129
130 root = et.Element(
131 self.__class__.__name__, {"name": self.name, "type": self.type}
132 )
133 try:
134 et.SubElement(root, "manufacturer").text = self.manufacturer
135 except AttributeError:
136 logger.debug("Dipole has no manufacturer information")
137 if self.length is not None:
138 et.SubElement(root, "length", {"units": "meters"}).text = (
139 f"{self.length:.3f}"
140 )
141 if self.azimuth is not None:
142 et.SubElement(root, "azimuth", {"units": "degrees"}).text = (
143 f"{self.azimuth:.3f}"
144 )
145 for item in self.electrode:
146 root.append(item.to_xml())
147
148 if string:
149 return helpers.element_to_string(root)
150 return root