UncountablePythonSDK 0.0.16__py3-none-any.whl → 0.0.18__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 UncountablePythonSDK might be problematic. Click here for more details.
- {UncountablePythonSDK-0.0.16.dist-info → UncountablePythonSDK-0.0.18.dist-info}/METADATA +14 -1
- {UncountablePythonSDK-0.0.16.dist-info → UncountablePythonSDK-0.0.18.dist-info}/RECORD +81 -33
- {UncountablePythonSDK-0.0.16.dist-info → UncountablePythonSDK-0.0.18.dist-info}/top_level.txt +1 -0
- docs/.gitignore +1 -0
- docs/conf.py +52 -0
- docs/index.md +13 -0
- docs/justfile +12 -0
- docs/quickstart.md +19 -0
- docs/requirements.txt +7 -0
- docs/static/favicons/android-chrome-192x192.png +0 -0
- docs/static/favicons/android-chrome-512x512.png +0 -0
- docs/static/favicons/apple-touch-icon.png +0 -0
- docs/static/favicons/browserconfig.xml +9 -0
- docs/static/favicons/favicon-16x16.png +0 -0
- docs/static/favicons/favicon-32x32.png +0 -0
- docs/static/favicons/manifest.json +18 -0
- docs/static/favicons/mstile-150x150.png +0 -0
- docs/static/favicons/safari-pinned-tab.svg +32 -0
- docs/static/logo_blue.png +0 -0
- examples/create_entity.py +23 -16
- pkgs/argument_parser/_is_enum.py +1 -1
- pkgs/argument_parser/argument_parser.py +26 -19
- pkgs/serialization/serial_class.py +3 -3
- pkgs/serialization_util/_get_type_for_serialization.py +1 -3
- pkgs/serialization_util/serialization_helpers.py +1 -3
- pkgs/strenum_compat/strenum_compat.py +1 -9
- pkgs/type_spec/actions_registry/__init__.py +0 -0
- pkgs/type_spec/actions_registry/__main__.py +114 -0
- pkgs/type_spec/actions_registry/emit_typescript.py +120 -0
- pkgs/type_spec/builder.py +14 -10
- pkgs/type_spec/config.py +3 -2
- pkgs/type_spec/emit_python.py +54 -17
- pkgs/type_spec/emit_typescript.py +8 -9
- pkgs/type_spec/emit_typescript_util.py +1 -2
- pkgs/type_spec/load_types.py +2 -1
- pkgs/type_spec/open_api_util.py +2 -2
- pkgs/type_spec/parts/base.py.prepart +2 -1
- pkgs/type_spec/util.py +9 -9
- pkgs/type_spec/value_spec/__main__.py +2 -2
- pkgs/type_spec/value_spec/emit_python.py +1 -0
- type_spec/external/api/batch/execute_batch_load_async.yaml +18 -0
- type_spec/external/api/chemical/convert_chemical_formats.yaml +33 -0
- type_spec/external/api/entity/create_entities.yaml +1 -1
- type_spec/external/api/entity/create_entity.yaml +1 -1
- type_spec/external/api/id_source/list_id_source.yaml +35 -0
- type_spec/external/api/id_source/match_id_source.yaml +32 -0
- type_spec/external/api/recipe_links/create_recipe_link.yaml +25 -0
- type_spec/external/api/recipes/associate_recipe_as_input.yaml +19 -0
- type_spec/external/api/recipes/associate_recipe_as_lot.yaml +19 -0
- type_spec/external/api/recipes/create_recipe.yaml +38 -0
- type_spec/external/api/recipes/get_recipes_data.yaml +21 -21
- type_spec/external/api/recipes/set_recipe_inputs.yaml +6 -0
- type_spec/external/api/recipes/set_recipe_metadata.yaml +19 -0
- type_spec/external/api/triggers/run_trigger.yaml +18 -0
- uncountable/core/client.py +13 -14
- uncountable/types/__init__.py +30 -0
- uncountable/types/api/batch/execute_batch_load_async.py +35 -0
- uncountable/types/api/chemical/__init__.py +1 -0
- uncountable/types/api/chemical/convert_chemical_formats.py +50 -0
- uncountable/types/api/entity/create_entities.py +1 -1
- uncountable/types/api/entity/create_entity.py +1 -1
- uncountable/types/api/id_source/__init__.py +1 -0
- uncountable/types/api/id_source/list_id_source.py +46 -0
- uncountable/types/api/id_source/match_id_source.py +48 -0
- uncountable/types/api/recipe_links/__init__.py +1 -0
- uncountable/types/api/recipe_links/create_recipe_link.py +39 -0
- uncountable/types/api/recipes/associate_recipe_as_input.py +35 -0
- uncountable/types/api/recipes/associate_recipe_as_lot.py +36 -0
- uncountable/types/api/recipes/create_recipe.py +43 -0
- uncountable/types/api/recipes/set_recipe_inputs.py +2 -0
- uncountable/types/api/recipes/set_recipe_metadata.py +36 -0
- uncountable/types/api/triggers/__init__.py +1 -0
- uncountable/types/api/triggers/run_trigger.py +36 -0
- uncountable/types/async_batch.py +45 -0
- uncountable/types/base.py +2 -1
- uncountable/types/chemical_structure.py +27 -0
- uncountable/types/client_base.py +404 -2
- uncountable/types/id_source.py +49 -0
- uncountable/types/identifier.py +54 -0
- uncountable/types/recipe_identifiers.py +62 -0
- {UncountablePythonSDK-0.0.16.dist-info → UncountablePythonSDK-0.0.18.dist-info}/WHEEL +0 -0
pkgs/type_spec/util.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Optional, TypeVar, Union
|
|
5
5
|
|
|
6
6
|
import regex as re
|
|
7
7
|
|
|
@@ -29,11 +29,11 @@ LiteralTypeValue = Union[str, bool]
|
|
|
29
29
|
class ParsedTypePart:
|
|
30
30
|
name: str
|
|
31
31
|
# An empty list is distinct from None
|
|
32
|
-
parameters: Optional[
|
|
32
|
+
parameters: Optional[list["ParsedTypePath"]] = None
|
|
33
33
|
literal_value: Optional[LiteralTypeValue] = None
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
ParsedTypePath =
|
|
36
|
+
ParsedTypePath = list[ParsedTypePart]
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
@dataclass
|
|
@@ -48,7 +48,7 @@ def consume_parameter(
|
|
|
48
48
|
) -> ConsumedParameter:
|
|
49
49
|
if bits[at] != "'":
|
|
50
50
|
return ConsumedParameter(at=at, part=ParsedTypePart(name=bits[at]))
|
|
51
|
-
quote_stack:
|
|
51
|
+
quote_stack: list[str] = []
|
|
52
52
|
at += 1
|
|
53
53
|
while at < len(bits):
|
|
54
54
|
if bits[at] == "'":
|
|
@@ -68,7 +68,7 @@ def parse_type_str(type_str: str) -> ParsedTypePath:
|
|
|
68
68
|
"""
|
|
69
69
|
IMPROVE: will not detect all errors yet, focuses on correct cases
|
|
70
70
|
"""
|
|
71
|
-
raw_bits:
|
|
71
|
+
raw_bits: list[str] = re.split(r"([.<>,'])", type_str)
|
|
72
72
|
bits = [
|
|
73
73
|
stripped_bit
|
|
74
74
|
for stripped_bit in (padded_bit.strip() for padded_bit in raw_bits)
|
|
@@ -81,7 +81,7 @@ def parse_type_str(type_str: str) -> ParsedTypePath:
|
|
|
81
81
|
cur_path = result
|
|
82
82
|
cur_path.append(cur_part)
|
|
83
83
|
|
|
84
|
-
path_stack:
|
|
84
|
+
path_stack: list[ParsedTypePath] = []
|
|
85
85
|
|
|
86
86
|
at = 1
|
|
87
87
|
while at < len(bits):
|
|
@@ -156,19 +156,19 @@ def is_valid_property_name(name: str) -> bool:
|
|
|
156
156
|
return re_pattern_property_name.match(name) is not None
|
|
157
157
|
|
|
158
158
|
|
|
159
|
-
def check_fields(data:
|
|
159
|
+
def check_fields(data: dict[str, T], allowed: list[str]) -> None:
|
|
160
160
|
for key in data:
|
|
161
161
|
if key not in allowed:
|
|
162
162
|
raise Exception(f"unexpected-field: {key}")
|
|
163
163
|
|
|
164
164
|
|
|
165
|
-
def split_any_name(name: str) ->
|
|
165
|
+
def split_any_name(name: str) -> list[str]:
|
|
166
166
|
"""
|
|
167
167
|
Splits a name on case and underscores.
|
|
168
168
|
myName => [my, name]
|
|
169
169
|
my_name => [my, name]
|
|
170
170
|
"""
|
|
171
|
-
bits:
|
|
171
|
+
bits: list[str] = re_pattern_split_name.split(name)
|
|
172
172
|
return [s.lower() for s in filter(lambda x: x is not None and x != "_", bits)]
|
|
173
173
|
|
|
174
174
|
|
|
@@ -17,7 +17,7 @@ If null is allowed as a legitimate value, such as in conditionals like `if`, the
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
import sys
|
|
20
|
-
from typing import
|
|
20
|
+
from typing import TypeVar, cast
|
|
21
21
|
|
|
22
22
|
import regex as re
|
|
23
23
|
import yaml
|
|
@@ -177,7 +177,7 @@ def main() -> None:
|
|
|
177
177
|
raise Exception(f"missing-{key}:{get_where()}")
|
|
178
178
|
return cast(base_t.PureJsonValue, x)
|
|
179
179
|
|
|
180
|
-
def get_as(node: base_t.PureJsonValue, key: str, type_:
|
|
180
|
+
def get_as(node: base_t.PureJsonValue, key: str, type_: type[TypeT]) -> TypeT:
|
|
181
181
|
raw = get(node, key)
|
|
182
182
|
assert isinstance(raw, type_)
|
|
183
183
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/batch/execute_batch_load_async
|
|
5
|
+
function: main.site.app.external.batch.execute_batch_load_async.execute_batch_load_async
|
|
6
|
+
desc: Run multiple API calls via one request
|
|
7
|
+
|
|
8
|
+
Arguments:
|
|
9
|
+
type: Object
|
|
10
|
+
properties:
|
|
11
|
+
requests:
|
|
12
|
+
type: List<async_batch.AsyncBatchRequest>
|
|
13
|
+
|
|
14
|
+
Data:
|
|
15
|
+
type: Object
|
|
16
|
+
properties:
|
|
17
|
+
job_id:
|
|
18
|
+
type: ObjectId
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/chemical/convert_chemical_formats
|
|
5
|
+
function: main.site.app.external.chemical.convert_chemical_formats.convert_chemical_formats
|
|
6
|
+
desc: Converts chemical formats, into the format used by Uncountable and usable in other APIs for eg. set_input_attribute_values
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
ChemicalStructureFile:
|
|
10
|
+
type: Object
|
|
11
|
+
properties:
|
|
12
|
+
struct_file:
|
|
13
|
+
type: String
|
|
14
|
+
desc: Stringified chemical structure. MOL v2000/3000, SMILES, Part of SDF.
|
|
15
|
+
|
|
16
|
+
Arguments:
|
|
17
|
+
type: Object
|
|
18
|
+
properties:
|
|
19
|
+
source_chemical_structures:
|
|
20
|
+
type: List<ChemicalStructureFile>
|
|
21
|
+
|
|
22
|
+
UncountableChemicalStructure:
|
|
23
|
+
type: Alias
|
|
24
|
+
alias: Dict<String, JsonValue>
|
|
25
|
+
desc: Uncountable format for chemical structures compatible with set ingredient attributes api etc.
|
|
26
|
+
|
|
27
|
+
Data:
|
|
28
|
+
type: Object
|
|
29
|
+
properties:
|
|
30
|
+
chemical_structures:
|
|
31
|
+
type: List<UncountableChemicalStructure>
|
|
32
|
+
desc: The parsed chemical structures in the Uncountable format
|
|
33
|
+
convert_value: no_convert
|
|
@@ -19,7 +19,7 @@ Arguments:
|
|
|
19
19
|
type: ObjectId
|
|
20
20
|
desc: "Definition id for the entities to create"
|
|
21
21
|
entity_type:
|
|
22
|
-
type: Union<Literal<entity.EntityType.lab_request>, Literal<entity.EntityType.approval>, Literal<entity.EntityType.custom_entity>, Literal<entity.EntityType.task>, Literal<entity.EntityType.project>>
|
|
22
|
+
type: Union<Literal<entity.EntityType.lab_request>, Literal<entity.EntityType.approval>, Literal<entity.EntityType.custom_entity>, Literal<entity.EntityType.task>, Literal<entity.EntityType.project>, Literal<entity.EntityType.equipment>, Literal<entity.EntityType.inv_local_locations>>
|
|
23
23
|
desc: "The type of the entities to create"
|
|
24
24
|
entities_to_create:
|
|
25
25
|
type: List<EntityToCreate>
|
|
@@ -26,7 +26,7 @@ Arguments:
|
|
|
26
26
|
type: ObjectId
|
|
27
27
|
desc: "Definition id of the entity to create"
|
|
28
28
|
entity_type:
|
|
29
|
-
type: Union<Literal<entity.EntityType.lab_request>, Literal<entity.EntityType.approval>, Literal<entity.EntityType.custom_entity>, Literal<entity.EntityType.task>, Literal<entity.EntityType.project>>
|
|
29
|
+
type: Union<Literal<entity.EntityType.lab_request>, Literal<entity.EntityType.approval>, Literal<entity.EntityType.custom_entity>, Literal<entity.EntityType.task>, Literal<entity.EntityType.project>, Literal<entity.EntityType.equipment>, Literal<entity.EntityType.inv_local_locations>>
|
|
30
30
|
desc: "The type of the entities requested"
|
|
31
31
|
field_values?:
|
|
32
32
|
type: Optional<List<field_values.FieldRefNameValue>>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: get
|
|
4
|
+
path: ${external}/id_source/list_id_source
|
|
5
|
+
function: main.site.app.external.id_source.list_id_source.list_id_source
|
|
6
|
+
desc: "Lists id and label pairs"
|
|
7
|
+
Arguments:
|
|
8
|
+
type: Object
|
|
9
|
+
properties:
|
|
10
|
+
spec:
|
|
11
|
+
type: id_source.IdSourceSpec
|
|
12
|
+
desc: "The id source spec to use"
|
|
13
|
+
search_label:
|
|
14
|
+
type: String
|
|
15
|
+
desc: Text to search within the labels to search matches
|
|
16
|
+
offset?:
|
|
17
|
+
type: Optional<Integer>
|
|
18
|
+
desc: "Used for pagination. Pagination is done based on the sorting of the config"
|
|
19
|
+
limit?:
|
|
20
|
+
type: Optional<Integer>
|
|
21
|
+
desc: "The number of data points to return. If not filled in, it will be set to 100, and cannot be set higher than 100"
|
|
22
|
+
|
|
23
|
+
IdName:
|
|
24
|
+
type: Object
|
|
25
|
+
properties:
|
|
26
|
+
id:
|
|
27
|
+
type: Union<ObjectId,String>
|
|
28
|
+
name:
|
|
29
|
+
type: String
|
|
30
|
+
|
|
31
|
+
Data:
|
|
32
|
+
type: Object
|
|
33
|
+
properties:
|
|
34
|
+
results:
|
|
35
|
+
type: List<IdName>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: get
|
|
4
|
+
path: ${external}/id_source/match_id_source
|
|
5
|
+
function: main.site.app.external.id_source.match_id_source.match_id_source
|
|
6
|
+
desc: "Lists id and label pairs"
|
|
7
|
+
Arguments:
|
|
8
|
+
type: Object
|
|
9
|
+
properties:
|
|
10
|
+
spec:
|
|
11
|
+
type: id_source.IdSourceSpec
|
|
12
|
+
desc: "The id source spec to use"
|
|
13
|
+
names:
|
|
14
|
+
type: List<String>
|
|
15
|
+
desc: "The names to match with the id source. At most 10 are allowed at a time"
|
|
16
|
+
|
|
17
|
+
Match:
|
|
18
|
+
type: Object
|
|
19
|
+
properties:
|
|
20
|
+
name:
|
|
21
|
+
type: String
|
|
22
|
+
desc: The name used to match
|
|
23
|
+
ids:
|
|
24
|
+
type: List<Union<ObjectId, String>>
|
|
25
|
+
desc: The list of matches
|
|
26
|
+
|
|
27
|
+
Data:
|
|
28
|
+
type: Object
|
|
29
|
+
properties:
|
|
30
|
+
results:
|
|
31
|
+
type: List<Match>
|
|
32
|
+
convert_value: no_convert
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/recipe_links/create_recipe_link
|
|
5
|
+
function: main.site.app.external.recipe_links.create_recipe_link.create_recipe_link
|
|
6
|
+
desc: Create a link between two recipes. Skip if the link already exists
|
|
7
|
+
|
|
8
|
+
Arguments:
|
|
9
|
+
type: Object
|
|
10
|
+
properties:
|
|
11
|
+
recipe_from_key:
|
|
12
|
+
type: identifier.IdentifierKey
|
|
13
|
+
desc: "Identifier for the recipe the link comes from"
|
|
14
|
+
recipe_to_key:
|
|
15
|
+
type: identifier.IdentifierKey
|
|
16
|
+
desc: "Identifier for the recipe the link goes to"
|
|
17
|
+
link_type:
|
|
18
|
+
type: Union<Literal<recipe_links.RecipeLinkType.child>, Literal<recipe_links.RecipeLinkType.control>, Literal<recipe_links.RecipeLinkType.user_link>>
|
|
19
|
+
desc: "The type of link being created"
|
|
20
|
+
name:
|
|
21
|
+
type: String
|
|
22
|
+
desc: "The name used for the link"
|
|
23
|
+
|
|
24
|
+
Data:
|
|
25
|
+
type: async_batch.AsyncBatchActionReturn
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/recipes/associate_recipe_as_input
|
|
5
|
+
function: main.site.app.external.recipes.associate_recipe_as_input.associate_recipe_as_input
|
|
6
|
+
desc: Create or return the input association for a recipe
|
|
7
|
+
|
|
8
|
+
Arguments:
|
|
9
|
+
type: Object
|
|
10
|
+
properties:
|
|
11
|
+
recipe_key:
|
|
12
|
+
type: identifier.IdentifierKey
|
|
13
|
+
desc: "Identifier for the recipe"
|
|
14
|
+
|
|
15
|
+
Data:
|
|
16
|
+
type: Object
|
|
17
|
+
properties:
|
|
18
|
+
result_id:
|
|
19
|
+
type: ObjectId
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/recipes/associate_recipe_as_lot
|
|
5
|
+
function: main.site.app.external.recipes.associate_recipe_as_lot.associate_recipe_as_lot
|
|
6
|
+
desc: Create a new lot association for the provided recipe with the provided ingredient
|
|
7
|
+
|
|
8
|
+
Arguments:
|
|
9
|
+
type: Object
|
|
10
|
+
properties:
|
|
11
|
+
recipe_key:
|
|
12
|
+
type: identifier.IdentifierKey
|
|
13
|
+
desc: "Identifier for the recipe"
|
|
14
|
+
ingredient_key:
|
|
15
|
+
type: identifier.IdentifierKey
|
|
16
|
+
desc: "Identifier for the ingredient"
|
|
17
|
+
|
|
18
|
+
Data:
|
|
19
|
+
type: async_batch.AsyncBatchActionReturn
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/recipes/create_recipe
|
|
5
|
+
function: main.site.app.external.recipes.create_recipe.create_recipe
|
|
6
|
+
desc: Returns the id of the recipe being created.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Arguments:
|
|
10
|
+
type: Object
|
|
11
|
+
properties:
|
|
12
|
+
name?:
|
|
13
|
+
type: String
|
|
14
|
+
desc: The name for the recipe
|
|
15
|
+
material_family_id:
|
|
16
|
+
type: ObjectId
|
|
17
|
+
desc: The material family for the recipe
|
|
18
|
+
workflow_id:
|
|
19
|
+
type: ObjectId
|
|
20
|
+
desc: The identifier of the workflow to create the recipe with
|
|
21
|
+
workflow_variant_id?:
|
|
22
|
+
type: Optional<ObjectId>
|
|
23
|
+
desc: The identifier of the workflow variant to create the recipe with
|
|
24
|
+
recipe_metadata?:
|
|
25
|
+
type: List<recipe_metadata.MetadataValue>
|
|
26
|
+
desc: Metadata values to populate the recipe with
|
|
27
|
+
identifiers:
|
|
28
|
+
type: recipe_identifiers.RecipeIdentifiers
|
|
29
|
+
desc: A recipe won't be created if it matches the identifier. An identifier must be unique in the schema
|
|
30
|
+
definition_key?:
|
|
31
|
+
type: identifier.IdentifierKey
|
|
32
|
+
desc: The entity definition identifier, default is used if not supplied
|
|
33
|
+
|
|
34
|
+
Data:
|
|
35
|
+
type: Object
|
|
36
|
+
properties:
|
|
37
|
+
result_id:
|
|
38
|
+
type: ObjectId
|
|
@@ -3,7 +3,7 @@ $endpoint:
|
|
|
3
3
|
method: get
|
|
4
4
|
path: ${external}/recipes/external_get_recipes_data
|
|
5
5
|
function: main.site.app.external.recipes.get_recipes_data.get_recipes_data
|
|
6
|
-
desc: "Gets all data associated with a set of recipes
|
|
6
|
+
desc: "Gets all data associated with a set of recipes. Because Uncountables recipe structure is complex, various data values are exploded out to increase efficiency in parsing, and this page is paginated to prevent too large of return values"
|
|
7
7
|
Arguments:
|
|
8
8
|
type: Object
|
|
9
9
|
properties:
|
|
@@ -22,7 +22,7 @@ Arguments:
|
|
|
22
22
|
|
|
23
23
|
RecipeOutput:
|
|
24
24
|
type: Object
|
|
25
|
-
desc: "A representation of a single measurement associated with a recipe
|
|
25
|
+
desc: "A representation of a single measurement associated with a recipe within Uncountable system"
|
|
26
26
|
properties:
|
|
27
27
|
id:
|
|
28
28
|
type: ObjectId
|
|
@@ -113,7 +113,7 @@ RecipeStep:
|
|
|
113
113
|
desc: "An optional name for the recipe step"
|
|
114
114
|
recipe_step_id:
|
|
115
115
|
type: ObjectId
|
|
116
|
-
desc: "An ID value for the recipe step, used elsewhere to reference the part of the recipe
|
|
116
|
+
desc: "An ID value for the recipe step, used elsewhere to reference the part of the recipe that the input was placed in"
|
|
117
117
|
recipe_step_number:
|
|
118
118
|
type: Integer
|
|
119
119
|
desc: "An ordering for the recipe step, allowing the user to determine the ordering of the steps"
|
|
@@ -126,24 +126,24 @@ RecipeStepGroup:
|
|
|
126
126
|
properties:
|
|
127
127
|
name:
|
|
128
128
|
type: Optional<String>
|
|
129
|
-
desc: "An optional name for the recipe step group. These names are on a per recipe basis, allowing users to individually name sub-components of recipes
|
|
129
|
+
desc: "An optional name for the recipe step group. These names are on a per recipe basis, allowing users to individually name sub-components of recipes"
|
|
130
130
|
recipe_steps:
|
|
131
131
|
type: List<RecipeStep>
|
|
132
|
-
desc: "A listing of recipe steps. Recipe Steps are the base component in
|
|
132
|
+
desc: "A listing of recipe steps. Recipe Steps are the base component in Uncountable's recipe structure, where inputs are directly placed"
|
|
133
133
|
|
|
134
134
|
RecipeWorkflowStep:
|
|
135
135
|
type: Object
|
|
136
|
-
desc: "A workflow step associated with a recipe
|
|
136
|
+
desc: "A workflow step associated with a recipe. Workflow steps are comprised of recipe step groups, in turn comprised of recipe steps, where inputs are located. These groupings exists to give customers the ability to flexibly place inputs into the correct process ordering"
|
|
137
137
|
properties:
|
|
138
138
|
recipe_workflow_step_id:
|
|
139
139
|
type: ObjectId
|
|
140
140
|
desc: "A unique reference for the workflow step within the recipe."
|
|
141
141
|
workflow_step_id:
|
|
142
142
|
type: ObjectId
|
|
143
|
-
desc: "A reference to the workflow_step_id for the workflow. This will be shared across recipes
|
|
143
|
+
desc: "A reference to the workflow_step_id for the workflow. This will be shared across recipes, and can be used to do analysis about what part of a generic process inputs are located in."
|
|
144
144
|
recipe_step_groups:
|
|
145
145
|
type: List<RecipeStepGroup>
|
|
146
|
-
desc: "A listing of recipe step groups within the recipe
|
|
146
|
+
desc: "A listing of recipe step groups within the recipe"
|
|
147
147
|
|
|
148
148
|
RecipeStepRelationship:
|
|
149
149
|
type: Object
|
|
@@ -172,46 +172,46 @@ Recipe:
|
|
|
172
172
|
properties:
|
|
173
173
|
recipe_id:
|
|
174
174
|
type: ObjectId
|
|
175
|
-
desc: "A unique identifier for the recipe
|
|
175
|
+
desc: "A unique identifier for the recipe. Used for joining elsewhere"
|
|
176
176
|
creating_user_id?:
|
|
177
177
|
type: ObjectId
|
|
178
|
-
desc: "The user ID who originally created the recipe
|
|
178
|
+
desc: "The user ID who originally created the recipe. Null when created from automated scripts, such as data transfers, or from the Uncountable implementation team."
|
|
179
179
|
create_datetime:
|
|
180
180
|
type: String
|
|
181
|
-
desc: "When the recipe
|
|
181
|
+
desc: "When the recipe was created"
|
|
182
182
|
last_modified_datetime:
|
|
183
183
|
type: String
|
|
184
|
-
desc: "when the recipe
|
|
184
|
+
desc: "when the recipe was last modified"
|
|
185
185
|
name:
|
|
186
186
|
type: String
|
|
187
|
-
desc: "The full name in the system of the recipe
|
|
187
|
+
desc: "The full name in the system of the recipe"
|
|
188
188
|
notes:
|
|
189
189
|
type: Optional<String>
|
|
190
|
-
desc: "Any notes associated with the recipe
|
|
190
|
+
desc: "Any notes associated with the recipe. This may be null if there are no notes present"
|
|
191
191
|
barcode_value?:
|
|
192
192
|
type: String
|
|
193
193
|
desc: "The value used in the barcoding system to lookup this experiment."
|
|
194
194
|
workflow_id:
|
|
195
195
|
type: ObjectId
|
|
196
|
-
desc: "The workflow ID associated with the recipe
|
|
196
|
+
desc: "The workflow ID associated with the recipe. Workflows correspond to a set of experimental steps performed, and are referenced in the workflows return object"
|
|
197
197
|
metadata:
|
|
198
198
|
type: List<recipe_metadata.RecipeMetadata>
|
|
199
|
-
desc: "Metadata associated with a recipe
|
|
199
|
+
desc: "Metadata associated with a recipe. Metadata includes values that are neither ingredients nor process parameters, such as a location of an experiment"
|
|
200
200
|
inputs:
|
|
201
201
|
type: List<RecipeInput>
|
|
202
|
-
desc: "Inputs and quantities of those inputs associated with a recipe
|
|
202
|
+
desc: "Inputs and quantities of those inputs associated with a recipe. Uncountable refers to inputs as either ingredients or process parameters."
|
|
203
203
|
outputs:
|
|
204
204
|
type: List<RecipeOutput>
|
|
205
|
-
desc: "Outputs and quantities of those outputs associated with a recipe
|
|
205
|
+
desc: "Outputs and quantities of those outputs associated with a recipe. These can be of any form referred to in the Uncountable system."
|
|
206
206
|
workflow_steps:
|
|
207
207
|
type: List<RecipeWorkflowStep>
|
|
208
|
-
desc: "A reference of workflow steps in the recipe
|
|
208
|
+
desc: "A reference of workflow steps in the recipe. This is used to reference input values to where they occurred in the experimental process."
|
|
209
209
|
tag_ids:
|
|
210
210
|
type: List<ObjectId>
|
|
211
|
-
desc: A list of Tag IDs associated with the recipe
|
|
211
|
+
desc: A list of Tag IDs associated with the recipe
|
|
212
212
|
experiment_group_ids:
|
|
213
213
|
type: List<ObjectId>
|
|
214
|
-
desc: A list of experiment group IDs associated with the recipe
|
|
214
|
+
desc: A list of experiment group IDs associated with the recipe
|
|
215
215
|
step_relationships:
|
|
216
216
|
type: List<RecipeStepRelationship>
|
|
217
217
|
|
|
@@ -25,6 +25,12 @@ RecipeInputValue:
|
|
|
25
25
|
set_actual_value?:
|
|
26
26
|
type: Boolean
|
|
27
27
|
desc: "If True, modify the actual value for the input. If not provided or False, modify the set value for the input."
|
|
28
|
+
lot_recipe_id?:
|
|
29
|
+
type: ObjectId
|
|
30
|
+
desc: The recipe id for a lot to be associated to this recipe input. If the recipe is not a lot, it will be created as a lot.
|
|
31
|
+
remove?:
|
|
32
|
+
type: Boolean
|
|
33
|
+
desc: When true will remove the input from the recipe
|
|
28
34
|
|
|
29
35
|
Arguments:
|
|
30
36
|
type: Object
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/recipes/set_recipe_metadata
|
|
5
|
+
function: main.site.app.external.recipes.set_recipe_metadata.set_recipe_metadata
|
|
6
|
+
desc: "Set metadata values on a recipe"
|
|
7
|
+
|
|
8
|
+
Arguments:
|
|
9
|
+
type: Object
|
|
10
|
+
properties:
|
|
11
|
+
recipe_key:
|
|
12
|
+
type: identifier.IdentifierKey
|
|
13
|
+
desc: "Identifier for the recipe"
|
|
14
|
+
recipe_metadata:
|
|
15
|
+
type: List<recipe_metadata.MetadataValue>
|
|
16
|
+
desc: "Metadata values to populate the recipe with"
|
|
17
|
+
|
|
18
|
+
Data:
|
|
19
|
+
type: Object
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/triggers/run_trigger
|
|
5
|
+
function: main.site.app.external.triggers.run_trigger.run_trigger
|
|
6
|
+
desc: "Runs a trigger. Requires admin access"
|
|
7
|
+
|
|
8
|
+
Arguments:
|
|
9
|
+
type: Object
|
|
10
|
+
properties:
|
|
11
|
+
entity?:
|
|
12
|
+
type: entity.Entity
|
|
13
|
+
trigger_ref_name:
|
|
14
|
+
type: String
|
|
15
|
+
|
|
16
|
+
Data:
|
|
17
|
+
type: response.Response
|
|
18
|
+
properties:
|
uncountable/core/client.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import typing
|
|
1
4
|
from dataclasses import dataclass
|
|
2
5
|
from enum import StrEnum
|
|
3
|
-
import json
|
|
4
6
|
from urllib.parse import urljoin
|
|
5
|
-
|
|
6
|
-
import typing
|
|
7
|
+
|
|
7
8
|
import requests
|
|
8
|
-
|
|
9
|
-
from uncountable.types.client_base import APIRequest, ClientMethods
|
|
9
|
+
|
|
10
10
|
from pkgs.argument_parser import CachedParser
|
|
11
11
|
from pkgs.serialization_util import serialize_for_api
|
|
12
|
-
|
|
12
|
+
from uncountable.types.client_base import APIRequest, ClientMethods
|
|
13
13
|
|
|
14
14
|
DT = typing.TypeVar("DT")
|
|
15
15
|
|
|
@@ -24,8 +24,8 @@ class HTTPRequestBase:
|
|
|
24
24
|
method: EndpointMethod
|
|
25
25
|
url: str
|
|
26
26
|
headers: dict[str, str]
|
|
27
|
-
body: typing.Optional[str] = None
|
|
28
|
-
query_params: typing.Optional[dict[str, str]]
|
|
27
|
+
body: typing.Optional[typing.Union[str, dict[str, str]]] = None
|
|
28
|
+
query_params: typing.Optional[dict[str, str]] = None
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@dataclass(kw_only=True)
|
|
@@ -37,6 +37,7 @@ class HTTPGetRequest(HTTPRequestBase):
|
|
|
37
37
|
@dataclass(kw_only=True)
|
|
38
38
|
class HTTPPostRequest(HTTPRequestBase):
|
|
39
39
|
method: typing.Literal[EndpointMethod.POST]
|
|
40
|
+
body: typing.Union[str, dict[str, str]]
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
HTTPRequest = HTTPPostRequest | HTTPGetRequest
|
|
@@ -98,9 +99,7 @@ class Client(ClientMethods):
|
|
|
98
99
|
match self._auth_details:
|
|
99
100
|
case AuthDetailsApiKey():
|
|
100
101
|
encoded = base64.standard_b64encode(
|
|
101
|
-
f"{self._auth_details.api_id}:{self._auth_details.api_secret_key}".encode(
|
|
102
|
-
"utf-8"
|
|
103
|
-
)
|
|
102
|
+
f"{self._auth_details.api_id}:{self._auth_details.api_secret_key}".encode()
|
|
104
103
|
).decode("utf-8")
|
|
105
104
|
return {"Authorization": f"Basic {encoded}"}
|
|
106
105
|
typing.assert_never(self._auth_details)
|
|
@@ -108,21 +107,21 @@ class Client(ClientMethods):
|
|
|
108
107
|
def _build_http_request(self, *, api_request: APIRequest) -> HTTPRequest:
|
|
109
108
|
headers = self._build_auth_headers()
|
|
110
109
|
method = api_request.method.lower()
|
|
111
|
-
|
|
110
|
+
data = {"data": json.dumps(serialize_for_api(api_request.args))}
|
|
112
111
|
match method:
|
|
113
112
|
case "get":
|
|
114
113
|
return HTTPGetRequest(
|
|
115
114
|
method=EndpointMethod.GET,
|
|
116
115
|
url=urljoin(self._base_url, api_request.endpoint),
|
|
117
|
-
query_params=
|
|
116
|
+
query_params=data,
|
|
118
117
|
headers=headers,
|
|
119
118
|
)
|
|
120
119
|
case "post":
|
|
121
120
|
return HTTPPostRequest(
|
|
122
121
|
method=EndpointMethod.POST,
|
|
123
122
|
url=urljoin(self._base_url, api_request.endpoint),
|
|
123
|
+
body=data,
|
|
124
124
|
headers=headers,
|
|
125
|
-
query_params=query_params
|
|
126
125
|
)
|
|
127
126
|
case _:
|
|
128
127
|
raise ValueError(f"unsupported request method: {method}")
|