logs-py 4.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. LOGS/Auxiliary/CheckClassName.py +1075 -0
  2. LOGS/Auxiliary/Constants.py +99 -0
  3. LOGS/Auxiliary/CustomEntityClassGenerator.py +254 -0
  4. LOGS/Auxiliary/CustomFieldClassGenerator.py +115 -0
  5. LOGS/Auxiliary/CustomFieldValueTypeChecker.py +168 -0
  6. LOGS/Auxiliary/CustomSectionClassGenerator.py +113 -0
  7. LOGS/Auxiliary/CustomTypeClassGenerator.py +147 -0
  8. LOGS/Auxiliary/DateTimeConverter.py +66 -0
  9. LOGS/Auxiliary/Decorators.py +109 -0
  10. LOGS/Auxiliary/Exceptions.py +341 -0
  11. LOGS/Auxiliary/LOGSErrorResponse.py +89 -0
  12. LOGS/Auxiliary/MinimalModelGenerator.py +236 -0
  13. LOGS/Auxiliary/ParameterHelper.py +56 -0
  14. LOGS/Auxiliary/ReplaceMessage.py +13 -0
  15. LOGS/Auxiliary/Tools.py +432 -0
  16. LOGS/Auxiliary/__init__.py +15 -0
  17. LOGS/Converter/Conversion.py +248 -0
  18. LOGS/Converter/Converter.py +96 -0
  19. LOGS/Converter/ConverterParameter.py +88 -0
  20. LOGS/Converter/DateTimeRange.py +58 -0
  21. LOGS/Converter/ExportParameters.py +89 -0
  22. LOGS/Converter/__init__.py +13 -0
  23. LOGS/Entities/Attachment.py +84 -0
  24. LOGS/Entities/AttachmentMinimal.py +8 -0
  25. LOGS/Entities/AttachmentRequestParameter.py +42 -0
  26. LOGS/Entities/Attachments.py +53 -0
  27. LOGS/Entities/AutoloadFileInfo.py +12 -0
  28. LOGS/Entities/AutoloadStatusError.py +7 -0
  29. LOGS/Entities/AxisNaming.py +33 -0
  30. LOGS/Entities/AxisZoom.py +33 -0
  31. LOGS/Entities/Bridge.py +165 -0
  32. LOGS/Entities/BridgeClientInfo.py +93 -0
  33. LOGS/Entities/BridgeMinimal.py +8 -0
  34. LOGS/Entities/BridgeRequestParameter.py +49 -0
  35. LOGS/Entities/BridgeType.py +7 -0
  36. LOGS/Entities/Bridges.py +12 -0
  37. LOGS/Entities/CustomField.py +243 -0
  38. LOGS/Entities/CustomFieldMinimal.py +8 -0
  39. LOGS/Entities/CustomFieldModels.py +111 -0
  40. LOGS/Entities/CustomFieldRequestParameter.py +69 -0
  41. LOGS/Entities/CustomFieldSearchQuery.py +40 -0
  42. LOGS/Entities/CustomFields.py +12 -0
  43. LOGS/Entities/CustomType.py +212 -0
  44. LOGS/Entities/CustomTypeMinimal.py +8 -0
  45. LOGS/Entities/CustomTypeRequestParameter.py +60 -0
  46. LOGS/Entities/CustomTypeSection.py +63 -0
  47. LOGS/Entities/CustomTypes.py +12 -0
  48. LOGS/Entities/DataFormat.py +97 -0
  49. LOGS/Entities/DataFormatInstrument.py +18 -0
  50. LOGS/Entities/DataFormatInstrumentMinimal.py +8 -0
  51. LOGS/Entities/DataFormatInstrumentRequestParameter.py +17 -0
  52. LOGS/Entities/DataFormatInstruments.py +16 -0
  53. LOGS/Entities/DataFormatMinimal.py +18 -0
  54. LOGS/Entities/DataFormatRequestParameter.py +21 -0
  55. LOGS/Entities/DataFormats.py +12 -0
  56. LOGS/Entities/DataSource.py +218 -0
  57. LOGS/Entities/DataSourceConnectionStatus.py +12 -0
  58. LOGS/Entities/DataSourceMinimal.py +8 -0
  59. LOGS/Entities/DataSourceRequestParameter.py +57 -0
  60. LOGS/Entities/DataSourceStatus.py +108 -0
  61. LOGS/Entities/DataSourceStatusIterator.py +16 -0
  62. LOGS/Entities/DataSourceStatusRequestParameter.py +31 -0
  63. LOGS/Entities/DataSources.py +12 -0
  64. LOGS/Entities/Dataset.py +439 -0
  65. LOGS/Entities/DatasetBase.py +196 -0
  66. LOGS/Entities/DatasetCreator.py +148 -0
  67. LOGS/Entities/DatasetInfo.py +147 -0
  68. LOGS/Entities/DatasetMatchTypes.py +157 -0
  69. LOGS/Entities/DatasetMatching.py +196 -0
  70. LOGS/Entities/DatasetMinimal.py +8 -0
  71. LOGS/Entities/DatasetModels.py +33 -0
  72. LOGS/Entities/DatasetRequestParameter.py +92 -0
  73. LOGS/Entities/DatasetTemplate.py +23 -0
  74. LOGS/Entities/DatasetUploadParameter.py +14 -0
  75. LOGS/Entities/Datasets.py +142 -0
  76. LOGS/Entities/Datatrack.py +179 -0
  77. LOGS/Entities/DatatrackFormattedTable.py +25 -0
  78. LOGS/Entities/DatatrackGeneric.py +34 -0
  79. LOGS/Entities/DatatrackImage.py +25 -0
  80. LOGS/Entities/DatatrackNumericArray.py +30 -0
  81. LOGS/Entities/DatatrackNumericMatrix.py +98 -0
  82. LOGS/Entities/Entities.py +71 -0
  83. LOGS/Entities/EntitiesRequestParameter.py +18 -0
  84. LOGS/Entities/EntityOriginWriteModelWithId.py +15 -0
  85. LOGS/Entities/FileEntry.py +138 -0
  86. LOGS/Entities/FileExcludePattern.py +8 -0
  87. LOGS/Entities/FormatMetaData.py +56 -0
  88. LOGS/Entities/FormattedTable/DatatypeFormattedTable.py +135 -0
  89. LOGS/Entities/FormattedTable/DatatypeFormattedTableCell.py +108 -0
  90. LOGS/Entities/FormattedTable/DatatypeFormattedTableSettings.py +11 -0
  91. LOGS/Entities/FormattedTable/__init__.py +9 -0
  92. LOGS/Entities/HierarchyLeaf.py +15 -0
  93. LOGS/Entities/HierarchyNode.py +40 -0
  94. LOGS/Entities/ILiteraryTypedEntity.py +19 -0
  95. LOGS/Entities/InventoryItem.py +102 -0
  96. LOGS/Entities/InventoryItemMinimal.py +25 -0
  97. LOGS/Entities/InventoryItemRequestParameter.py +58 -0
  98. LOGS/Entities/InventoryItems.py +12 -0
  99. LOGS/Entities/LabNotebook.py +33 -0
  100. LOGS/Entities/LabNotebookEntries.py +16 -0
  101. LOGS/Entities/LabNotebookEntry.py +106 -0
  102. LOGS/Entities/LabNotebookEntryContent/BasicAttribute.py +15 -0
  103. LOGS/Entities/LabNotebookEntryContent/EntityAttribute.py +85 -0
  104. LOGS/Entities/LabNotebookEntryContent/EntryContentBlockquote.py +13 -0
  105. LOGS/Entities/LabNotebookEntryContent/EntryContentBulletList.py +17 -0
  106. LOGS/Entities/LabNotebookEntryContent/EntryContentCallout.py +40 -0
  107. LOGS/Entities/LabNotebookEntryContent/EntryContentContentPlaceholderNode.py +31 -0
  108. LOGS/Entities/LabNotebookEntryContent/EntryContentConverter.py +207 -0
  109. LOGS/Entities/LabNotebookEntryContent/EntryContentDocument.py +8 -0
  110. LOGS/Entities/LabNotebookEntryContent/EntryContentEntity.py +13 -0
  111. LOGS/Entities/LabNotebookEntryContent/EntryContentEntityMention.py +31 -0
  112. LOGS/Entities/LabNotebookEntryContent/EntryContentHeading.py +33 -0
  113. LOGS/Entities/LabNotebookEntryContent/EntryContentHorizontalRule.py +12 -0
  114. LOGS/Entities/LabNotebookEntryContent/EntryContentItem.py +37 -0
  115. LOGS/Entities/LabNotebookEntryContent/EntryContentListItem.py +49 -0
  116. LOGS/Entities/LabNotebookEntryContent/EntryContentOrderedList.py +31 -0
  117. LOGS/Entities/LabNotebookEntryContent/EntryContentParagraph.py +13 -0
  118. LOGS/Entities/LabNotebookEntryContent/EntryContentTable.py +17 -0
  119. LOGS/Entities/LabNotebookEntryContent/EntryContentTableCell.py +40 -0
  120. LOGS/Entities/LabNotebookEntryContent/EntryContentTableRow.py +8 -0
  121. LOGS/Entities/LabNotebookEntryContent/EntryContentTaskList.py +17 -0
  122. LOGS/Entities/LabNotebookEntryContent/EntryContentTaskListItem.py +31 -0
  123. LOGS/Entities/LabNotebookEntryContent/EntryContentText.py +33 -0
  124. LOGS/Entities/LabNotebookEntryContent/IEntryContentWithAttribute.py +23 -0
  125. LOGS/Entities/LabNotebookEntryContent/IEntryContentWithContent.py +38 -0
  126. LOGS/Entities/LabNotebookEntryContent/IEntryContentWithTextAttribute.py +16 -0
  127. LOGS/Entities/LabNotebookEntryContent/TextAttribute.py +46 -0
  128. LOGS/Entities/LabNotebookEntryContent/TextMarkAtributes.py +64 -0
  129. LOGS/Entities/LabNotebookEntryContent/TextMarkConverter.py +45 -0
  130. LOGS/Entities/LabNotebookEntryContent/TextMarks.py +71 -0
  131. LOGS/Entities/LabNotebookEntryContent/__init__.py +34 -0
  132. LOGS/Entities/LabNotebookEntryMinimal.py +8 -0
  133. LOGS/Entities/LabNotebookEntryRequestParameter.py +59 -0
  134. LOGS/Entities/LabNotebookExperiment.py +58 -0
  135. LOGS/Entities/LabNotebookExperimentMinimal.py +8 -0
  136. LOGS/Entities/LabNotebookExperimentRequestParameter.py +52 -0
  137. LOGS/Entities/LabNotebookExperiments.py +16 -0
  138. LOGS/Entities/LabNotebookMinimal.py +8 -0
  139. LOGS/Entities/LabNotebookModels.py +14 -0
  140. LOGS/Entities/LabNotebookRequestParameter.py +42 -0
  141. LOGS/Entities/LabNotebookTemplate.py +42 -0
  142. LOGS/Entities/LabNotebookTemplateMinimal.py +8 -0
  143. LOGS/Entities/LabNotebookTemplateRequestParameter.py +38 -0
  144. LOGS/Entities/LabNotebookTemplates.py +16 -0
  145. LOGS/Entities/LabNotebooks.py +12 -0
  146. LOGS/Entities/Method.py +66 -0
  147. LOGS/Entities/MethodMinimal.py +8 -0
  148. LOGS/Entities/MethodRequestParameter.py +16 -0
  149. LOGS/Entities/Methods.py +12 -0
  150. LOGS/Entities/Origin.py +53 -0
  151. LOGS/Entities/OriginMinimal.py +8 -0
  152. LOGS/Entities/OriginRequestParameter.py +28 -0
  153. LOGS/Entities/Origins.py +12 -0
  154. LOGS/Entities/ParserLog.py +49 -0
  155. LOGS/Entities/Permission.py +9 -0
  156. LOGS/Entities/Person.py +145 -0
  157. LOGS/Entities/PersonCategory.py +12 -0
  158. LOGS/Entities/PersonMinimal.py +8 -0
  159. LOGS/Entities/PersonRequestParameter.py +58 -0
  160. LOGS/Entities/Persons.py +12 -0
  161. LOGS/Entities/Project.py +52 -0
  162. LOGS/Entities/ProjectMinimal.py +8 -0
  163. LOGS/Entities/ProjectPersonPermission.py +102 -0
  164. LOGS/Entities/ProjectRequestParameter.py +58 -0
  165. LOGS/Entities/Projects.py +12 -0
  166. LOGS/Entities/Role.py +94 -0
  167. LOGS/Entities/RoleMinimal.py +8 -0
  168. LOGS/Entities/RoleRequestParameter.py +40 -0
  169. LOGS/Entities/Roles.py +12 -0
  170. LOGS/Entities/RunState.py +9 -0
  171. LOGS/Entities/Sample.py +53 -0
  172. LOGS/Entities/SampleMinimal.py +8 -0
  173. LOGS/Entities/SampleRequestParameter.py +54 -0
  174. LOGS/Entities/Samples.py +12 -0
  175. LOGS/Entities/SharedContent.py +87 -0
  176. LOGS/Entities/SharedContentMinimal.py +8 -0
  177. LOGS/Entities/SharedContentRequestParameter.py +38 -0
  178. LOGS/Entities/SharedContents.py +12 -0
  179. LOGS/Entities/Signature.py +60 -0
  180. LOGS/Entities/Track.py +93 -0
  181. LOGS/Entities/TrackData.py +20 -0
  182. LOGS/Entities/TrackImage.py +21 -0
  183. LOGS/Entities/TrackImageData.py +20 -0
  184. LOGS/Entities/TrackMatrix.py +28 -0
  185. LOGS/Entities/TrackMatrixData.py +22 -0
  186. LOGS/Entities/TrackSettings.py +55 -0
  187. LOGS/Entities/TrackTable.py +21 -0
  188. LOGS/Entities/TrackTableData.py +22 -0
  189. LOGS/Entities/TrackXY.py +40 -0
  190. LOGS/Entities/TrackXYComplex.py +51 -0
  191. LOGS/Entities/TrackXYComplexData.py +50 -0
  192. LOGS/Entities/TrackXYData.py +31 -0
  193. LOGS/Entities/Vendor.py +40 -0
  194. LOGS/Entities/VendorMinimal.py +8 -0
  195. LOGS/Entities/VendorRequestParameter.py +17 -0
  196. LOGS/Entities/Vendors.py +12 -0
  197. LOGS/Entities/__init__.py +118 -0
  198. LOGS/Entity/ConnectedEntity.py +170 -0
  199. LOGS/Entity/Entity.py +203 -0
  200. LOGS/Entity/EntityConnector.py +70 -0
  201. LOGS/Entity/EntityIterator.py +263 -0
  202. LOGS/Entity/EntityMinimal.py +141 -0
  203. LOGS/Entity/EntityMinimalWithIntId.py +36 -0
  204. LOGS/Entity/EntityMinimalWithStrId.py +36 -0
  205. LOGS/Entity/EntityMinimalWithType.py +47 -0
  206. LOGS/Entity/EntityRequestParameter.py +104 -0
  207. LOGS/Entity/EntitySortBy.py +69 -0
  208. LOGS/Entity/EntityWithIntId.py +26 -0
  209. LOGS/Entity/EntityWithStrId.py +26 -0
  210. LOGS/Entity/IGenericEntityOrderBy.py +55 -0
  211. LOGS/Entity/IdIterator.py +207 -0
  212. LOGS/Entity/SerializableContent.py +834 -0
  213. LOGS/Entity/__init__.py +23 -0
  214. LOGS/Interfaces/ICustomFieldValue.py +92 -0
  215. LOGS/Interfaces/ICustomSectionValue.py +161 -0
  216. LOGS/Interfaces/ICustomTypeValue.py +152 -0
  217. LOGS/Interfaces/ICustomValue.py +28 -0
  218. LOGS/Interfaces/IEntityInterface.py +7 -0
  219. LOGS/Interfaces/IEntryRecord.py +57 -0
  220. LOGS/Interfaces/IHierarchicalEntity.py +41 -0
  221. LOGS/Interfaces/IHierarchyType.py +63 -0
  222. LOGS/Interfaces/ILockableEntity.py +52 -0
  223. LOGS/Interfaces/IModificationRecord.py +56 -0
  224. LOGS/Interfaces/INamedEntity.py +25 -0
  225. LOGS/Interfaces/IOwnedEntity.py +27 -0
  226. LOGS/Interfaces/IPaginationRequest.py +11 -0
  227. LOGS/Interfaces/IPermissionedEntity.py +72 -0
  228. LOGS/Interfaces/IProjectBased.py +27 -0
  229. LOGS/Interfaces/ISessionedEntity.py +59 -0
  230. LOGS/Interfaces/ISignableEntity.py +49 -0
  231. LOGS/Interfaces/ISoftDeletable.py +28 -0
  232. LOGS/Interfaces/ITypedEntity.py +129 -0
  233. LOGS/Interfaces/IUniqueEntity.py +61 -0
  234. LOGS/Interfaces/IVersionedEntity.py +39 -0
  235. LOGS/Interfaces/__init__.py +7 -0
  236. LOGS/LOGS.py +1436 -0
  237. LOGS/LOGSConnection.py +647 -0
  238. LOGS/LOGSOptions.py +11 -0
  239. LOGS/Parameters/Color.py +92 -0
  240. LOGS/Parameters/ParameterBase.py +55 -0
  241. LOGS/Parameters/ParameterConverter.py +24 -0
  242. LOGS/Parameters/ParameterElement.py +99 -0
  243. LOGS/Parameters/ParameterList.py +52 -0
  244. LOGS/Parameters/ParameterTable.py +64 -0
  245. LOGS/Parameters/__init__.py +13 -0
  246. LOGS/ServerMetaData.py +120 -0
  247. LOGS/__init__.py +12 -0
  248. logs_py-4.0.7.dist-info/METADATA +51 -0
  249. logs_py-4.0.7.dist-info/RECORD +251 -0
  250. logs_py-4.0.7.dist-info/WHEEL +5 -0
  251. logs_py-4.0.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,834 @@
