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,99 @@
1
+ import os
2
+ from typing import TYPE_CHECKING, Union
3
+
4
+ if TYPE_CHECKING:
5
+ from LOGS.Entities.Attachment import Attachment
6
+ from LOGS.Entities.Bridge import Bridge
7
+ from LOGS.Entities.CustomField import CustomField
8
+ from LOGS.Entities.CustomType import CustomType
9
+ from LOGS.Entities.DataFormat import DataFormat
10
+ from LOGS.Entities.DataFormatInstrument import DataFormatInstrument
11
+ from LOGS.Entities.Dataset import Dataset
12
+ from LOGS.Entities.DatasetTemplate import DatasetTemplate
13
+ from LOGS.Entities.DataSource import DataSource
14
+ from LOGS.Entities.FileEntry import FileEntry
15
+ from LOGS.Entities.InventoryItem import InventoryItem
16
+ from LOGS.Entities.LabNotebook import LabNotebook
17
+ from LOGS.Entities.LabNotebookEntry import LabNotebookEntry
18
+ from LOGS.Entities.LabNotebookExperiment import LabNotebookExperiment
19
+ from LOGS.Entities.LabNotebookTemplate import LabNotebookTemplate
20
+ from LOGS.Entities.Method import Method
21
+ from LOGS.Entities.Origin import Origin
22
+ from LOGS.Entities.Person import Person
23
+ from LOGS.Entities.Project import Project
24
+ from LOGS.Entities.Role import Role
25
+ from LOGS.Entities.Sample import Sample
26
+ from LOGS.Entities.Vendor import Vendor
27
+
28
+
29
+ class Constants:
30
+ _control_character = {
31
+ "0": ("\u0000", "null"),
32
+ "1": ("\u0001", "start of heading"),
33
+ "2": ("\u0002", "start of text"),
34
+ "3": ("\u0003", "end of text"),
35
+ "4": ("\u0004", "end of transmission"),
36
+ "5": ("\u0005", "enquiry"),
37
+ "6": ("\u0006", "acknowledge"),
38
+ "7": ("\u0007", "bell"),
39
+ "8": ("\u0008", "backspace"),
40
+ "9": ("\u0009", "horizontal tabulation"),
41
+ "a": ("\u000a", "line feed"),
42
+ "b": ("\u000b", "vertical tabulation"),
43
+ "c": ("\u000c", "form feed"),
44
+ "d": ("\u000d", "carriage return"),
45
+ "e": ("\u000e", "shift out"),
46
+ "f": ("\u000f", "shift in"),
47
+ "10": ("\u0010", "data link escape"),
48
+ "11": ("\u0011", "device control one"),
49
+ "12": ("\u0012", "device control two"),
50
+ "13": ("\u0013", "device control three"),
51
+ "14": ("\u0014", "device control four"),
52
+ "15": ("\u0015", "negative acknowledge"),
53
+ "16": ("\u0016", "synchronous idle"),
54
+ "17": ("\u0017", "end of transmission block"),
55
+ "18": ("\u0018", "cancel"),
56
+ "19": ("\u0019", "end of medium"),
57
+ "1a": ("\u001a", "substitute"),
58
+ "1b": ("\u001b", "escape"),
59
+ "1c": ("\u001c", "file separator"),
60
+ "1d": ("\u001d", "group separator"),
61
+ "1e": ("\u001e", "record separator"),
62
+ "1f": ("\u001f", "unit separator"),
63
+ }
64
+
65
+ byteUnits = ["", "K", "M", "G", "T", "P", "E", "Z"]
66
+
67
+ exceptionIndentation = " " * 2
68
+
69
+ # Typing aliases constants
70
+ ID_TYPE = Union[int, str]
71
+
72
+ FILE_TYPE = Union[str, os.DirEntry, "FileEntry"]
73
+
74
+ TYPED_ENTITIES = Union[
75
+ "Sample", "Dataset", "InventoryItem", "DatasetTemplate", "Person", "Project"
76
+ ]
77
+
78
+ ENTITIES = Union[
79
+ "Bridge",
80
+ "CustomField",
81
+ "CustomType",
82
+ "Dataset",
83
+ "DataSource",
84
+ "InventoryItem",
85
+ "LabNotebook",
86
+ "LabNotebookEntry",
87
+ "LabNotebookExperiment",
88
+ "LabNotebookTemplate",
89
+ "Origin",
90
+ "Person",
91
+ "Project",
92
+ "Sample",
93
+ "DataFormat",
94
+ "Role",
95
+ "Vendor",
96
+ "DataFormatInstrument",
97
+ "Method",
98
+ "Attachment",
99
+ ]
@@ -0,0 +1,254 @@
1
+ import base64
2
+ from typing import (
3
+ TYPE_CHECKING,
4
+ Any,
5
+ Dict,
6
+ Generic,
7
+ Optional,
8
+ Type,
9
+ TypeVar,
10
+ Union,
11
+ cast,
12
+ )
13
+
14
+ from LOGS.Auxiliary.CheckClassName import CheckClassName
15
+ from LOGS.Auxiliary.Constants import Constants
16
+ from LOGS.Auxiliary.Exceptions import TypedEntityNotConnectedException
17
+ from LOGS.Auxiliary.Tools import Tools
18
+ from LOGS.Entities.CustomFieldModels import CustomTypeEntityTypeMapper
19
+ from LOGS.Entity import Entity
20
+ from LOGS.Interfaces.ITypedEntity import ITypedEntity
21
+
22
+ if TYPE_CHECKING:
23
+ from LOGS.Entities.CustomType import CustomType
24
+ from LOGS.LOGSConnection import LOGSConnection
25
+
26
+
27
+ _T = TypeVar("_T", bound=Constants.TYPED_ENTITIES)
28
+
29
+
30
+ class CustomEntityClassGenerator(Generic[_T]):
31
+
32
+ _customTypeCache: Dict[str, "CustomType"] = {}
33
+ _classCache: Dict[str, Type[_T]] = {}
34
+
35
+ @classmethod
36
+ def generate(
37
+ cls,
38
+ customType: Optional["CustomType"],
39
+ connection: "LOGSConnection",
40
+ fieldName: Optional[str] = None,
41
+ limitToEntityType: Optional[Type[_T]] = None,
42
+ ) -> Type[_T]:
43
+ if customType is None:
44
+ if not limitToEntityType:
45
+ raise ValueError(
46
+ "When 'customType' is None, 'limitToEntityType' must be provided."
47
+ )
48
+
49
+ if not isinstance(limitToEntityType, type) or not issubclass(
50
+ limitToEntityType, Entity
51
+ ):
52
+ raise ValueError("'limitToEntityType' must be an entity class.")
53
+
54
+ return limitToEntityType
55
+ else:
56
+ if not customType or not customType.id:
57
+ raise ValueError(f"Custom type is missing for field '{fieldName}'.")
58
+
59
+ cacheId = cls._getCacheKey(customType.id, connection)
60
+ if cacheId in cls._classCache:
61
+ return cls._classCache[cacheId]
62
+
63
+ if not customType.entityType:
64
+ raise ValueError(
65
+ f"Custom type '{customType.name}' (ID: {customType.id}) has no entity type defined."
66
+ )
67
+
68
+ entityType = CustomTypeEntityTypeMapper.getClass(customType.entityType)
69
+
70
+ if limitToEntityType and limitToEntityType != entityType:
71
+ raise Exception(
72
+ f"The custom type '{customType.name}' is not valid for entity type '{limitToEntityType.__name__}'. (Expected entity type '{entityType.__name__}')"
73
+ )
74
+
75
+ name = (
76
+ entityType.__name__
77
+ + "_"
78
+ + (
79
+ CheckClassName.sanitizeClassName(customType.name)
80
+ if customType.name
81
+ else f"CustomType_ID{customType.id}"
82
+ )
83
+ )
84
+
85
+ doc = f"This class represents a LOGS {entityType.__name__} of custom type '{customType.name}' (TypeID: {customType.id})"
86
+
87
+ module = ".".join(
88
+ entityType.__module__.split(".")[:-1]
89
+ + [f"Custom{Tools.wordToPlural(entityType.__name__)}"],
90
+ )
91
+
92
+ def __init__(
93
+ self,
94
+ ref=None,
95
+ id: Optional[int] = None,
96
+ connection: Optional["LOGSConnection"] = connection,
97
+ *args,
98
+ **kwargs,
99
+ ):
100
+ cast(Any, type(self).__bases__[0]).__init__(
101
+ self,
102
+ ref=ref,
103
+ id=id,
104
+ connection=connection,
105
+ *args,
106
+ **kwargs,
107
+ )
108
+
109
+ typeDict = {
110
+ "__init__": __init__,
111
+ "__module__": module,
112
+ "__doc__": doc,
113
+ "_customType": customType,
114
+ "_baseType": entityType,
115
+ }
116
+
117
+ newClass = cast(
118
+ Type[_T],
119
+ type(
120
+ name,
121
+ (entityType,),
122
+ typeDict,
123
+ ),
124
+ )
125
+
126
+ return newClass
127
+
128
+ @classmethod
129
+ def _getCacheKey(
130
+ cls,
131
+ customTypeOrId: int,
132
+ connection: "LOGSConnection",
133
+ ) -> str:
134
+ url = connection.getEndpointUrl(
135
+ ["custom_type_entities"] + [str(customTypeOrId)],
136
+ )
137
+ return base64.b64encode(url.encode()).decode()
138
+
139
+ @classmethod
140
+ def fetchCustomType(
141
+ cls, customTypeOrId: Union["CustomType", int], connection: "LOGSConnection"
142
+ ):
143
+ from LOGS.Entities.CustomType import CustomType
144
+
145
+ if isinstance(customTypeOrId, CustomType):
146
+ return customTypeOrId
147
+
148
+ if isinstance(customTypeOrId, int):
149
+ cacheId = cls._getCacheKey(customTypeOrId, connection)
150
+ if cacheId in cls._customTypeCache:
151
+ return cls._customTypeCache[cacheId]
152
+
153
+ customType = CustomType(id=customTypeOrId, connection=connection)
154
+ customType.fetch()
155
+
156
+ cls._customTypeCache[cacheId] = customType
157
+ return customType
158
+
159
+ raise ValueError(
160
+ f"Parameter '{customTypeOrId}' must be either a {CustomType.__name__} instance or an integer ID."
161
+ )
162
+
163
+ @classmethod
164
+ def convert(
165
+ cls,
166
+ value: Any,
167
+ customTypeOrId: Union["CustomType", int],
168
+ connection: "LOGSConnection",
169
+ fieldName: Optional[str] = None,
170
+ limitToEntityType: Optional[Type[_T]] = None,
171
+ ) -> _T:
172
+
173
+ if isinstance(value, Entity):
174
+ return cast(_T, value)
175
+
176
+ customType = cls.fetchCustomType(customTypeOrId, connection)
177
+
178
+ c = cls.generate(customType, connection, fieldName, limitToEntityType)
179
+ try:
180
+ return c(value)
181
+ except Exception as e:
182
+ raise ValueError(
183
+ f"Field '{fieldName}' cannot be converted to '{c.__name__}': {str(e)}"
184
+ ) from e
185
+
186
+ @classmethod
187
+ def convertToUntyped(cls, entity: _T, fieldName: Optional[str] = None) -> _T:
188
+ if not isinstance(entity, ITypedEntity) or not entity._baseType:
189
+ return entity
190
+
191
+ if not entity._connection:
192
+ raise TypedEntityNotConnectedException(entity)
193
+
194
+ customType = entity.customType
195
+ customValues = entity.customValues
196
+
197
+ entity._customType = None
198
+ entity._customValues = None
199
+
200
+ c = cls.generate(
201
+ None,
202
+ connection=entity._connection,
203
+ fieldName=fieldName,
204
+ limitToEntityType=cast(_T, entity._baseType),
205
+ )(entity, connection=entity._getConnection())
206
+
207
+ entity._customType = customType
208
+ entity._customValues = customValues
209
+
210
+ c._untypedCustomType = customType.toDict() if customType else None
211
+ c._untypedValues = cast(Any, customValues.toDict()) if customValues else None
212
+ return c
213
+
214
+ @classmethod
215
+ def convertFromUntyped(
216
+ cls,
217
+ entity: _T,
218
+ fieldName: Optional[str] = None,
219
+ limitToEntityType: Optional[Type[_T]] = None,
220
+ ) -> _T:
221
+ if not isinstance(entity, ITypedEntity) or not entity._untypedCustomType:
222
+ return entity
223
+
224
+ if (
225
+ not isinstance(entity._untypedCustomType, dict)
226
+ or "id" not in entity._untypedCustomType
227
+ ):
228
+ raise Exception(f"Custom type of {entity.identifier} is invalid.")
229
+
230
+ if not entity._connection:
231
+ raise TypedEntityNotConnectedException(entity)
232
+
233
+ customTypeId = entity._untypedCustomType["id"]
234
+ entity._untypedCustomType = None
235
+ values = entity._untypedValues
236
+ entity._untypedValues = None
237
+
238
+ customType = cls.fetchCustomType(customTypeId, entity._connection)
239
+
240
+ c = cls.generate(
241
+ customType,
242
+ connection=entity._connection,
243
+ fieldName=fieldName,
244
+ limitToEntityType=limitToEntityType,
245
+ )
246
+
247
+ try:
248
+ e = c(entity, connection=entity._getConnection())
249
+ except Exception as e:
250
+ raise ValueError(
251
+ f"Field '{fieldName}' cannot be converted to '{c.__name__}': {str(e)}"
252
+ ) from e
253
+ e.customValues = values
254
+ return e
@@ -0,0 +1,115 @@
1
+ from base64 import b64encode
2
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Type, cast
3
+
4
+ from LOGS.Auxiliary.CheckClassName import CheckClassName
5
+ from LOGS.Auxiliary.Constants import Constants
6
+ from LOGS.Auxiliary.Exceptions import EntityFetchingException
7
+
8
+ if TYPE_CHECKING:
9
+ from LOGS.Interfaces.ICustomFieldValue import ICustomFieldValue
10
+ from LOGS.LOGSConnection import LOGSConnection
11
+
12
+
13
+ class CustomFieldClassGenerator:
14
+
15
+ _classCache: Dict[str, Type["ICustomFieldValue"]] = {}
16
+
17
+ @classmethod
18
+ def _getCacheKey(cls, customTypeField: int, connection: "LOGSConnection") -> str:
19
+ url = connection.getEndpointUrl(["custom_type_fields", customTypeField])
20
+ return b64encode(url.encode()).decode()
21
+
22
+ @classmethod
23
+ def generate(
24
+ cls,
25
+ customFieldId: int,
26
+ connection: "LOGSConnection",
27
+ fieldName: Optional[str] = None,
28
+ ) -> Type["ICustomFieldValue"]:
29
+ from LOGS.Entities.CustomField import CustomField
30
+ from LOGS.Interfaces.ICustomFieldValue import ICustomFieldValue
31
+
32
+ cacheId = cls._getCacheKey(customFieldId, connection)
33
+ if cacheId in cls._classCache:
34
+ return cls._classCache[cacheId]
35
+
36
+ customField = CustomField(id=customFieldId, connection=connection)
37
+ try:
38
+ customField.fetch()
39
+ except EntityFetchingException as e:
40
+ raise ValueError(
41
+ f"Custom field with ID {customFieldId} specified in '{fieldName}' not found:"
42
+ + f"\n{Constants.exceptionIndentation}{str(e)}"
43
+ )
44
+
45
+ if cacheId in cls._classCache:
46
+ return cls._classCache[cacheId]
47
+
48
+ name = (
49
+ CheckClassName.sanitizeClassName(customField.name)
50
+ if customField.name
51
+ else f"CustomField_ID{customField.id}"
52
+ )
53
+
54
+ def __init__(self, ref=None):
55
+ cast(Any, type(self).__bases__[0]).__init__(self, ref, customField.dataType)
56
+
57
+ doc = (
58
+ f"This class represents the value of the LOGS custom field '{customField.name}' (ID: {customField.id})"
59
+ + ("\n" + customField.description if customField.description else "")
60
+ )
61
+
62
+ module = ".".join(
63
+ ICustomFieldValue.__module__.split(".")[:-1]
64
+ + ["GeneratedCustomFieldValues"],
65
+ )
66
+
67
+ newClass: Type[ICustomFieldValue] = type(
68
+ name,
69
+ (ICustomFieldValue,),
70
+ {
71
+ "__init__": __init__,
72
+ "_dataType": customField.dataType,
73
+ "__module__": module,
74
+ "__doc__": doc,
75
+ "_name": customField.name,
76
+ "_id": customField.id,
77
+ "_connection": connection,
78
+ "_value": (
79
+ customField.defaultValues
80
+ if customField.defaultValues is not None
81
+ else None
82
+ ),
83
+ },
84
+ )
85
+ cls._classCache[cacheId] = newClass
86
+
87
+ return newClass
88
+
89
+ @classmethod
90
+ def convert(
91
+ cls,
92
+ value: Any,
93
+ customFieldId: int,
94
+ connection: "LOGSConnection",
95
+ fieldName: Optional[str] = None,
96
+ ) -> Optional["ICustomFieldValue"]:
97
+ from LOGS.Interfaces.ICustomFieldValue import ICustomFieldValue
98
+
99
+ if isinstance(value, ICustomFieldValue):
100
+ return value
101
+
102
+ if not isinstance(value, dict):
103
+ raise ValueError(
104
+ f"Field '{fieldName}' cannot be converted from value of type '{type(value).__name__}'."
105
+ )
106
+
107
+ c = cls.generate(
108
+ customFieldId=customFieldId, fieldName=fieldName, connection=connection
109
+ )
110
+ try:
111
+ return c(value)
112
+ except Exception as e:
113
+ raise ValueError(
114
+ f"Field '{fieldName}' cannot be converted to '{c.__name__}': {str(e)}"
115
+ ) from e
@@ -0,0 +1,168 @@
1
+ from datetime import date, datetime, time, timedelta
2
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union
3
+
4
+ from LOGS.Auxiliary.MinimalModelGenerator import MinimalModelGenerator
5
+ from LOGS.Auxiliary.Tools import Tools
6
+ from LOGS.Converter.DateTimeRange import DateTimeRange
7
+ from LOGS.Entities.CustomFieldModels import CustomFieldDataType
8
+
9
+ if TYPE_CHECKING:
10
+ from LOGS.LOGSConnection import LOGSConnection
11
+
12
+
13
+ class CustomFieldValueTypeChecker:
14
+
15
+ _customFieldTypeMap: Dict[CustomFieldDataType, Union[type, str, Callable]] = {
16
+ CustomFieldDataType.String: str,
17
+ CustomFieldDataType.StringArray: str,
18
+ CustomFieldDataType.Integer: int,
19
+ CustomFieldDataType.IntegerArray: int,
20
+ CustomFieldDataType.Float: float,
21
+ CustomFieldDataType.FloatArray: float,
22
+ CustomFieldDataType.Boolean: bool,
23
+ CustomFieldDataType.Date: date,
24
+ CustomFieldDataType.DateArray: date,
25
+ CustomFieldDataType.DateTime: datetime,
26
+ CustomFieldDataType.DateTimeArray: datetime,
27
+ CustomFieldDataType.Time: time,
28
+ CustomFieldDataType.TimeArray: time,
29
+ CustomFieldDataType.DateTimeRange: DateTimeRange,
30
+ CustomFieldDataType.TimeRange: lambda value: (
31
+ timedelta(seconds=value) if isinstance(value, (int, float)) else value
32
+ ),
33
+ CustomFieldDataType.Dataset: "DatasetMinimal",
34
+ CustomFieldDataType.DatasetArray: "DatasetMinimal",
35
+ CustomFieldDataType.Sample: "SampleMinimal",
36
+ CustomFieldDataType.SampleArray: "SampleMinimal",
37
+ CustomFieldDataType.Project: "ProjectMinimal",
38
+ CustomFieldDataType.ProjectArray: "ProjectMinimal",
39
+ CustomFieldDataType.Person: "PersonMinimal",
40
+ CustomFieldDataType.PersonArray: "PersonMinimal",
41
+ CustomFieldDataType.Method: "MethodMinimal",
42
+ CustomFieldDataType.MethodArray: "MethodMinimal",
43
+ CustomFieldDataType.SharedContent: "SharedContentMinimal",
44
+ CustomFieldDataType.SharedContentArray: "SharedContentMinimal",
45
+ CustomFieldDataType.LabNotebook: "LabNotebookMinimal",
46
+ CustomFieldDataType.LabNotebookArray: "LabNotebookMinimal",
47
+ CustomFieldDataType.LabNotebookExperiment: "LabNotebookExperimentMinimal",
48
+ CustomFieldDataType.LabNotebookExperimentArray: "LabNotebookExperimentMinimal",
49
+ CustomFieldDataType.LabNotebookEntry: "LabNotebookEntryMinimal",
50
+ CustomFieldDataType.LabNotebookEntryArray: "LabNotebookEntryMinimal",
51
+ CustomFieldDataType.Attachment: "AttachmentMinimal",
52
+ CustomFieldDataType.InventoryItem: "InventoryItemMinimal",
53
+ CustomFieldDataType.InventoryItemArray: "InventoryItemMinimal",
54
+ CustomFieldDataType.Barcode: str,
55
+ CustomFieldDataType.Url: str,
56
+ CustomFieldDataType.UrlArray: str,
57
+ }
58
+
59
+ _converter: Callable[[Any, str], Any]
60
+ _isArrayType: bool
61
+ _enumOptions: Any = None
62
+
63
+ @classmethod
64
+ def _arrayTypeCheck(cls, data_type: CustomFieldDataType) -> bool:
65
+ return data_type.name.endswith("Array")
66
+
67
+ @classmethod
68
+ def _generateConverter(
69
+ cls,
70
+ dataType: CustomFieldDataType,
71
+ isArrayType: bool,
72
+ connection: Optional["LOGSConnection"],
73
+ ) -> Callable[[Any, str], Any]:
74
+ dataClass = cls._customFieldTypeMap.get(dataType, None)
75
+
76
+ if dataClass is None:
77
+ raise ValueError(f"Unknown custom field type: '{dataType}'")
78
+
79
+ if isinstance(dataClass, str):
80
+ if isArrayType:
81
+ return lambda value, fieldName: MinimalModelGenerator.MinimalFromList(
82
+ value, dataClass, fieldName, connection
83
+ )
84
+ else:
85
+ return lambda value, fieldName: MinimalModelGenerator.MinimalFromSingle(
86
+ value, dataClass, fieldName, connection
87
+ )
88
+ elif isinstance(dataClass, type):
89
+ if isArrayType:
90
+ return lambda value, fieldName: Tools.checkListAndConvert(
91
+ value, dataClass, fieldName, allowNone=True
92
+ )
93
+ else:
94
+ return lambda value, fieldName: Tools.checkAndConvert(
95
+ value, dataClass, f"{fieldName} <{dataType.name}>", allowNone=True
96
+ )
97
+ elif callable(dataClass):
98
+ if isArrayType:
99
+ return lambda value, fieldName: Tools.checkListAndConvert(
100
+ value, dataType.name, fieldName, dataClass, allowNone=True
101
+ )
102
+ else:
103
+ return lambda value, fieldName: Tools.checkAndConvert(
104
+ value, dataType.name, fieldName, dataClass, allowNone=True
105
+ )
106
+ else:
107
+ raise ValueError(f"Cannot create converter for type '{dataType}'")
108
+
109
+ def __str__(self):
110
+ s = " array" if self._isArrayType else ""
111
+ return f"<{type(self).__name__} [{self.dataType.name}]{s}>"
112
+
113
+ def __init__(
114
+ self,
115
+ dataType: CustomFieldDataType,
116
+ connection: Optional["LOGSConnection"],
117
+ enumOptions: Any = None,
118
+ ):
119
+ Tools.checkAndConvert(
120
+ dataType, CustomFieldDataType, "dataType", allowNone=False
121
+ )
122
+
123
+ self.dataType = dataType
124
+ self._isArrayType = self._arrayTypeCheck(dataType)
125
+ self._converter = self._generateConverter(
126
+ dataType, self._isArrayType, connection
127
+ )
128
+ self.enumOptions = enumOptions
129
+
130
+ def checkAndConvertIgnoreEnumOptions(self, value: Any, fieldName: str = "value"):
131
+ if not self._isArrayType and isinstance(value, list):
132
+ return [
133
+ self._converter(v, f"{fieldName}[{i}]") for i, v in enumerate(value)
134
+ ]
135
+ return self._converter(value, fieldName)
136
+
137
+ def checkAndConvert(self, value: Any, fieldName: str = "value", allowNone=True):
138
+ if allowNone and value is None:
139
+ return None
140
+ result = self._converter(value, fieldName)
141
+ if self._isArrayType:
142
+ if self._enumOptions:
143
+ for i, v in enumerate(result):
144
+ if v not in self._enumOptions:
145
+ raise ValueError(
146
+ f"Value of field '{fieldName}[{i}]' must be out of enumOptions. (Value '{Tools.truncString(str(v))}' is not accepted)"
147
+ )
148
+ else:
149
+ if self._enumOptions and result not in self._enumOptions:
150
+ raise ValueError(
151
+ f"Value of field '{fieldName}' must be out of enumOptions. (Value '{Tools.truncString(str(result))}' is not accepted)"
152
+ )
153
+
154
+ return result
155
+
156
+ @property
157
+ def enumOptions(self) -> Any:
158
+ return self._enumOptions
159
+
160
+ @enumOptions.setter
161
+ def enumOptions(self, value: Any):
162
+ if not value:
163
+ self._enumOptions = None
164
+ return
165
+
166
+ self._enumOptions = Tools.checkAndConvert(
167
+ value, list, "enumOptions", allowNone=False
168
+ )