logs-py 2.9.5__py3-none-any.whl → 3.0.0__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.
Potentially problematic release.
This version of logs-py might be problematic. Click here for more details.
- LOGS/Auxiliary/DateTimeConverter.py +11 -1
- LOGS/Auxiliary/Exceptions.py +40 -4
- LOGS/Auxiliary/LOGSErrorResponse.py +4 -1
- LOGS/Auxiliary/MinimalModelGenerator.py +88 -28
- LOGS/Auxiliary/Tools.py +11 -0
- LOGS/Converter/Conversion.py +248 -0
- LOGS/Converter/Converter.py +96 -0
- LOGS/Converter/ConverterParameter.py +88 -0
- LOGS/Converter/ExportParamters.py +89 -0
- LOGS/Converter/__init__.py +13 -0
- LOGS/Entities/Bridge.py +6 -3
- LOGS/Entities/CustomField.py +98 -93
- LOGS/Entities/CustomFieldModels.py +57 -0
- LOGS/Entities/CustomFieldRelations.py +10 -0
- LOGS/Entities/CustomFieldRequestParameter.py +43 -15
- LOGS/Entities/CustomFieldValue.py +88 -0
- LOGS/Entities/CustomFieldValueConverter.py +66 -0
- LOGS/Entities/CustomType.py +187 -0
- LOGS/Entities/CustomTypeEntityType.py +11 -0
- LOGS/Entities/CustomTypeMinimal.py +8 -0
- LOGS/Entities/CustomTypeRelations.py +59 -0
- LOGS/Entities/CustomTypeRequestParameter.py +61 -0
- LOGS/Entities/CustomTypeSection.py +39 -0
- LOGS/Entities/CustomTypes.py +12 -0
- LOGS/Entities/DataSource.py +28 -14
- LOGS/Entities/Dataset.py +276 -138
- LOGS/Entities/DatasetCreator.py +23 -72
- LOGS/Entities/DatasetInfo.py +23 -2
- LOGS/Entities/DatasetModels.py +31 -0
- LOGS/Entities/DatasetRequestParameter.py +45 -32
- LOGS/Entities/Datatrack.py +74 -30
- LOGS/Entities/DatatrackFormattedTable.py +25 -0
- LOGS/Entities/DatatrackGeneric.py +34 -0
- LOGS/Entities/DatatrackImage.py +25 -0
- LOGS/Entities/DatatrackNumericArray.py +9 -39
- LOGS/Entities/DatatrackNumericMatrix.py +86 -0
- LOGS/Entities/DocumentRequestParameter.py +2 -2
- LOGS/Entities/EntitiesRequestParameter.py +2 -2
- LOGS/Entities/Experiment.py +3 -3
- LOGS/Entities/FileExcludePattern.py +8 -0
- LOGS/Entities/FormatFormat.py +22 -1
- LOGS/Entities/FormatFormatRequestParameter.py +2 -1
- LOGS/Entities/FormatFormats.py +1 -1
- LOGS/Entities/FormatMethod.py +2 -2
- LOGS/Entities/FormattedTable/DatatypeFormattedTable.py +135 -0
- LOGS/Entities/FormattedTable/DatatypeFormattedTableCell.py +108 -0
- LOGS/Entities/FormattedTable/DatatypeFormattedTableSettings.py +11 -0
- LOGS/Entities/FormattedTable/__init__.py +9 -0
- LOGS/Entities/ILiterarTypedEntity.py +19 -0
- LOGS/Entities/Instrument.py +3 -3
- LOGS/Entities/Inventories.py +12 -0
- LOGS/Entities/Inventory.py +95 -0
- LOGS/Entities/InventoryMinimal.py +20 -0
- LOGS/Entities/InventoryRelations.py +23 -0
- LOGS/Entities/InventoryRequestParameter.py +53 -0
- LOGS/Entities/LabNotebook.py +37 -0
- LOGS/Entities/LabNotebookEntry.py +50 -27
- LOGS/Entities/LabNotebookEntryContent/BasicAttribute.py +15 -0
- LOGS/Entities/LabNotebookEntryContent/EntityAttribute.py +85 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentBlockquote.py +13 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentBulletList.py +17 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentCallout.py +40 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentContentPlaceholderNode.py +31 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentConverter.py +207 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentDocument.py +8 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentEntity.py +13 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentEntityMention.py +31 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentHeading.py +33 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentHorizontalRule.py +12 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentItem.py +37 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentListItem.py +49 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentOrderedList.py +31 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentParagraph.py +13 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentTable.py +17 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentTableCell.py +40 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentTableRow.py +8 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentTaskList.py +17 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentTaskListItem.py +31 -0
- LOGS/Entities/LabNotebookEntryContent/EntryContentText.py +33 -0
- LOGS/Entities/LabNotebookEntryContent/IEntryContentWithAttribute.py +23 -0
- LOGS/Entities/LabNotebookEntryContent/IEntryContentWithContent.py +38 -0
- LOGS/Entities/LabNotebookEntryContent/IEntryContentWithTextAttribute.py +16 -0
- LOGS/Entities/LabNotebookEntryContent/TextAttribute.py +46 -0
- LOGS/Entities/LabNotebookEntryContent/TextMarkAtributes.py +64 -0
- LOGS/Entities/LabNotebookEntryContent/TextMarkConverter.py +45 -0
- LOGS/Entities/LabNotebookEntryContent/TextMarks.py +71 -0
- LOGS/Entities/LabNotebookEntryContent/__init__.py +34 -0
- LOGS/Entities/LabNotebookEntryRequestParameter.py +2 -0
- LOGS/Entities/LabNotebookExperiment.py +52 -0
- LOGS/Entities/LabNotebookExperimentMinimal.py +8 -0
- LOGS/Entities/LabNotebookExperimentRequestParameter.py +49 -0
- LOGS/Entities/LabNotebookExperiments.py +16 -0
- LOGS/Entities/LabNotebookMinimal.py +19 -0
- LOGS/Entities/LabNotebookModels.py +14 -0
- LOGS/Entities/LabNotebookRequestParameter.py +43 -0
- LOGS/Entities/LabNotebooks.py +12 -0
- LOGS/Entities/Method.py +3 -3
- LOGS/Entities/ParserLog.py +4 -0
- LOGS/Entities/Person.py +2 -2
- LOGS/Entities/Project.py +7 -7
- LOGS/Entities/{ProjectUserPermission.py → ProjectPersonPermission.py} +14 -4
- LOGS/Entities/Role.py +3 -3
- LOGS/Entities/RunState.py +1 -0
- LOGS/Entities/Sample.py +36 -57
- LOGS/Entities/SampleRequestParameter.py +30 -15
- LOGS/Entities/Track.py +10 -6
- LOGS/Entities/TrackData.py +11 -0
- LOGS/Entities/TrackImage.py +21 -0
- LOGS/Entities/TrackImageData.py +20 -0
- LOGS/Entities/TrackMatrix.py +28 -0
- LOGS/Entities/TrackMatrixData.py +22 -0
- LOGS/Entities/TrackTable.py +21 -0
- LOGS/Entities/TrackTableData.py +22 -0
- LOGS/Entities/TrackXY.py +5 -1
- LOGS/Entities/TrackXYComplex.py +1 -1
- LOGS/Entities/__init__.py +26 -7
- LOGS/Entity/ConnectedEntity.py +39 -1
- LOGS/Entity/Entity.py +9 -14
- LOGS/Entity/SerializeableContent.py +127 -45
- LOGS/Interfaces/IHierarchyType.py +63 -0
- LOGS/Interfaces/IPermissionedEntity.py +29 -5
- LOGS/Interfaces/IProjectBased.py +1 -1
- LOGS/Interfaces/IRelatedEntity.py +3 -2
- LOGS/Interfaces/ITypedEntity.py +69 -12
- LOGS/Interfaces/IVersionedEntity.py +39 -0
- LOGS/LOGS.py +140 -48
- LOGS/LOGSConnection.py +52 -24
- LOGS/LOGSOptions.py +8 -0
- LOGS/Parameters/Color.py +92 -0
- LOGS/Parameters/ParameterBase.py +55 -0
- LOGS/Parameters/ParameterConverter.py +24 -0
- LOGS/Parameters/ParameterElement.py +99 -0
- LOGS/Parameters/ParameterList.py +52 -0
- LOGS/Parameters/ParameterTable.py +64 -0
- LOGS/Parameters/__init__.py +13 -0
- LOGS/__init__.py +1 -0
- {logs_py-2.9.5.dist-info → logs_py-3.0.0.dist-info}/METADATA +3 -2
- logs_py-3.0.0.dist-info/RECORD +263 -0
- {logs_py-2.9.5.dist-info → logs_py-3.0.0.dist-info}/WHEEL +1 -1
- LOGS/Entities/CustomFieldEnums.py +0 -25
- LOGS/Entities/DatasetType.py +0 -7
- LOGS/Entities/DatasetTypeMinimal.py +0 -8
- LOGS/Entities/SampleType.py +0 -34
- LOGS/Entities/SampleTypeMinimal.py +0 -8
- LOGS/Entities/SampleTypeRequestParameter.py +0 -8
- LOGS/Entities/SampleTypes.py +0 -12
- logs_py-2.9.5.dist-info/RECORD +0 -183
- {logs_py-2.9.5.dist-info → logs_py-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,8 @@ _Relations = TypeVar("_Relations", bound=IRelationModel)
|
|
|
14
14
|
|
|
15
15
|
@dataclass
|
|
16
16
|
class IRelatedEntity(Generic[_Relations], IEntityInterface):
|
|
17
|
-
_relationType: Optional[Type[_Relations]] = None
|
|
17
|
+
# The expression here should be _relationType: Optional[Type[_Relations]] = None, but it is not working
|
|
18
|
+
_relationType: Optional[Type] = None
|
|
18
19
|
_relations: Optional[_Relations] = None
|
|
19
20
|
|
|
20
21
|
@property
|
|
@@ -29,6 +30,6 @@ class IRelatedEntity(Generic[_Relations], IEntityInterface):
|
|
|
29
30
|
% (type(self).__name__)
|
|
30
31
|
)
|
|
31
32
|
|
|
32
|
-
self.
|
|
33
|
+
self._relations = Tools.checkAndConvert(
|
|
33
34
|
value, self._relationType, "relations", allowNone=True
|
|
34
35
|
)
|
LOGS/Interfaces/ITypedEntity.py
CHANGED
|
@@ -1,28 +1,85 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import TYPE_CHECKING,
|
|
2
|
+
from typing import TYPE_CHECKING, List, Optional, Sequence, Union
|
|
3
3
|
|
|
4
4
|
from LOGS.Auxiliary import Tools
|
|
5
|
+
from LOGS.Auxiliary.MinimalModelGenerator import MinimalFromSingle
|
|
6
|
+
from LOGS.Entities.CustomFieldValue import (
|
|
7
|
+
CustomFieldValue,
|
|
8
|
+
CustomSectionValue,
|
|
9
|
+
ICustomValue,
|
|
10
|
+
)
|
|
11
|
+
from LOGS.Entities.CustomFieldValueConverter import CustomFieldValueConverter
|
|
5
12
|
from LOGS.Interfaces.IEntityInterface import IEntityInterface
|
|
6
13
|
|
|
7
14
|
if TYPE_CHECKING:
|
|
8
|
-
|
|
15
|
+
from LOGS.Entities.CustomTypeMinimal import CustomTypeMinimal
|
|
9
16
|
|
|
10
17
|
|
|
11
18
|
@dataclass
|
|
12
19
|
class ITypedEntityRequest:
|
|
13
|
-
includeCustomFields: Optional[bool] = None
|
|
14
|
-
customFields: Optional[Dict[str, Any]] = None
|
|
20
|
+
includeCustomFields: Optional[List[bool]] = None
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
class ITypedEntity(IEntityInterface):
|
|
18
|
-
|
|
24
|
+
_customType: Optional["CustomTypeMinimal"] = None
|
|
25
|
+
_customValues: Optional[
|
|
26
|
+
Sequence[Union["CustomFieldValue", "CustomSectionValue"]]
|
|
27
|
+
] = None
|
|
28
|
+
|
|
29
|
+
def _customValueConverter(self, value):
|
|
30
|
+
return Tools.checkListAndConvert(
|
|
31
|
+
value, ICustomValue, "customValues", allowNone=True
|
|
32
|
+
)
|
|
19
33
|
|
|
20
34
|
@property
|
|
21
|
-
def
|
|
22
|
-
return self.
|
|
35
|
+
def customType(self) -> Optional["CustomTypeMinimal"]:
|
|
36
|
+
return self._customType
|
|
23
37
|
|
|
24
|
-
@
|
|
25
|
-
def
|
|
26
|
-
self.
|
|
27
|
-
|
|
28
|
-
|
|
38
|
+
@customType.setter
|
|
39
|
+
def customType(self, value):
|
|
40
|
+
self._customType = MinimalFromSingle(value, "CustomTypeMinimal", "customType")
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def customValues(
|
|
44
|
+
self,
|
|
45
|
+
) -> Optional[
|
|
46
|
+
Union[
|
|
47
|
+
Sequence[Union["CustomFieldValue", "CustomSectionValue"]],
|
|
48
|
+
Union["CustomFieldValue", "CustomSectionValue"],
|
|
49
|
+
]
|
|
50
|
+
]:
|
|
51
|
+
return self._customValues
|
|
52
|
+
|
|
53
|
+
@customValues.setter
|
|
54
|
+
def customValues(self, value):
|
|
55
|
+
self._customValues = CustomFieldValueConverter.convert(value, "customValues")
|
|
56
|
+
|
|
57
|
+
def _extractCustomFieldValue(
|
|
58
|
+
self,
|
|
59
|
+
value: Union[
|
|
60
|
+
CustomFieldValue,
|
|
61
|
+
CustomSectionValue,
|
|
62
|
+
Sequence[Union[CustomFieldValue, CustomSectionValue]],
|
|
63
|
+
],
|
|
64
|
+
) -> List[CustomFieldValue]:
|
|
65
|
+
if isinstance(value, list):
|
|
66
|
+
result: List[CustomFieldValue] = []
|
|
67
|
+
for v in value:
|
|
68
|
+
result += self._extractCustomFieldValue(v)
|
|
69
|
+
return result
|
|
70
|
+
if isinstance(value, CustomFieldValue):
|
|
71
|
+
return [value]
|
|
72
|
+
if isinstance(value, CustomSectionValue):
|
|
73
|
+
if value.content:
|
|
74
|
+
return self._extractCustomFieldValue(value.content)
|
|
75
|
+
|
|
76
|
+
return []
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def customFieldValues(
|
|
80
|
+
self,
|
|
81
|
+
) -> Optional[List["CustomFieldValue"]]:
|
|
82
|
+
if self.customValues == None:
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
return self._extractCustomFieldValue(self.customValues)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import TYPE_CHECKING, Generic, List, Optional, TypeVar, Union
|
|
3
|
+
|
|
4
|
+
from LOGS.Auxiliary import Tools
|
|
5
|
+
from LOGS.Interfaces.ICreationRecord import ICreationRecord, ICreationRecordRequest
|
|
6
|
+
from LOGS.Interfaces.IModificationRecord import (
|
|
7
|
+
IModificationRecord,
|
|
8
|
+
IModificationRecordRequest,
|
|
9
|
+
)
|
|
10
|
+
from LOGS.Interfaces.ISoftDeletable import ISoftDeletable, ISoftDeletableRequest
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
_idType = TypeVar("_idType", bound=Union[int, str])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class IVersionedEntityRequest(
|
|
20
|
+
Generic[_idType],
|
|
21
|
+
ICreationRecordRequest,
|
|
22
|
+
IModificationRecordRequest,
|
|
23
|
+
ISoftDeletableRequest,
|
|
24
|
+
):
|
|
25
|
+
originalIds: Optional[List[_idType]] = None
|
|
26
|
+
versionIds: Optional[List[int]] = None
|
|
27
|
+
versions: Optional[List[int]] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class IVersionedEntity(ICreationRecord, IModificationRecord, ISoftDeletable):
|
|
31
|
+
_version: Optional[int] = None
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def version(self) -> Optional[int]:
|
|
35
|
+
return self._version
|
|
36
|
+
|
|
37
|
+
@version.setter
|
|
38
|
+
def version(self, value):
|
|
39
|
+
self._version = Tools.checkAndConvert(value, int, "version", allowNone=True)
|
LOGS/LOGS.py
CHANGED
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
"""
|
|
3
3
|
A library to access the LOGS API via Python
|
|
4
4
|
"""
|
|
5
|
-
# +---------------+-----------+--------------+-------------------------+
|
|
6
|
-
# | When | Version | Who | What |
|
|
7
|
-
# +===============+===========+==============+=========================+
|
|
8
|
-
# | 2021-07-16 | 0.1 | Sina Kazemi | First version |
|
|
9
|
-
# +---------------+-----------+--------------+-------------------------+
|
|
10
|
-
# | 2021-09-18 | 1.0 | Sina Kazemi | First official release |
|
|
11
|
-
# +---------------+-----------+--------------+-------------------------+
|
|
12
5
|
|
|
13
6
|
import json
|
|
14
7
|
import os
|
|
@@ -36,13 +29,15 @@ from LOGS.Auxiliary import (
|
|
|
36
29
|
Tools,
|
|
37
30
|
formatErrorMessage,
|
|
38
31
|
)
|
|
39
|
-
from LOGS.Entities import SampleType
|
|
40
32
|
from LOGS.Entities.Bridge import Bridge
|
|
41
33
|
from LOGS.Entities.BridgeRequestParameter import BridgeRequestParameter
|
|
42
34
|
from LOGS.Entities.Bridges import Bridges
|
|
43
35
|
from LOGS.Entities.CustomField import CustomField
|
|
44
36
|
from LOGS.Entities.CustomFieldRequestParameter import CustomFieldRequestParameter
|
|
45
37
|
from LOGS.Entities.CustomFields import CustomFields
|
|
38
|
+
from LOGS.Entities.CustomType import CustomType
|
|
39
|
+
from LOGS.Entities.CustomTypeRequestParameter import CustomTypeRequestParameter
|
|
40
|
+
from LOGS.Entities.CustomTypes import CustomTypes
|
|
46
41
|
from LOGS.Entities.Dataset import Dataset
|
|
47
42
|
from LOGS.Entities.DatasetCreator import DatasetCreator
|
|
48
43
|
from LOGS.Entities.DatasetMatching import DatasetMatching
|
|
@@ -83,11 +78,22 @@ from LOGS.Entities.FormatVendors import FormatVendors
|
|
|
83
78
|
from LOGS.Entities.Instrument import Instrument
|
|
84
79
|
from LOGS.Entities.InstrumentRequestParameter import InstrumentRequestParameter
|
|
85
80
|
from LOGS.Entities.Instruments import Instruments
|
|
81
|
+
from LOGS.Entities.Inventories import Inventories
|
|
82
|
+
from LOGS.Entities.Inventory import Inventory
|
|
83
|
+
from LOGS.Entities.InventoryRequestParameter import InventoryRequestParameter
|
|
84
|
+
from LOGS.Entities.LabNotebook import LabNotebook
|
|
86
85
|
from LOGS.Entities.LabNotebookEntries import LabNotebookEntries
|
|
87
86
|
from LOGS.Entities.LabNotebookEntry import LabNotebookEntry
|
|
88
87
|
from LOGS.Entities.LabNotebookEntryRequestParameter import (
|
|
89
88
|
LabNotebookEntryRequestParameter,
|
|
90
89
|
)
|
|
90
|
+
from LOGS.Entities.LabNotebookExperiment import LabNotebookExperiment
|
|
91
|
+
from LOGS.Entities.LabNotebookExperimentRequestParameter import (
|
|
92
|
+
LabNotebookExperimentRequestParameter,
|
|
93
|
+
)
|
|
94
|
+
from LOGS.Entities.LabNotebookExperiments import LabNotebookExperiments
|
|
95
|
+
from LOGS.Entities.LabNotebookRequestParameter import LabNotebookRequestParameter
|
|
96
|
+
from LOGS.Entities.LabNotebooks import LabNotebooks
|
|
91
97
|
from LOGS.Entities.Method import Method
|
|
92
98
|
from LOGS.Entities.MethodRequestParameter import MethodRequestParameter
|
|
93
99
|
from LOGS.Entities.Methods import Methods
|
|
@@ -106,39 +112,41 @@ from LOGS.Entities.Roles import Roles
|
|
|
106
112
|
from LOGS.Entities.Sample import Sample
|
|
107
113
|
from LOGS.Entities.SampleRequestParameter import SampleRequestParameter
|
|
108
114
|
from LOGS.Entities.Samples import Samples
|
|
109
|
-
from LOGS.Entities.SampleTypeRequestParameter import SampleTypeRequestParameter
|
|
110
|
-
from LOGS.Entities.SampleTypes import SampleTypes
|
|
111
115
|
from LOGS.Entity import Entity, EntityIterator, IEntityWithIntId
|
|
112
116
|
from LOGS.Entity.ConnectedEntity import ConnectedEntity
|
|
113
117
|
from LOGS.Interfaces.ISoftDeletable import ISoftDeletable
|
|
114
118
|
from LOGS.Interfaces.IUniqueEntity import IUniqueEntity
|
|
115
119
|
from LOGS.LOGSConnection import LOGSConnection
|
|
120
|
+
from LOGS.LOGSOptions import LOGSOptions
|
|
116
121
|
from LOGS.ServerMetaData import ServerMetaData
|
|
117
122
|
|
|
118
123
|
_T = TypeVar(
|
|
119
124
|
"_T",
|
|
120
|
-
Document,
|
|
121
|
-
Person,
|
|
122
|
-
Project,
|
|
123
|
-
Sample,
|
|
124
|
-
Dataset,
|
|
125
125
|
Bridge,
|
|
126
|
+
CustomField,
|
|
127
|
+
CustomType,
|
|
128
|
+
Dataset,
|
|
126
129
|
DataSource,
|
|
130
|
+
Document,
|
|
127
131
|
Equipment,
|
|
128
132
|
Experiment,
|
|
133
|
+
Format,
|
|
134
|
+
FormatFormat,
|
|
135
|
+
FormatInstrument,
|
|
136
|
+
FormatMethod,
|
|
137
|
+
FormatVendor,
|
|
138
|
+
IEntityWithIntId,
|
|
129
139
|
Instrument,
|
|
130
|
-
|
|
140
|
+
Inventory,
|
|
141
|
+
LabNotebook,
|
|
131
142
|
LabNotebookEntry,
|
|
132
|
-
|
|
143
|
+
LabNotebookExperiment,
|
|
144
|
+
Method,
|
|
133
145
|
Origin,
|
|
134
|
-
|
|
146
|
+
Person,
|
|
147
|
+
Project,
|
|
135
148
|
Role,
|
|
136
|
-
|
|
137
|
-
FormatMethod,
|
|
138
|
-
FormatInstrument,
|
|
139
|
-
FormatFormat,
|
|
140
|
-
CustomField,
|
|
141
|
-
SampleType,
|
|
149
|
+
Sample,
|
|
142
150
|
)
|
|
143
151
|
|
|
144
152
|
|
|
@@ -147,32 +155,36 @@ class LOGS:
|
|
|
147
155
|
|
|
148
156
|
_connection: LOGSConnection
|
|
149
157
|
_entities: List[Type] = [
|
|
150
|
-
Document,
|
|
151
|
-
Person,
|
|
152
|
-
Project,
|
|
153
|
-
Sample,
|
|
154
|
-
Dataset,
|
|
155
158
|
Bridge,
|
|
159
|
+
CustomField,
|
|
160
|
+
CustomType,
|
|
161
|
+
Dataset,
|
|
156
162
|
DataSource,
|
|
163
|
+
Document,
|
|
157
164
|
Equipment,
|
|
158
165
|
Experiment,
|
|
159
166
|
Instrument,
|
|
160
|
-
|
|
167
|
+
Inventory,
|
|
168
|
+
LabNotebook,
|
|
161
169
|
LabNotebookEntry,
|
|
170
|
+
LabNotebookExperiment,
|
|
171
|
+
Method,
|
|
162
172
|
Origin,
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
Person,
|
|
174
|
+
Project,
|
|
175
|
+
Sample,
|
|
165
176
|
]
|
|
166
177
|
_entityByName = {t.__name__: t for t in _entities}
|
|
167
178
|
_defaultConfigFile: str = "logs.json"
|
|
168
179
|
_currentUser: Person
|
|
180
|
+
_cacheDir: Optional[str] = None
|
|
169
181
|
|
|
170
182
|
def __init__(
|
|
171
183
|
self,
|
|
172
184
|
url: Optional[str] = None,
|
|
173
185
|
apiKey: Optional[str] = None,
|
|
174
186
|
configFile: Optional[str] = None,
|
|
175
|
-
|
|
187
|
+
options: Optional[LOGSOptions] = None,
|
|
176
188
|
verify: bool = True,
|
|
177
189
|
):
|
|
178
190
|
"""Checks the connection to the server on creation
|
|
@@ -185,6 +197,10 @@ class LOGS:
|
|
|
185
197
|
:raises: Exception: The URL does not define a group.
|
|
186
198
|
:raises: Exception: Server cannot be reached.
|
|
187
199
|
"""
|
|
200
|
+
self._options = Tools.checkAndConvert(
|
|
201
|
+
options, LOGSOptions, "options", initOnNone=True
|
|
202
|
+
)
|
|
203
|
+
|
|
188
204
|
_url = url
|
|
189
205
|
_apiKey = apiKey
|
|
190
206
|
|
|
@@ -212,13 +228,11 @@ class LOGS:
|
|
|
212
228
|
"The API key to access the server %a must be provided" % _url
|
|
213
229
|
)
|
|
214
230
|
|
|
215
|
-
self.verbose = verbose
|
|
216
231
|
self.promptPrefix = "LOGSAPI>"
|
|
217
232
|
|
|
218
233
|
self._connection = LOGSConnection(
|
|
219
|
-
url=_url, apiKey=_apiKey,
|
|
234
|
+
url=_url, apiKey=_apiKey, options=self._options, verify=verify
|
|
220
235
|
)
|
|
221
|
-
|
|
222
236
|
self._currentUser = self._fetchCurrentUser()
|
|
223
237
|
|
|
224
238
|
def _fetchCurrentUser(self) -> Person:
|
|
@@ -263,8 +277,23 @@ class LOGS:
|
|
|
263
277
|
size /= 1024.0
|
|
264
278
|
return "%.1f%s%s" % (size, "Yi", suffix)
|
|
265
279
|
|
|
280
|
+
def getDatasetDir(self, dataset: Dataset):
|
|
281
|
+
if self.cacheDir:
|
|
282
|
+
if not os.path.isdir(self.cacheDir):
|
|
283
|
+
raise LOGSException(
|
|
284
|
+
f"Specified cache directory '{self.cacheDir}' cannot be opened or is not a directory."
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
dataDir = os.path.join(self.cacheDir, dataset.cacheId)
|
|
288
|
+
if dataDir and not os.path.exists(dataDir):
|
|
289
|
+
os.mkdir(dataDir)
|
|
290
|
+
return dataDir
|
|
291
|
+
return None
|
|
292
|
+
|
|
266
293
|
def _fetchEntity(self, entityType: Type[_T], id: Union[int, str]) -> _T:
|
|
267
294
|
e = entityType(id=cast(Any, id), connection=self._connection)
|
|
295
|
+
if isinstance(e, Dataset):
|
|
296
|
+
e.cacheDir = self.getDatasetDir(e)
|
|
268
297
|
e.fetch()
|
|
269
298
|
return e
|
|
270
299
|
|
|
@@ -352,7 +381,8 @@ class LOGS:
|
|
|
352
381
|
raise EntityNotFoundException(entities[0])
|
|
353
382
|
|
|
354
383
|
data, responseError = self._connection.putEndpoint(
|
|
355
|
-
entityType._endpoint + [str(entities[0].id)],
|
|
384
|
+
entityType._endpoint + [str(entities[0].id)],
|
|
385
|
+
data=entities[0]._toDictWithSlack(),
|
|
356
386
|
)
|
|
357
387
|
if (
|
|
358
388
|
isinstance(data, dict)
|
|
@@ -364,7 +394,7 @@ class LOGS:
|
|
|
364
394
|
else:
|
|
365
395
|
data, responseError = self._connection.postEndpoint(
|
|
366
396
|
entityType._endpoint + ["bulk_edit"],
|
|
367
|
-
data=[e.
|
|
397
|
+
data=[e._toDictWithSlack() for e in entities],
|
|
368
398
|
)
|
|
369
399
|
if (
|
|
370
400
|
isinstance(data, dict)
|
|
@@ -946,6 +976,42 @@ class LOGS:
|
|
|
946
976
|
)
|
|
947
977
|
return DataSources(connection=self._connection, parameters=parameter)
|
|
948
978
|
|
|
979
|
+
def labNotebook(self, id: int) -> LabNotebook:
|
|
980
|
+
return self._fetchEntity(LabNotebook, id)
|
|
981
|
+
|
|
982
|
+
def labNotebooks(
|
|
983
|
+
self, parameter: Optional[LabNotebookRequestParameter] = None
|
|
984
|
+
) -> LabNotebooks:
|
|
985
|
+
if parameter and not isinstance(parameter, LabNotebookRequestParameter):
|
|
986
|
+
raise LOGSException(
|
|
987
|
+
"Parameter for %s.LabNotebooks must be of type %a. (Got %a)"
|
|
988
|
+
% (
|
|
989
|
+
type(self).__name__,
|
|
990
|
+
LabNotebookRequestParameter.__name__,
|
|
991
|
+
type(parameter).__name__,
|
|
992
|
+
)
|
|
993
|
+
)
|
|
994
|
+
return LabNotebooks(connection=self._connection, parameters=parameter)
|
|
995
|
+
|
|
996
|
+
def labNotebookExperiment(self, id: int) -> LabNotebookExperiment:
|
|
997
|
+
return self._fetchEntity(LabNotebookExperiment, id)
|
|
998
|
+
|
|
999
|
+
def labNotebookExperiments(
|
|
1000
|
+
self, parameter: Optional[LabNotebookExperimentRequestParameter] = None
|
|
1001
|
+
) -> LabNotebookExperiments:
|
|
1002
|
+
if parameter and not isinstance(
|
|
1003
|
+
parameter, LabNotebookExperimentRequestParameter
|
|
1004
|
+
):
|
|
1005
|
+
raise LOGSException(
|
|
1006
|
+
"Parameter for %s.LabNotebookExperiments must be of type %a. (Got %a)"
|
|
1007
|
+
% (
|
|
1008
|
+
type(self).__name__,
|
|
1009
|
+
LabNotebookExperimentRequestParameter.__name__,
|
|
1010
|
+
type(parameter).__name__,
|
|
1011
|
+
)
|
|
1012
|
+
)
|
|
1013
|
+
return LabNotebookExperiments(connection=self._connection, parameters=parameter)
|
|
1014
|
+
|
|
949
1015
|
def labNotebookEntry(self, id: int) -> LabNotebookEntry:
|
|
950
1016
|
return self._fetchEntity(LabNotebookEntry, id)
|
|
951
1017
|
|
|
@@ -1048,22 +1114,39 @@ class LOGS:
|
|
|
1048
1114
|
)
|
|
1049
1115
|
return CustomFields(connection=self._connection, parameters=parameter)
|
|
1050
1116
|
|
|
1051
|
-
def
|
|
1052
|
-
return self._fetchEntity(
|
|
1117
|
+
def customType(self, id: int) -> CustomType:
|
|
1118
|
+
return self._fetchEntity(CustomType, id)
|
|
1053
1119
|
|
|
1054
|
-
def
|
|
1055
|
-
self, parameter: Optional[
|
|
1056
|
-
) ->
|
|
1057
|
-
if parameter and not isinstance(parameter,
|
|
1120
|
+
def customTypes(
|
|
1121
|
+
self, parameter: Optional[CustomTypeRequestParameter] = None
|
|
1122
|
+
) -> CustomTypes:
|
|
1123
|
+
if parameter and not isinstance(parameter, CustomTypeRequestParameter):
|
|
1058
1124
|
raise LOGSException(
|
|
1059
|
-
"Parameter for %s.
|
|
1125
|
+
"Parameter for %s.CustomTypes must be of type %a. (Got %a)"
|
|
1060
1126
|
% (
|
|
1061
1127
|
type(self).__name__,
|
|
1062
|
-
|
|
1128
|
+
CustomTypeRequestParameter.__name__,
|
|
1063
1129
|
type(parameter).__name__,
|
|
1064
1130
|
)
|
|
1065
1131
|
)
|
|
1066
|
-
return
|
|
1132
|
+
return CustomTypes(connection=self._connection, parameters=parameter)
|
|
1133
|
+
|
|
1134
|
+
def inventory(self, id: int) -> Inventory:
|
|
1135
|
+
return self._fetchEntity(Inventory, id)
|
|
1136
|
+
|
|
1137
|
+
def inventories(
|
|
1138
|
+
self, parameter: Optional[InventoryRequestParameter] = None
|
|
1139
|
+
) -> Inventories:
|
|
1140
|
+
if parameter and not isinstance(parameter, InventoryRequestParameter):
|
|
1141
|
+
raise LOGSException(
|
|
1142
|
+
"Parameter for %s.Inventories must be of type %a. (Got %a)"
|
|
1143
|
+
% (
|
|
1144
|
+
type(self).__name__,
|
|
1145
|
+
InventoryRequestParameter.__name__,
|
|
1146
|
+
type(parameter).__name__,
|
|
1147
|
+
)
|
|
1148
|
+
)
|
|
1149
|
+
return Inventories(connection=self._connection, parameters=parameter)
|
|
1067
1150
|
|
|
1068
1151
|
def entity(self, uid: str):
|
|
1069
1152
|
return Entities(connection=self._connection).fetch(uid=uid)
|
|
@@ -1126,6 +1209,13 @@ class LOGS:
|
|
|
1126
1209
|
return self._currentUser
|
|
1127
1210
|
|
|
1128
1211
|
@property
|
|
1212
|
+
def cacheDir(self) -> Optional[str]:
|
|
1213
|
+
return self._cacheDir
|
|
1214
|
+
|
|
1215
|
+
@cacheDir.setter
|
|
1216
|
+
def cacheDir(self, value):
|
|
1217
|
+
self._cacheDir = Tools.checkAndConvert(value, str, "cacheDir")
|
|
1218
|
+
|
|
1129
1219
|
def version(self) -> Optional[str]:
|
|
1130
1220
|
return self._connection.metadata.version
|
|
1131
1221
|
|
|
@@ -1142,4 +1232,6 @@ if __name__ == "__main__":
|
|
|
1142
1232
|
# api_key = "8V6oQ804t2nPgGPDJIk4CuneRI5q48ERUxgEpk+YqXzX9uLuMUySycHkeXP6DefN"
|
|
1143
1233
|
# url = "http://localhost:900/sandbox"
|
|
1144
1234
|
|
|
1145
|
-
logs = LOGS(
|
|
1235
|
+
logs = LOGS(
|
|
1236
|
+
_url, api_key, options=LOGSOptions(showRequestUrl=True, showRequestBody=False)
|
|
1237
|
+
)
|
LOGS/LOGSConnection.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import random
|
|
2
3
|
import re
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from enum import Enum
|
|
@@ -10,6 +11,7 @@ from requests import Response
|
|
|
10
11
|
from LOGS.Auxiliary import LOGSException, Tools
|
|
11
12
|
from LOGS.Auxiliary.LOGSErrorResponse import LOGSErrorResponse
|
|
12
13
|
from LOGS.Entities.FileEntry import FileEntry
|
|
14
|
+
from LOGS.LOGSOptions import LOGSOptions
|
|
13
15
|
from LOGS.ServerMetaData import ServerMetaData
|
|
14
16
|
|
|
15
17
|
|
|
@@ -32,7 +34,7 @@ class LOGSConnection:
|
|
|
32
34
|
"""Python class to access the LOGS web API"""
|
|
33
35
|
|
|
34
36
|
_noErrorStates = set([200, 201, 204])
|
|
35
|
-
_compatibleAPIVersions = set(["
|
|
37
|
+
_compatibleAPIVersions = set(["3.0"])
|
|
36
38
|
# _compatibleAPIVersions = set(["1.1"])
|
|
37
39
|
__urlRe = re.compile(r"(?:(https*)\:\/\/)*([^\/:]+)(?:\:(\d+))*(?:\/(.*))*")
|
|
38
40
|
__urlApiRe = re.compile(r"api\/(\d+\.\d+)")
|
|
@@ -45,7 +47,7 @@ class LOGSConnection:
|
|
|
45
47
|
url: str,
|
|
46
48
|
apiKey: str,
|
|
47
49
|
use_internal: bool = False,
|
|
48
|
-
|
|
50
|
+
options: Optional[LOGSOptions] = None,
|
|
49
51
|
verify: bool = True,
|
|
50
52
|
):
|
|
51
53
|
"""Checks the connection to the server on creation
|
|
@@ -58,13 +60,14 @@ class LOGSConnection:
|
|
|
58
60
|
:raises Exception: The URL does not define a group.
|
|
59
61
|
:raises Exception: Server cannot be reached.
|
|
60
62
|
"""
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
self._options = Tools.checkAndConvert(
|
|
64
|
+
options, LOGSOptions, "options", initOnNone=True
|
|
65
|
+
)
|
|
63
66
|
self.promptPrefix = "LOGSAPI>"
|
|
64
67
|
|
|
65
68
|
self.url = url
|
|
66
69
|
|
|
67
|
-
if self.
|
|
70
|
+
if self._options.showServerInfo:
|
|
68
71
|
self.printServerStatus()
|
|
69
72
|
|
|
70
73
|
self._apiKey = apiKey
|
|
@@ -127,8 +130,10 @@ class LOGSConnection:
|
|
|
127
130
|
result, error = self.getEndpoint(testEndpoint)
|
|
128
131
|
url = result["url"] if isinstance(result, dict) else self.url
|
|
129
132
|
if error:
|
|
130
|
-
raise LOGSException(
|
|
131
|
-
|
|
133
|
+
raise LOGSException(
|
|
134
|
+
"Could not connect to '%s': %s" % (url, "\n".join(error.errors))
|
|
135
|
+
)
|
|
136
|
+
if self._options.showServerInfo:
|
|
132
137
|
print(
|
|
133
138
|
self.promptPrefix,
|
|
134
139
|
"Connection to server '%s://%s%s' successful."
|
|
@@ -236,7 +241,7 @@ class LOGSConnection:
|
|
|
236
241
|
|
|
237
242
|
:return: The respose of the server and the error code.
|
|
238
243
|
"""
|
|
239
|
-
if self.
|
|
244
|
+
if self._options.showRequestUrl:
|
|
240
245
|
paramString = ""
|
|
241
246
|
if parameters:
|
|
242
247
|
paramString = " ".join(
|
|
@@ -282,7 +287,7 @@ class LOGSConnection:
|
|
|
282
287
|
:return: The respose of the server and the error code.
|
|
283
288
|
"""
|
|
284
289
|
|
|
285
|
-
if self.
|
|
290
|
+
if self._options.showRequestUrl:
|
|
286
291
|
print(self.promptPrefix, "DELETE: %s" % url)
|
|
287
292
|
|
|
288
293
|
response = requests.delete(
|
|
@@ -305,9 +310,12 @@ class LOGSConnection:
|
|
|
305
310
|
:return: The respose of the server and the error code.
|
|
306
311
|
"""
|
|
307
312
|
|
|
308
|
-
if self.
|
|
313
|
+
if self._options.showRequestUrl:
|
|
309
314
|
print(self.promptPrefix, "PUT: %s" % url)
|
|
310
315
|
|
|
316
|
+
if self._options.showRequestBody:
|
|
317
|
+
print(self.promptPrefix, "BODY: %s" % self.__convertBody(data))
|
|
318
|
+
|
|
311
319
|
response = requests.put(
|
|
312
320
|
url, headers=self.getHeader(), json=data, verify=self._verify
|
|
313
321
|
)
|
|
@@ -336,9 +344,14 @@ class LOGSConnection:
|
|
|
336
344
|
data: List[MultipartEntry] = [],
|
|
337
345
|
responseType: ResponseTypes = ResponseTypes.JSON,
|
|
338
346
|
):
|
|
339
|
-
if self.
|
|
347
|
+
if self._options.showRequestUrl:
|
|
340
348
|
print(self.promptPrefix, "POST: %s" % url)
|
|
341
349
|
|
|
350
|
+
if self._options.showRequestBody:
|
|
351
|
+
seperator = "-" * 29 + "".join(
|
|
352
|
+
[str(random.randint(0, 9)) for _ in range(29)]
|
|
353
|
+
)
|
|
354
|
+
|
|
342
355
|
files = []
|
|
343
356
|
for entry in data:
|
|
344
357
|
content: Any = ""
|
|
@@ -348,6 +361,22 @@ class LOGSConnection:
|
|
|
348
361
|
content = read.read()
|
|
349
362
|
else:
|
|
350
363
|
content = json.dumps(entry.content)
|
|
364
|
+
|
|
365
|
+
if self._options.showRequestBody:
|
|
366
|
+
print(self.promptPrefix, "BODY: %s" % seperator)
|
|
367
|
+
print(
|
|
368
|
+
self.promptPrefix,
|
|
369
|
+
"BODY: %s"
|
|
370
|
+
% "Content-Disposition: form-data; name='entry.fileName'",
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
c = (
|
|
374
|
+
str(content[:100]) + "..."
|
|
375
|
+
if isinstance(content, bytes) and len(content) > 100
|
|
376
|
+
else content
|
|
377
|
+
)
|
|
378
|
+
print(self.promptPrefix, "BODY: %s" % c)
|
|
379
|
+
|
|
351
380
|
files.append((entry.name, (entry.fileName, content)))
|
|
352
381
|
|
|
353
382
|
#### For checking the request
|
|
@@ -378,19 +407,7 @@ class LOGSConnection:
|
|
|
378
407
|
|
|
379
408
|
:return: The respose of the server and the error code.
|
|
380
409
|
"""
|
|
381
|
-
if self.
|
|
382
|
-
paramString = ""
|
|
383
|
-
if parameters:
|
|
384
|
-
paramString = " ".join(
|
|
385
|
-
("%s:%s" % (k, v))
|
|
386
|
-
for k, v in parameters.items()
|
|
387
|
-
if v != None and v != ""
|
|
388
|
-
)
|
|
389
|
-
print(
|
|
390
|
-
self.promptPrefix,
|
|
391
|
-
"GET: %s %s" % (url, "{" + paramString + "}" if paramString else ""),
|
|
392
|
-
)
|
|
393
|
-
if self.verbose:
|
|
410
|
+
if self._options.showRequestUrl:
|
|
394
411
|
paramString = ""
|
|
395
412
|
if parameters:
|
|
396
413
|
paramString = " ".join(
|
|
@@ -403,6 +420,9 @@ class LOGSConnection:
|
|
|
403
420
|
"POST: %s %s" % (url, "{" + paramString + "}" if paramString else ""),
|
|
404
421
|
)
|
|
405
422
|
|
|
423
|
+
if self._options.showRequestBody:
|
|
424
|
+
print(self.promptPrefix, "BODY: %s" % self.__convertBody(data))
|
|
425
|
+
|
|
406
426
|
response = requests.post(
|
|
407
427
|
url,
|
|
408
428
|
headers=self.getHeader(),
|
|
@@ -502,6 +522,14 @@ class LOGSConnection:
|
|
|
502
522
|
# # print(">", params)
|
|
503
523
|
# return {"customFields[%s]" % k: v for k, v in params.items()}
|
|
504
524
|
|
|
525
|
+
@classmethod
|
|
526
|
+
def __convertBody(cls, body) -> str:
|
|
527
|
+
if body == None:
|
|
528
|
+
return "None"
|
|
529
|
+
if isinstance(body, dict) or isinstance(body, list):
|
|
530
|
+
return json.dumps(body)
|
|
531
|
+
return body
|
|
532
|
+
|
|
505
533
|
def __convertResponse(
|
|
506
534
|
self,
|
|
507
535
|
response: Response,
|