1
+ import dataclasses
2
+ import inspect
3
+ import json
4
+ import math
5
+ from datetime import datetime, time, timedelta
6
+ from enum import Enum
7
+ from typing import (
8
+ Any,
9
+ Callable,
10
+ Dict,
11
+ List,
12
+ Optional,
13
+ Tuple,
14
+ Type,
15
+ TypeVar,
16
+ Union,
17
+ cast,
18
+ )
19
+ from uuid import UUID
20
+
21
+ import numpy as np
22
+
23
+ from LOGS.Auxiliary.Constants import Constants
24
+ from LOGS.Auxiliary.Exceptions import EntityIncompleteException
25
+ from LOGS.Auxiliary.ReplaceMessage import ReplaceMessage
26
+ from LOGS.Auxiliary.Tools import Tools
27
+
28
+ _T = TypeVar("_T")
29
+
30
+
31
+ class SerializableContent:
32
+ _indentationString: str = " "
33
+
34
+ _noSerialize: List[str] = []
35
+ _typeMapper: Optional[Dict[str, Any]] = None
36
+ _slack: Dict[str, Any] = {}
37
+ _mappingErrors: List[Dict[str, str]] = []
38
+
39
+ _planeClass: bool = False
40
+ _includeNone: bool = False
41
+ _debugPrintRef: bool = False
42
+ _includeSlack: bool = False
43
+
44
+ def __init__(self, ref=None):
45
+ if ref != None:
46
+ self._fromRef(ref=ref, selfClass=type(self))
47
+
48
+ def override(self, ref):
49
+ self._fromRef(ref=ref, selfClass=type(self))
50
+
51
+ def _fromRef(
52
+ self,
53
+ ref,
54
+ selfClass,
55
+ convertOtherType: Optional[Tuple[type, Callable[[Any], Any]]] = None,
56
+ ):
57
+ if self._debugPrintRef:
58
+ print("FromRef", selfClass.__name__, "\n", self._dictToJson(ref))
59
+
60
+ if convertOtherType and isinstance(ref, convertOtherType[0]):
61
+ ref = convertOtherType[1](ref)
62
+
63
+ serializableAncestors = tuple(
64
+ [c for c in inspect.getmro(selfClass) if issubclass(c, SerializableContent)]
65
+ )
66
+
67
+ # if isinstance(ref, selfClass):
68
+ if isinstance(ref, serializableAncestors):
69
+ self.fromInstance(ref)
70
+ elif isinstance(ref, dict):
71
+ if hasattr(self, "_typeMapper") and self._typeMapper:
72
+ for k, t in self._typeMapper.items():
73
+ if k in ref:
74
+ if isinstance(ref[k], list):
75
+ self.checkFieldAndConvertToList(
76
+ ref, fieldName=k, fieldType=t
77
+ )
78
+ else:
79
+ self.checkFieldAndConvert(
80
+ elements=ref, fieldName=k, fieldType=t, allowNone=True
81
+ )
82
+
83
+ self.fromDict(ref)
84
+ else:
85
+ if isinstance(ref, str):
86
+ try:
87
+ self.fromString(ref)
88
+ return
89
+ except NotImplementedError:
90
+ pass
91
+
92
+ types: List[type] = [dict, type(self)]
93
+ if convertOtherType:
94
+ if isinstance(convertOtherType[0], (tuple, list)):
95
+ types.extend(convertOtherType[0])
96
+ else:
97
+ types.append(convertOtherType[0])
98
+ raise Exception(
99
+ "%s instance cannot be derived from type %a. (Expected one of %s)"
100
+ % (
101
+ selfClass.__name__,
102
+ type(ref).__name__,
103
+ ", ".join([f"'{t.__name__}'" for t in types]),
104
+ )
105
+ )
106
+
107
+ def fromString(self, text: str):
108
+ raise NotImplementedError("fromString is not implemented.")
109
+
110
+ def fromInstance(self, ref):
111
+ attrList = self._getAttrList()
112
+ if "customType" in attrList:
113
+ attrList.remove(
114
+ "customType"
115
+ ) # do not copy customType in fromInstance as the customType is part of the class definition
116
+ for k in attrList:
117
+ if hasattr(ref, k):
118
+ try:
119
+ setattr(self, k, getattr(ref, k))
120
+ except AttributeError:
121
+ pass
122
+
123
+ def _getSlack(self, ignoreClasses: Optional[List[Type]] = None):
124
+ slack = {"class": type(self).__name__, "slack": {}}
125
+ if self._slack:
126
+ slack["slack"]["self"] = self._slack
127
+ attrList = self._getAttrList()
128
+ for k in attrList:
129
+ try:
130
+ if hasattr(self, k):
131
+ item = getattr(self, k)
132
+ if isinstance(item, list):
133
+ slacks = {}
134
+ for i, e in enumerate(item):
135
+ if ignoreClasses and any(
136
+ type(e) == c for c in ignoreClasses
137
+ ):
138
+ continue
139
+ if isinstance(e, SerializableContent):
140
+ s = e._getSlack(ignoreClasses=ignoreClasses)
141
+ if s["slack"]:
142
+ slacks[f"{k}[{i}]"] = s
143
+ if slacks:
144
+ slack["slack"].update(slacks)
145
+
146
+ if isinstance(item, SerializableContent):
147
+ if ignoreClasses and any(
148
+ type(item) == c for c in ignoreClasses
149
+ ):
150
+ continue
151
+ s = item._getSlack(ignoreClasses=ignoreClasses)
152
+ if s["slack"]:
153
+ slack["slack"][k] = s
154
+ except AttributeError:
155
+ pass
156
+
157
+ return slack
158
+
159
+ def _printSlackDict(self, slack: dict, prefix=""):
160
+ if not slack or "slack" not in slack or not slack["slack"]:
161
+ return
162
+
163
+ prefix += slack["class"] if prefix == "" else f"({slack['class']})"
164
+ if "self" in slack["slack"]:
165
+ print(f"{prefix}:")
166
+ for k, v in slack["slack"]["self"].items():
167
+ print(f" {prefix}.{k}: {self.truncString(str(v), length=100)}")
168
+ for k, v in slack["slack"].items():
169
+ if k == "self":
170
+ continue
171
+ self._printSlackDict(v, " " + prefix + f".{k}")
172
+
173
+ def _printSlack(self, ignoreClasses: Optional[List[Type]] = None):
174
+ self._printSlackDict(self._getSlack(ignoreClasses=ignoreClasses))
175
+
176
+ def _printMappingErrors(self):
177
+ if self._mappingErrors:
178
+ print(f"Mapping errors in {type(self).__name__}:")
179
+ for e in self._mappingErrors:
180
+ path = ".".join(e["path"]) if "path" in e else ""
181
+ print(
182
+ f" - Field '{e['field']}' with value '{e['value']}' could not be mapped: {e['error']}"
183
+ + (f" (at {path})" if path else "")
184
+ )
185
+
186
+ def fromDict(self, ref) -> None:
187
+ # print("ref", ref)
188
+ # print("ref", type(self).__name__)
189
+ if not hasattr(self, "_noSerialize"):
190
+ self._noSerialize = []
191
+
192
+ self._mappingErrors = []
193
+
194
+ mappedKey = {
195
+ k: False
196
+ for k, v in ref.items()
197
+ if v is not None and k not in self._noSerialize
198
+ }
199
+
200
+ for k in dir(self):
201
+ # print(
202
+ # f"[{type(self).__name__}] Attribute",
203
+ # k,
204
+ # k in ref and hasattr(self, k) and not callable(getattr(self, k)),
205
+ # "->",
206
+ # ref[k] if k in ref else "NULL",
207
+ # )
208
+
209
+ try:
210
+ hasAttr = hasattr(self, k)
211
+ except (
212
+ EntityIncompleteException
213
+ ): # while deserializing we want to ignore incomplete fields
214
+ hasAttr = False
215
+
216
+ if k in ref and hasAttr and not callable(getattr(self, k)):
217
+ try:
218
+ # print(" ", k, "->", ref[k])
219
+ setattr(self, k, ref[k])
220
+ mappedKey[k] = True
221
+ except AttributeError as e:
222
+ # comment in for debugging mapping errors
223
+ # print(
224
+ # f"[{type(self).__name__}] ERROR:",
225
+ # k,
226
+ # "->",
227
+ # ref[k],
228
+ # "Message:",
229
+ # e,
230
+ # )
231
+ # import traceback
232
+ # traceback.print_exc()
233
+
234
+ self._mappingErrors.append(
235
+ {
236
+ "class": type(self).__name__,
237
+ "field": k,
238
+ "value": Tools.truncString(str(ref[k]), length=100),
239
+ "error": str(e),
240
+ }
241
+ )
242
+
243
+ self._slack = {k: ref[k] for k, v in mappedKey.items() if not v}
244
+
245
+ # if self._slack:
246
+ # print(type(self).__name__, "->", ", ".join(self._slack.keys()))
247
+
248
+ @classmethod
249
+ def toBaseclassString(cls, obj):
250
+ if isinstance(obj, SerializableContent):
251
+ return obj.toString()
252
+ if isinstance(obj, dict):
253
+ return "<%s>" % obj["type"] if "type" in obj else "unknown"
254
+ return "<%s>" % type(obj).__name__
255
+
256
+ def toString(self):
257
+ return str(self)
258
+
259
+ def _typeSerializer(self, item: Any):
260
+
261
+ if isinstance(item, SerializableContent):
262
+ if self._includeSlack:
263
+ return item._toDictWithSlack()
264
+ else:
265
+ return item.toDict()
266
+ if dataclasses.is_dataclass(item):
267
+ return (dataclasses.asdict(item) if not isinstance(item, type) else None,)
268
+ elif isinstance(item, cast(Any, np.float32)):
269
+ return item.item()
270
+ elif isinstance(item, (list, np.ndarray)):
271
+ l: Any = []
272
+ for e in item:
273
+ l.append(self._serializeItem(e))
274
+ return l
275
+ elif isinstance(item, timedelta):
276
+ return item.total_seconds()
277
+ elif isinstance(item, time):
278
+ return Tools.timeToString(item)
279
+ elif isinstance(item, datetime):
280
+ return Tools.datetimeToUTCString(item)
281
+ elif isinstance(item, Enum):
282
+ return item.value
283
+ elif isinstance(item, UUID):
284
+ return str(item)
285
+ elif isinstance(item, float):
286
+ if math.isnan(item):
287
+ item = 0
288
+ elif math.isinf(item):
289
+ item = 0
290
+ return item
291
+ elif hasattr(
292
+ item, "__dict__"
293
+ ): # check if it is a defined class -> is this robust?
294
+ return None
295
+
296
+ return item
297
+
298
+ def _serializeItem(self, item):
299
+ if isinstance(item, dict):
300
+ return {i: self._serializeItem(v) for i, v in item.items()}
301
+ elif isinstance(item, list):
302
+ return [self._serializeItem(v) for v in item]
303
+ elif isinstance(item, tuple):
304
+ return [self._serializeItem(v) for v in item]
305
+
306
+ return self._typeSerializer(item)
307
+
308
+ def _getAttrList(self):
309
+ result = None
310
+ if self._planeClass:
311
+ result = [
312
+ a[0]
313
+ for a in inspect.getmembers(self, lambda a: not inspect.isroutine(a))
314
+ if a[0][0] != "_"
315
+ ]
316
+ else:
317
+ realAttributes = [
318
+ a[1:]
319
+ for a in dir(self)
320
+ if a[0] == "_" and hasattr(self, a) and not callable(getattr(self, a))
321
+ ]
322
+
323
+ result = []
324
+ for a in realAttributes:
325
+ try:
326
+ if hasattr(self, a):
327
+ result.append(a)
328
+ except (
329
+ EntityIncompleteException
330
+ ): # while serializing we want to ignore incomplete fields
331
+ pass
332
+
333
+ return result
334
+
335
+ def _toDictWithSlack(self):
336
+ tmp = self._includeSlack
337
+ self._includeSlack = True
338
+ result = self.toDict()
339
+ self._includeSlack = tmp
340
+ return result
341
+
342
+ def _truncFieldString(self, text: str, indentation: int, length: int = 100) -> str:
343
+ tab = self._indentationString * indentation
344
+
345
+ lines = [
346
+ Tools.truncString(l, length) for l in text.split("\n") if l.strip() != ""
347
+ ]
348
+ if len(lines) > 4:
349
+ lines = lines[:4]
350
+ return "\n".join(lines) + f"\n{tab}..."
351
+ else:
352
+ return "\n".join(lines)
353
+
354
+ def _serializableContentToString(self, item: Any, indentation: int, hideNone: bool):
355
+ from LOGS.Entity.EntityMinimal import EntityMinimal
356
+
357
+ if isinstance(item, (SerializableContent, EntityMinimal)):
358
+ return self._truncFieldString(
359
+ item.contentToString(indentation + 1, hideNone=hideNone),
360
+ indentation + 1,
361
+ )
362
+ elif isinstance(item, str):
363
+ return self._truncFieldString(item, indentation + 1)
364
+ else:
365
+ return Tools.truncString(str(self._typeSerializer(item)), 100)
366
+
367
+ def contentToString(self, indentation: int = 1, hideNone: bool = False) -> str:
368
+ from LOGS.Interfaces.ITypedEntity import ITypedEntity
369
+
370
+ if not hasattr(self, "_noSerialize"):
371
+ self._noSerialize = []
372
+
373
+ attrList = self._getAttrList()
374
+
375
+ # s = f"{type(self).__name__}"
376
+ s = str(self)
377
+
378
+ if isinstance(self, ITypedEntity):
379
+ if self.customType:
380
+ s += f" <{self.customType.name}>"
381
+ if "customType" in attrList:
382
+ attrList.remove("customType")
383
+ if "customValues" in attrList:
384
+ attrList.remove("customValues")
385
+
386
+ s += "\n"
387
+
388
+ tab = self._indentationString * indentation
389
+
390
+ v: Optional[str] = None
391
+ for k in attrList:
392
+ if k in self._noSerialize:
393
+ continue
394
+ # s += f"{tab}{k}:\n"
395
+ a = getattr(self, k)
396
+
397
+ if a is None and hideNone: # skip None values if hiding is enabled
398
+ continue
399
+
400
+ if isinstance(a, list):
401
+ if len(a) == 0:
402
+ if not hideNone:
403
+ s += f"{tab}{k} = []\n"
404
+ continue
405
+ l = []
406
+ for i, e in enumerate(a):
407
+ l.append(
408
+ f"{tab}{self._indentationString }{self._serializableContentToString(e, indentation + 1, hideNone)}",
409
+ )
410
+ if i > 3:
411
+ break
412
+
413
+ sub = ",\n".join(l)
414
+ s += f"{tab}{k} = [\n{sub}\n{tab}]\n"
415
+ continue
416
+
417
+ if isinstance(a, dict):
418
+ l = []
419
+ for i, e in enumerate(a.keys()):
420
+ l.append(
421
+ f"{tab}{self._indentationString }{self._serializableContentToString(e, indentation + 1, hideNone)}",
422
+ )
423
+ if i > 3:
424
+ break
425
+
426
+ sub = ",\n".join(l)
427
+ s += f"{tab}{k} = {{\n{sub}\n{tab}}}\n"
428
+ continue
429
+
430
+ v = self._serializableContentToString(a, indentation, hideNone)
431
+ s += f"{tab}{k} = {v}\n"
432
+
433
+ if isinstance(self, ITypedEntity) and self.customValues:
434
+ content = self.customValues._contentToString(
435
+ indentation + 1, hideNone=hideNone
436
+ )
437
+ if content or not hideNone:
438
+ s += f"{tab}customValues =\n{content}"
439
+
440
+ return s
441
+
442
+ def printContent(self, hideNone: bool = False) -> None:
443
+ print(self.contentToString(hideNone=hideNone))
444
+
445
+ def toDict(self) -> Dict[str, Any]:
446
+ # print("toDict", type(self).__name__)
447
+ if not hasattr(self, "_noSerialize"):
448
+ self._noSerialize = []
449
+ d = {}
450
+
451
+ # customFields = self._getICustomValueFields()
452
+
453
+ attrList = self._getAttrList()
454
+
455
+ for k in attrList:
456
+ if k in self._noSerialize:
457
+ continue
458
+
459
+ a = getattr(self, k)
460
+
461
+ if a != None or self._includeNone:
462
+ d[k] = self._serializeItem(a)
463
+
464
+ if self._includeSlack:
465
+ d.update({k: v for k, v in self._slack.items() if k not in d})
466
+
467
+ return d
468
+
469
+ @classmethod
470
+ def _objectDictToDict(cls, item):
471
+ # print(" item", item)
472
+ if isinstance(item, dict):
473
+ d = {}
474
+ for k, v in item.items():
475
+ # item[k] = cls._objectDictToDict(v)
476
+ d[k] = cls._objectDictToDict(v)
477
+ return d
478
+ if isinstance(item, list):
479
+ l = []
480
+ for v in item:
481
+ # item[k] = cls._objectDictToDict(v)
482
+ l.append(cls._objectDictToDict(v))
483
+ return l
484
+ else:
485
+ if hasattr(item, "toDict") and callable(getattr(item, "toDict")):
486
+ return item.toDict()
487
+
488
+ return item
489
+
490
+ @classmethod
491
+ def _dictToJson(cls, dict, indent=2, sort_keys=True, compact=False):
492
+ separators = None
493
+ if compact:
494
+ indent = None
495
+ separators = (",", ":")
496
+ return json.dumps(
497
+ dict, indent=indent, sort_keys=sort_keys, separators=separators
498
+ )
499
+
500
+ def toJson(self, indent=2, sort_keys=True, compact=False, validate=False):
501
+ return self._dictToJson(
502
+ self.toDict(), indent=indent, sort_keys=sort_keys, compact=compact
503
+ )
504
+
505
+ def printJson(self, indent=2, sort_keys=True, compact=False, validate=False):
506
+ print(
507
+ self.toJson(
508
+ validate=validate, indent=indent, sort_keys=sort_keys, compact=compact
509
+ )
510
+ )
511
+
512
+ @classmethod
513
+ def truncString(cls, text: str, length: int = 30) -> str:
514
+ return Tools.truncString(text=str(text), length=length)
515
+
516
+ @staticmethod
517
+ def delEntryFromDict(d: dict, entry: str):
518
+ if entry in d:
519
+ del d[entry]
520
+
521
+ def dictToString(self, element, length=30):
522
+ text = ""
523
+ if "name" in element:
524
+ text = "Name: " + str(element["name"])
525
+
526
+ return self.truncString(text, length=length)
527
+
528
+ @classmethod
529
+ def plural(cls, word, count):
530
+ if isinstance(count, list):
531
+ count = len(count)
532
+
533
+ if word[-1] == "y" and count > 1:
534
+ word = word[:-1] + "ie"
535
+
536
+ return word + ("s" if count > 1 else "")
537
+
538
+ def createOnDemand(self, attr: str, typeOfValue: type):
539
+ if getattr(self, attr) == None:
540
+ setattr(self, attr, typeOfValue())
541
+
542
+ return getattr(self, attr)
543
+
544
+ def checkAndConvertNullable(
545
+ self,
546
+ value: Any,
547
+ fieldType: Union[Type[_T], List[Type[_T]]],
548
+ fieldName: Optional[str] = None,
549
+ converter: Optional[Callable[[Any], _T]] = None,
550
+ ):
551
+ return self.checkAndConvert(
552
+ value=value,
553
+ fieldType=fieldType,
554
+ fieldName=fieldName,
555
+ converter=converter,
556
+ allowNone=True,
557
+ )
558
+
559
+ def checkAndConvert(
560
+ self,
561
+ value: Any,
562
+ fieldType: Union[Type[_T], List[Type[_T]]],
563
+ fieldName: Optional[str] = None,
564
+ converter: Optional[Callable[[Any], _T]] = None,
565
+ allowNone=False,
566
+ ) -> _T:
567
+ if (
568
+ inspect.isclass(fieldType)
569
+ and issubclass(fieldType, SerializableContent)
570
+ and isinstance(value, dict)
571
+ and not dataclasses.is_dataclass(fieldType)
572
+ ):
573
+ return cast(Any, fieldType)(ref=value)
574
+ return cast(
575
+ _T,
576
+ Tools.checkAndConvert(
577
+ value=value,
578
+ fieldType=cast(Any, fieldType),
579
+ fieldName=fieldName,
580
+ converter=converter,
581
+ allowNone=allowNone,
582
+ ),
583
+ )
584
+
585
+ def checkFieldAndConvert(
586
+ self,
587
+ elements: Dict[str, Any],
588
+ fieldType: Union[Type[_T], List[Type[_T]]],
589
+ fieldName: Optional[str] = None,
590
+ converter: Optional[Callable[[Any], _T]] = None,
591
+ allowNone=False,
592
+ ):
593
+ if fieldName in elements:
594
+ elements[fieldName] = self.checkAndConvert(
595
+ elements[fieldName],
596
+ fieldType=fieldType,
597
+ fieldName=fieldName,
598
+ converter=converter,
599
+ allowNone=allowNone,
600
+ )
601
+
602
+ def checkFieldAndConvertToList(
603
+ self,
604
+ elements: Dict[str, Any],
605
+ fieldType: Type[_T],
606
+ fieldName: Optional[str] = None,
607
+ converter: Optional[Callable[[Any], _T]] = None,
608
+ allowNone: bool = False,
609
+ singleToList: bool = False,
610
+ length: int = -1,
611
+ ):
612
+ if fieldName in elements:
613
+ elements[fieldName] = self.checkListAndConvert(
614
+ elements[fieldName],
615
+ fieldType=fieldType,
616
+ fieldName=fieldName,
617
+ converter=converter,
618
+ allowNone=allowNone,
619
+ singleToList=singleToList,
620
+ length=length,
621
+ )
622
+
623
+ def checkListAndConvertNullable(
624
+ self,
625
+ value: Any,
626
+ fieldType: Type[_T],
627
+ fieldName: Optional[str] = None,
628
+ converter: Optional[Callable[[Any], _T]] = None,
629
+ length: int = -1,
630
+ ):
631
+ return self.checkListAndConvert(
632
+ value=value,
633
+ fieldType=fieldType,
634
+ fieldName=fieldName,
635
+ converter=converter,
636
+ length=length,
637
+ singleToList=True,
638
+ allowNone=True,
639
+ )
640
+
641
+ def checkListAndConvert(
642
+ self,
643
+ value: Any,
644
+ fieldType: Type[_T],
645
+ fieldName: Optional[str] = None,
646
+ converter: Optional[Callable[[Any], _T]] = None,
647
+ allowNone: bool = False,
648
+ singleToList: bool = False,
649
+ length: int = -1,
650
+ ) -> List[_T]:
651
+ if (
652
+ inspect.isclass(fieldType)
653
+ and issubclass(fieldType, SerializableContent)
654
+ and not dataclasses.is_dataclass(fieldType)
655
+ ):
656
+ if isinstance(value, tuple):
657
+ value = list(value)
658
+ if isinstance(value, list):
659
+ for i in range(0, len(value)):
660
+ if isinstance(value[i], dict):
661
+ value[i] = cast(Any, fieldType)(ref=value[i])
662
+
663
+ return Tools.checkListAndConvert(
664
+ value=value,
665
+ fieldType=fieldType,
666
+ fieldName=fieldName,
667
+ converter=converter,
668
+ allowNone=allowNone,
669
+ singleToList=singleToList,
670
+ length=length,
671
+ )
672
+
673
+ def checkAndAppend(
674
+ self,
675
+ value: Any,
676
+ fieldType: Type[_T],
677
+ fieldName: Optional[str] = None,
678
+ converter: Optional[Callable[[Any], _T]] = None,
679
+ allowNone: bool = False,
680
+ ):
681
+ if not converter:
682
+ converter = fieldType
683
+
684
+ if value == None:
685
+ value = []
686
+
687
+ if fieldName == None:
688
+ fieldName = "field"
689
+ fieldName = cast(str, fieldName)
690
+
691
+ if not isinstance(value, list):
692
+ raise Exception(
693
+ "%s must be of type 'list'. (Got type %a)"
694
+ % (fieldName.capitalize(), type(value).__name__)
695
+ )
696
+
697
+ return [
698
+ self.checkAndConvert(
699
+ f,
700
+ fieldName=fieldName,
701
+ fieldType=fieldType,
702
+ converter=converter,
703
+ allowNone=allowNone,
704
+ )
705
+ for f in value
706
+ ]
707
+
708
+ def checkInstance(self, instance, path: List[str] = []):
709
+ # print("check", path, instance)
710
+ if isinstance(instance, SerializableContent):
711
+ instance.check(path)
712
+
713
+ def checkProperty(self, property: str, path: List[str] = []):
714
+ if hasattr(self, property):
715
+ item = getattr(self, property)
716
+ if isinstance(item, list):
717
+ for k, v in enumerate(item):
718
+ self.checkInstance(v, path + [str(self), "%s[%d]" % (property, k)])
719
+ elif isinstance(item, dict):
720
+ for k, v in item.items():
721
+ self.checkInstance(v, path + [str(self), "%s[%s]" % (property, k)])
722
+ else:
723
+ self.checkInstance(item, path + [str(self), "%s" % (property)])
724
+
725
+ def check(self, path: List[str] = []):
726
+ for k in dir(self):
727
+ if hasattr(self, k) and not callable(getattr(self, k)) and k[0] != "_":
728
+ self.checkProperty(k, path)
729
+
730
+ def __str__(self):
731
+ return "<%s>" % (type(self).__name__)
732
+
733
+ @classmethod
734
+ def replaceControlCharacters(
735
+ cls,
736
+ text: Optional[Union[str, Dict[str, str], List[str]]],
737
+ excludeCharacters: List[str] = [],
738
+ mergeMessages: bool = False,
739
+ excludeKeys: List[str] = [],
740
+ ):
741
+ if text is None:
742
+ return "", [ReplaceMessage(message="No text defined")]
743
+
744
+ messages: List[ReplaceMessage] = []
745
+
746
+ if isinstance(text, dict):
747
+ if (
748
+ "type" in text
749
+ and text["type"] == "parameter"
750
+ and "multiline" in text
751
+ and text["multiline"]
752
+ ):
753
+ excludeCharacters.append("line feed")
754
+ for k in text.keys():
755
+ if k not in excludeKeys:
756
+ text[k], messages = cast(
757
+ Any,
758
+ cls.replaceControlCharacters(
759
+ text[k],
760
+ excludeKeys=excludeKeys,
761
+ excludeCharacters=excludeCharacters,
762
+ mergeMessages=mergeMessages,
763
+ ),
764
+ )
765
+ if messages:
766
+ for m in messages:
767
+ m.unshiftPath(k)
768
+ elif isinstance(text, list):
769
+ for i in range(0, len(text)):
770
+ s, messages = cls.replaceControlCharacters(
771
+ text[i],
772
+ excludeKeys=excludeKeys,
773
+ excludeCharacters=excludeCharacters,
774
+ mergeMessages=mergeMessages,
775
+ )
776
+ text[i] = str(s)
777
+ if messages:
778
+ for m in messages:
779
+ m.unshiftPath(i)
780
+ elif isinstance(text, str):
781
+ for k in Constants._control_character:
782
+ if Constants._control_character[k][1] in excludeCharacters:
783
+ continue
784
+ l = len(text)
785
+ text = text.replace(Constants._control_character[k][0], "")
786
+ if len(text) != l:
787
+ if mergeMessages:
788
+ messages.append(
789
+ ReplaceMessage("'%s'" % Constants._control_character[k][1])
790
+ )
791
+ else:
792
+ messages.append(
793
+ ReplaceMessage(
794
+ "contained special character '%s'"
795
+ % Constants._control_character[k][1]
796
+ )
797
+ )
798
+ elif isinstance(text, float):
799
+ if math.isnan(text):
800
+ text = None
801
+ messages.append(
802
+ ReplaceMessage(message="contained a float with 'NaN' value")
803
+ )
804
+
805
+ if mergeMessages and messages:
806
+ if len(messages) > 1:
807
+ messages = [
808
+ ReplaceMessage(
809
+ message="contained special characters "
810
+ + ", ".join(m.message for m in messages)
811
+ )
812
+ ]
813
+ else:
814
+ messages = [
815
+ ReplaceMessage(
816
+ message="contained special character " + messages[0].message
817
+ )
818
+ ]
819
+ # for idx in range(0, len(messages)):
820
+ # msg = messages[idx]
821
+ # i = msg.index(0)
822
+ # if i > 0:
823
+ # m = ".".join(msg[0:i]) + ": " + " ".join(msg[i + 1 :])
824
+ # messages[idx] = m
825
+
826
+ return text, messages
827
+
828
+ @classmethod
829
+ def generateID(cls, len=10):
830
+ return Tools.generateRandomString(len=len)
831
+
832
+
833
+ class SerializableClass(SerializableContent):
834
+ _planeClass = True