UncountablePythonSDK 0.0.20__py3-none-any.whl → 0.0.22__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.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/METADATA +3 -1
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/RECORD +55 -34
- examples/async_batch.py +36 -0
- examples/upload_files.py +19 -0
- pkgs/type_spec/actions_registry/__main__.py +35 -23
- pkgs/type_spec/actions_registry/emit_typescript.py +71 -9
- pkgs/type_spec/builder.py +125 -8
- pkgs/type_spec/config.py +1 -0
- pkgs/type_spec/emit_open_api.py +197 -16
- pkgs/type_spec/emit_open_api_util.py +18 -0
- pkgs/type_spec/emit_python.py +241 -55
- pkgs/type_spec/load_types.py +48 -5
- pkgs/type_spec/open_api_util.py +13 -33
- pkgs/type_spec/type_info/emit_type_info.py +129 -8
- type_spec/external/api/entity/create_entities.yaml +13 -1
- type_spec/external/api/entity/create_entity.yaml +13 -1
- type_spec/external/api/entity/transition_entity_phase.yaml +44 -0
- type_spec/external/api/permissions/set_core_permissions.yaml +69 -0
- type_spec/external/api/recipes/associate_recipe_as_input.yaml +4 -4
- type_spec/external/api/recipes/create_recipe.yaml +2 -1
- type_spec/external/api/recipes/disassociate_recipe_as_input.yaml +16 -0
- type_spec/external/api/recipes/edit_recipe_inputs.yaml +86 -0
- type_spec/external/api/recipes/get_curve.yaml +4 -1
- type_spec/external/api/recipes/get_recipes_data.yaml +6 -0
- type_spec/external/api/recipes/set_recipe_metadata.yaml +1 -0
- type_spec/external/api/recipes/set_recipe_tags.yaml +62 -0
- uncountable/core/__init__.py +3 -1
- uncountable/core/async_batch.py +22 -0
- uncountable/core/client.py +84 -10
- uncountable/core/file_upload.py +95 -0
- uncountable/core/types.py +22 -0
- uncountable/types/__init__.py +18 -0
- uncountable/types/api/entity/create_entities.py +1 -1
- uncountable/types/api/entity/create_entity.py +1 -1
- uncountable/types/api/entity/transition_entity_phase.py +66 -0
- uncountable/types/api/permissions/__init__.py +1 -0
- uncountable/types/api/permissions/set_core_permissions.py +89 -0
- uncountable/types/api/recipes/associate_recipe_as_input.py +4 -3
- uncountable/types/api/recipes/create_recipe.py +1 -1
- uncountable/types/api/recipes/disassociate_recipe_as_input.py +35 -0
- uncountable/types/api/recipes/edit_recipe_inputs.py +106 -0
- uncountable/types/api/recipes/get_curve.py +2 -1
- uncountable/types/api/recipes/get_recipes_data.py +2 -0
- uncountable/types/api/recipes/set_recipe_tags.py +91 -0
- uncountable/types/async_batch.py +10 -0
- uncountable/types/async_batch_processor.py +154 -0
- uncountable/types/client_base.py +113 -48
- uncountable/types/identifier.py +3 -3
- uncountable/types/permissions.py +46 -0
- uncountable/types/post_base.py +30 -0
- uncountable/types/recipe_inputs.py +30 -0
- uncountable/types/recipe_metadata.py +2 -0
- uncountable/types/recipe_workflow_steps.py +77 -0
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/WHEEL +0 -0
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/top_level.txt +0 -0
|
@@ -23,7 +23,7 @@ def type_path_of(stype: builder.SpecType) -> object: # NamePath
|
|
|
23
23
|
extended scopes, generics, and enum literal values.
|
|
24
24
|
- Scoped Type: [ (namespace-string)..., type-string ]
|
|
25
25
|
- Instance Type: [ "$instance", Scoped-Type-Base, [TypePath-Parameters...] ]
|
|
26
|
-
- Literal Type: [ "$literal", [ "$value", value ]... ]
|
|
26
|
+
- Literal Type: [ "$literal", [ "$value", value, value-type-string ]... ]
|
|
27
27
|
|
|
28
28
|
@return (string-specific, multiple-types)
|
|
29
29
|
"""
|
|
@@ -34,11 +34,15 @@ def type_path_of(stype: builder.SpecType) -> object: # NamePath
|
|
|
34
34
|
|
|
35
35
|
if isinstance(stype, builder.SpecTypeInstance):
|
|
36
36
|
if stype.defn_type.name == builder.BaseTypeName.s_literal:
|
|
37
|
-
parts: list[
|
|
37
|
+
parts: list[object] = ["$literal"]
|
|
38
38
|
for parameter in stype.parameters:
|
|
39
39
|
assert isinstance(parameter, builder.SpecTypeLiteralWrapper)
|
|
40
40
|
# This allows expansion to enum literal values later
|
|
41
|
-
parts.append([
|
|
41
|
+
parts.append([
|
|
42
|
+
"$value",
|
|
43
|
+
parameter.value,
|
|
44
|
+
type_path_of(parameter.value_type),
|
|
45
|
+
])
|
|
42
46
|
return parts
|
|
43
47
|
|
|
44
48
|
return [
|
|
@@ -148,6 +152,114 @@ def _build_map_all(build: builder.SpecBuilder) -> MapAll:
|
|
|
148
152
|
return map_all
|
|
149
153
|
|
|
150
154
|
|
|
155
|
+
@dataclasses.dataclass(kw_only=True)
|
|
156
|
+
class InheritablePropertyParts:
|
|
157
|
+
"""This uses only the "soft" information for now, things that aren't relevant
|
|
158
|
+
to the language emitted types. There are some fields that should be inherited
|
|
159
|
+
at that level, but that needs to be done in builder. When that is done, the
|
|
160
|
+
"label" and "desc" could probably be removed from this list."""
|
|
161
|
+
|
|
162
|
+
label: Optional[str] = None
|
|
163
|
+
desc: Optional[str] = None
|
|
164
|
+
ext_info: Optional[data_t.ExtInfo] = None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _extract_inheritable_property_parts(
|
|
168
|
+
stype: builder.SpecTypeDefnObject,
|
|
169
|
+
prop: builder.SpecProperty,
|
|
170
|
+
) -> InheritablePropertyParts:
|
|
171
|
+
if not stype.is_base and isinstance(stype.base, builder.SpecTypeDefn):
|
|
172
|
+
base_prop = (stype.base.properties or {}).get(prop.name)
|
|
173
|
+
if base_prop is None:
|
|
174
|
+
base_parts = InheritablePropertyParts()
|
|
175
|
+
else:
|
|
176
|
+
base_parts = _extract_inheritable_property_parts(stype.base, base_prop)
|
|
177
|
+
# Layout should not be inherited, as it'd end up hiding properties in the derived type
|
|
178
|
+
if base_parts.ext_info is not None:
|
|
179
|
+
base_parts.ext_info.layout = None
|
|
180
|
+
else:
|
|
181
|
+
base_parts = InheritablePropertyParts()
|
|
182
|
+
|
|
183
|
+
label = prop.label or base_parts.label
|
|
184
|
+
desc = prop.desc or base_parts.desc
|
|
185
|
+
local_ext_info = _parse_ext_info(prop.ext_info)
|
|
186
|
+
if local_ext_info is None:
|
|
187
|
+
ext_info = base_parts.ext_info
|
|
188
|
+
elif base_parts.ext_info is None:
|
|
189
|
+
ext_info = local_ext_info
|
|
190
|
+
else:
|
|
191
|
+
ext_info = data_t.ExtInfo(
|
|
192
|
+
**(local_ext_info.__dict__ | base_parts.ext_info.__dict__)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
return InheritablePropertyParts(label=label, desc=desc, ext_info=ext_info)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
ExtInfoLayout = dict[str, set[str]]
|
|
199
|
+
ALL_FIELDS_GROUP = "*all_fields"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _extract_and_validate_layout(
|
|
203
|
+
stype: builder.SpecTypeDefnObject,
|
|
204
|
+
ext_info: data_t.ExtInfo,
|
|
205
|
+
base_layout: ExtInfoLayout | None,
|
|
206
|
+
) -> ExtInfoLayout:
|
|
207
|
+
"""
|
|
208
|
+
Produce a map of groups to fields, for validation.
|
|
209
|
+
"""
|
|
210
|
+
if ext_info.layout is None:
|
|
211
|
+
return {}
|
|
212
|
+
assert stype.properties is not None
|
|
213
|
+
|
|
214
|
+
all_fields_group: set[str] = set()
|
|
215
|
+
layout: ExtInfoLayout = {ALL_FIELDS_GROUP: all_fields_group}
|
|
216
|
+
|
|
217
|
+
for group in ext_info.layout.groups:
|
|
218
|
+
fields = set(group.fields or [])
|
|
219
|
+
for field in fields:
|
|
220
|
+
assert field in stype.properties, f"layout-refers-to-missing-field:{field}"
|
|
221
|
+
|
|
222
|
+
local_ref_name = None
|
|
223
|
+
if group.ref_name is not None:
|
|
224
|
+
assert (
|
|
225
|
+
base_layout is None or base_layout.get(group.ref_name) is None
|
|
226
|
+
), f"group-name-duplicate-in-base:{group.ref_name}"
|
|
227
|
+
local_ref_name = group.ref_name
|
|
228
|
+
|
|
229
|
+
if group.extends:
|
|
230
|
+
assert base_layout is not None, "missing-base-layout"
|
|
231
|
+
base_group = base_layout.get(group.extends)
|
|
232
|
+
assert base_group is not None, f"missing-base-group:{group.extends}"
|
|
233
|
+
fields.update(base_group)
|
|
234
|
+
local_ref_name = group.extends
|
|
235
|
+
|
|
236
|
+
assert local_ref_name not in layout, f"duplicate-group:{local_ref_name}"
|
|
237
|
+
if local_ref_name is not None:
|
|
238
|
+
layout[local_ref_name] = fields
|
|
239
|
+
all_fields_group.update(fields)
|
|
240
|
+
|
|
241
|
+
for group_ref_name in base_layout or {}:
|
|
242
|
+
assert group_ref_name in layout, f"missing-base-group:{group_ref_name}"
|
|
243
|
+
|
|
244
|
+
for prop_ref_name in stype.properties:
|
|
245
|
+
assert prop_ref_name in all_fields_group, f"layout-missing-field:{prop_ref_name}"
|
|
246
|
+
|
|
247
|
+
return layout
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _validate_type_ext_info(stype: builder.SpecTypeDefnObject) -> ExtInfoLayout | None:
|
|
251
|
+
ext_info = _parse_ext_info(stype.ext_info)
|
|
252
|
+
if ext_info is None:
|
|
253
|
+
return None
|
|
254
|
+
|
|
255
|
+
if not stype.is_base and isinstance(stype.base, builder.SpecTypeDefnObject):
|
|
256
|
+
base_layout = _validate_type_ext_info(stype.base)
|
|
257
|
+
else:
|
|
258
|
+
base_layout = None
|
|
259
|
+
|
|
260
|
+
return _extract_and_validate_layout(stype, ext_info, base_layout)
|
|
261
|
+
|
|
262
|
+
|
|
151
263
|
def _build_map_type(
|
|
152
264
|
build: builder.SpecBuilder, stype: builder.SpecTypeDefn
|
|
153
265
|
) -> MapType | None:
|
|
@@ -158,6 +270,8 @@ def _build_map_type(
|
|
|
158
270
|
and not stype.is_base
|
|
159
271
|
and stype.base is not None
|
|
160
272
|
):
|
|
273
|
+
_validate_type_ext_info(stype)
|
|
274
|
+
|
|
161
275
|
properties: dict[str, MapProperty] = {}
|
|
162
276
|
map_type = MapTypeObject(
|
|
163
277
|
type_name=stype.name,
|
|
@@ -170,14 +284,17 @@ def _build_map_type(
|
|
|
170
284
|
|
|
171
285
|
if stype.properties is not None:
|
|
172
286
|
for prop in stype.properties.values():
|
|
287
|
+
parts = _extract_inheritable_property_parts(stype, prop)
|
|
288
|
+
# Propertis can't have layouts
|
|
289
|
+
assert parts.ext_info is None or parts.ext_info.layout is None
|
|
173
290
|
map_property = MapProperty(
|
|
174
291
|
type_name=prop.name,
|
|
175
|
-
label=
|
|
292
|
+
label=parts.label,
|
|
176
293
|
api_name=ts_name(prop.name, prop.name_case),
|
|
177
294
|
extant=prop.extant,
|
|
178
295
|
type_path=type_path_of(prop.spec_type),
|
|
179
|
-
ext_info=
|
|
180
|
-
desc=
|
|
296
|
+
ext_info=serialize_for_api(parts.ext_info),
|
|
297
|
+
desc=parts.desc,
|
|
181
298
|
default=prop.default,
|
|
182
299
|
)
|
|
183
300
|
map_type.properties[prop.name] = map_property
|
|
@@ -211,7 +328,7 @@ def _build_map_type(
|
|
|
211
328
|
return None
|
|
212
329
|
|
|
213
330
|
|
|
214
|
-
def
|
|
331
|
+
def _parse_ext_info(in_ext: Any) -> Optional[data_t.ExtInfo]:
|
|
215
332
|
if in_ext is None:
|
|
216
333
|
return None
|
|
217
334
|
assert isinstance(in_ext, dict)
|
|
@@ -229,6 +346,10 @@ def _convert_ext_info(in_ext: Any) -> Optional[PureJsonValue]:
|
|
|
229
346
|
df["result_type"] = serialize_for_storage(converted)
|
|
230
347
|
mod_ext["data_format"] = df
|
|
231
348
|
|
|
232
|
-
|
|
349
|
+
return ext_info_parser.parse_storage(mod_ext)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _convert_ext_info(in_ext: Any) -> Optional[PureJsonValue]:
|
|
233
353
|
# we need to convert this to API storage since it'll be used as-is in the UI
|
|
354
|
+
parsed = _parse_ext_info(in_ext)
|
|
234
355
|
return cast(PureJsonValue, serialize_for_api(parsed))
|
|
@@ -19,7 +19,19 @@ Arguments:
|
|
|
19
19
|
type: ObjectId
|
|
20
20
|
desc: "Definition id for the entities to create"
|
|
21
21
|
entity_type:
|
|
22
|
-
type:
|
|
22
|
+
type: |
|
|
23
|
+
Union<
|
|
24
|
+
Literal<entity.EntityType.lab_request>,
|
|
25
|
+
Literal<entity.EntityType.approval>,
|
|
26
|
+
Literal<entity.EntityType.custom_entity>,
|
|
27
|
+
Literal<entity.EntityType.inventory_amount>,
|
|
28
|
+
Literal<entity.EntityType.task>,
|
|
29
|
+
Literal<entity.EntityType.project>,
|
|
30
|
+
Literal<entity.EntityType.equipment>,
|
|
31
|
+
Literal<entity.EntityType.inv_local_locations>,
|
|
32
|
+
Literal<entity.EntityType.field_option_set>,
|
|
33
|
+
Literal<entity.EntityType.webhook>
|
|
34
|
+
>
|
|
23
35
|
desc: "The type of the entities to create"
|
|
24
36
|
entities_to_create:
|
|
25
37
|
type: List<EntityToCreate>
|
|
@@ -26,7 +26,19 @@ Arguments:
|
|
|
26
26
|
type: ObjectId
|
|
27
27
|
desc: "Definition id of the entity to create"
|
|
28
28
|
entity_type:
|
|
29
|
-
type:
|
|
29
|
+
type: |
|
|
30
|
+
Union<
|
|
31
|
+
Literal<entity.EntityType.lab_request>,
|
|
32
|
+
Literal<entity.EntityType.approval>,
|
|
33
|
+
Literal<entity.EntityType.custom_entity>,
|
|
34
|
+
Literal<entity.EntityType.inventory_amount>,
|
|
35
|
+
Literal<entity.EntityType.task>,
|
|
36
|
+
Literal<entity.EntityType.project>,
|
|
37
|
+
Literal<entity.EntityType.equipment>,
|
|
38
|
+
Literal<entity.EntityType.inv_local_locations>,
|
|
39
|
+
Literal<entity.EntityType.field_option_set>,
|
|
40
|
+
Literal<entity.EntityType.webhook>
|
|
41
|
+
>
|
|
30
42
|
desc: "The type of the entities requested"
|
|
31
43
|
field_values?:
|
|
32
44
|
type: Optional<List<field_values.FieldRefNameValue>>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/entity/transition_entity_phase
|
|
5
|
+
function: main.site.app.external.entity.transition_entity_phase.transition_entity_phase
|
|
6
|
+
desc: "Transitions an entity from one phase to another"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
TransitionIdentifierPhases:
|
|
10
|
+
type: Object
|
|
11
|
+
properties:
|
|
12
|
+
type:
|
|
13
|
+
type: Literal<'phases'>
|
|
14
|
+
phase_from_key:
|
|
15
|
+
type: identifier.IdentifierKey
|
|
16
|
+
phase_to_key:
|
|
17
|
+
type: identifier.IdentifierKey
|
|
18
|
+
|
|
19
|
+
TransitionIdentifierTransition:
|
|
20
|
+
type: Object
|
|
21
|
+
properties:
|
|
22
|
+
type:
|
|
23
|
+
type: Literal<'transition'>
|
|
24
|
+
transition_key:
|
|
25
|
+
type: identifier.IdentifierKey
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
TransitionIdentifier:
|
|
29
|
+
type: Alias
|
|
30
|
+
alias: |
|
|
31
|
+
Union<TransitionIdentifierPhases, TransitionIdentifierTransition>
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Arguments:
|
|
35
|
+
type: Object
|
|
36
|
+
properties:
|
|
37
|
+
entity:
|
|
38
|
+
type: entity.Entity
|
|
39
|
+
transition:
|
|
40
|
+
type: TransitionIdentifier
|
|
41
|
+
|
|
42
|
+
Data:
|
|
43
|
+
type: response.Response
|
|
44
|
+
properties:
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/permissions/external_set_core_permissions
|
|
5
|
+
function: main.site.app.external.permissions.set_core_permissions.set_core_permissions
|
|
6
|
+
desc: "Sets recipe related permissions"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
PermissionsScopeProject:
|
|
10
|
+
type: Object
|
|
11
|
+
properties:
|
|
12
|
+
type:
|
|
13
|
+
type: Literal<'project'>
|
|
14
|
+
project_key:
|
|
15
|
+
type: identifier.IdentifierKey
|
|
16
|
+
|
|
17
|
+
PermissionsScopeRecipe:
|
|
18
|
+
type: Object
|
|
19
|
+
properties:
|
|
20
|
+
type:
|
|
21
|
+
type: Literal<'recipe'>
|
|
22
|
+
recipe_key:
|
|
23
|
+
type: identifier.IdentifierKey
|
|
24
|
+
|
|
25
|
+
PermissionsScopeMaterialFamily:
|
|
26
|
+
type: Object
|
|
27
|
+
properties:
|
|
28
|
+
type:
|
|
29
|
+
type: Literal<'material_family'>
|
|
30
|
+
material_family_key:
|
|
31
|
+
type: identifier.IdentifierKey
|
|
32
|
+
|
|
33
|
+
PermissionsScopeAllMaterialFamilies:
|
|
34
|
+
type: Object
|
|
35
|
+
properties:
|
|
36
|
+
type:
|
|
37
|
+
type: Literal<'all_material_families'>
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
PermissionsScope:
|
|
41
|
+
type: Alias
|
|
42
|
+
alias: |
|
|
43
|
+
Union<
|
|
44
|
+
PermissionsScopeProject,
|
|
45
|
+
PermissionsScopeRecipe,
|
|
46
|
+
PermissionsScopeMaterialFamily,
|
|
47
|
+
PermissionsScopeAllMaterialFamilies
|
|
48
|
+
>
|
|
49
|
+
|
|
50
|
+
Arguments:
|
|
51
|
+
type: Object
|
|
52
|
+
properties:
|
|
53
|
+
scope:
|
|
54
|
+
type: PermissionsScope
|
|
55
|
+
desc: Scope of permissions to change
|
|
56
|
+
user_group_ids?:
|
|
57
|
+
type: List<Integer>
|
|
58
|
+
desc: User group ids to grant permission to
|
|
59
|
+
user_ids?:
|
|
60
|
+
type: List<Integer>
|
|
61
|
+
desc: User ids to grant permission to
|
|
62
|
+
permissions_types:
|
|
63
|
+
type: List<permissions.CorePermissionType>
|
|
64
|
+
update_type:
|
|
65
|
+
type: post_base.UpdateType
|
|
66
|
+
desc: The type of update to perform
|
|
67
|
+
|
|
68
|
+
Data:
|
|
69
|
+
type: Object
|
|
@@ -11,9 +11,9 @@ Arguments:
|
|
|
11
11
|
recipe_key:
|
|
12
12
|
type: identifier.IdentifierKey
|
|
13
13
|
desc: "Identifier for the recipe"
|
|
14
|
+
input_key?:
|
|
15
|
+
type: identifier.IdentifierKey
|
|
16
|
+
desc: "Identifier for an input to use for the association. Optionally supplied. If not supplied, one is created"
|
|
14
17
|
|
|
15
18
|
Data:
|
|
16
|
-
type:
|
|
17
|
-
properties:
|
|
18
|
-
result_id:
|
|
19
|
-
type: ObjectId
|
|
19
|
+
type: async_batch.AsyncBatchActionReturn
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
$endpoint:
|
|
2
2
|
is_sdk: true
|
|
3
|
+
async_batch_path: create_recipe
|
|
3
4
|
method: post
|
|
4
5
|
path: ${external}/recipes/create_recipe
|
|
5
6
|
function: main.site.app.external.recipes.create_recipe.create_recipe
|
|
@@ -24,7 +25,7 @@ Arguments:
|
|
|
24
25
|
recipe_metadata?:
|
|
25
26
|
type: List<recipe_metadata.MetadataValue>
|
|
26
27
|
desc: Metadata values to populate the recipe with
|
|
27
|
-
identifiers
|
|
28
|
+
identifiers?:
|
|
28
29
|
type: recipe_identifiers.RecipeIdentifiers
|
|
29
30
|
desc: A recipe won't be created if it matches the identifier. An identifier must be unique in the schema
|
|
30
31
|
definition_key?:
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/recipes/disassociate_recipe_as_input
|
|
5
|
+
function: main.site.app.external.recipes.disassociate_recipe_as_input.disassociate_recipe_as_input
|
|
6
|
+
desc: Remove any association between a recipe and ingredients
|
|
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: async_batch.AsyncBatchActionReturn
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
is_beta: true
|
|
4
|
+
async_batch_path: edit_recipe_inputs
|
|
5
|
+
method: post
|
|
6
|
+
path: ${external}/recipes/edit_recipe_inputs
|
|
7
|
+
function: main.site.app.external.recipes.edit_recipe_inputs.edit_recipe_inputs
|
|
8
|
+
desc: Clear, update, or add inputs on a recipe
|
|
9
|
+
|
|
10
|
+
RecipeInputEditType:
|
|
11
|
+
type: StringEnum
|
|
12
|
+
values:
|
|
13
|
+
- clear_inputs
|
|
14
|
+
- upsert_input
|
|
15
|
+
- add_input
|
|
16
|
+
|
|
17
|
+
RecipeInputEditBase:
|
|
18
|
+
type: Object
|
|
19
|
+
properties:
|
|
20
|
+
type:
|
|
21
|
+
type: RecipeInputEditType
|
|
22
|
+
|
|
23
|
+
RecipeInputEditClearInputs:
|
|
24
|
+
type: RecipeInputEditBase
|
|
25
|
+
desc: "Clears all inputs and steps from recipe workflow step to start from scratch"
|
|
26
|
+
properties:
|
|
27
|
+
type:
|
|
28
|
+
type: Literal<RecipeInputEditType.clear_inputs>
|
|
29
|
+
|
|
30
|
+
RecipeInputEditInputBase:
|
|
31
|
+
type: RecipeInputEditBase
|
|
32
|
+
properties:
|
|
33
|
+
ingredient_key:
|
|
34
|
+
type: identifier.IdentifierKey
|
|
35
|
+
quantity_basis:
|
|
36
|
+
type: recipe_inputs.QuantityBasis
|
|
37
|
+
desc: "The basis to set for this edit. Normally this should be mass for ingredients and arbitrary for process parameters"
|
|
38
|
+
default: mass
|
|
39
|
+
input_value_type:
|
|
40
|
+
type: recipe_inputs.InputValueType
|
|
41
|
+
default: value
|
|
42
|
+
value_numeric?:
|
|
43
|
+
type: Decimal
|
|
44
|
+
value_str?:
|
|
45
|
+
type: String
|
|
46
|
+
calculation_key?:
|
|
47
|
+
type: identifier.IdentifierKey
|
|
48
|
+
desc: The basis calculation to set
|
|
49
|
+
|
|
50
|
+
RecipeInputEditUpsertInput:
|
|
51
|
+
type: RecipeInputEditInputBase
|
|
52
|
+
desc: "If the input is already present in the recipe, modifies it otherwise adds a new one"
|
|
53
|
+
properties:
|
|
54
|
+
type:
|
|
55
|
+
type: Literal<RecipeInputEditType.upsert_input>
|
|
56
|
+
clear_first:
|
|
57
|
+
type: Boolean
|
|
58
|
+
desc: whether to clear the current value first or modify in place
|
|
59
|
+
|
|
60
|
+
RecipeInputEditAddInput:
|
|
61
|
+
type: RecipeInputEditInputBase
|
|
62
|
+
desc: "Add the input into the recipe. If it's already present and the recipe is not a mixorder, this call will fail"
|
|
63
|
+
properties:
|
|
64
|
+
type:
|
|
65
|
+
type: Literal<RecipeInputEditType.add_input>
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
RecipeInputEdit:
|
|
69
|
+
type: Alias
|
|
70
|
+
alias: Union<RecipeInputEditClearInputs, RecipeInputEditUpsertInput, RecipeInputEditAddInput>
|
|
71
|
+
discriminator: type
|
|
72
|
+
|
|
73
|
+
Arguments:
|
|
74
|
+
type: Object
|
|
75
|
+
properties:
|
|
76
|
+
recipe_key:
|
|
77
|
+
type: identifier.IdentifierKey
|
|
78
|
+
desc: "Identifier for the recipe"
|
|
79
|
+
recipe_workflow_step_identifier:
|
|
80
|
+
type: recipe_workflow_steps.RecipeWorkflowStepIdentifier
|
|
81
|
+
edits:
|
|
82
|
+
type: List<RecipeInputEdit>
|
|
83
|
+
|
|
84
|
+
Data:
|
|
85
|
+
type: Object
|
|
86
|
+
properties:
|
|
@@ -7,9 +7,12 @@ $endpoint:
|
|
|
7
7
|
Arguments:
|
|
8
8
|
type: Object
|
|
9
9
|
properties:
|
|
10
|
-
recipe_output_id
|
|
10
|
+
recipe_output_id?:
|
|
11
11
|
type: ObjectId
|
|
12
12
|
desc: "The recipe output ID to fetch the curve for. This must be a curve recipe output. Recipe Outputs can be found from external_get_recipes_data"
|
|
13
|
+
recipe_input_id?:
|
|
14
|
+
type: ObjectId
|
|
15
|
+
desc: "The recipe input ID to fetch the curve for. This must be a curve recipe input. Recipe Inputs can be found from external_get_recipes_data"
|
|
13
16
|
|
|
14
17
|
Data:
|
|
15
18
|
type: Object
|
|
@@ -77,6 +77,9 @@ SimpleOutputCondition:
|
|
|
77
77
|
RecipeInput:
|
|
78
78
|
type: Object
|
|
79
79
|
properties:
|
|
80
|
+
id:
|
|
81
|
+
type: ObjectId
|
|
82
|
+
desc: "The globally unique id for the recipe input"
|
|
80
83
|
input_id:
|
|
81
84
|
type: ObjectId
|
|
82
85
|
desc: "The globally unique ID for the input, allowing for cross reference elsewhere"
|
|
@@ -92,6 +95,9 @@ RecipeInput:
|
|
|
92
95
|
quantity_json:
|
|
93
96
|
type: JsonValue
|
|
94
97
|
desc: "The quantity of the input if it is not numeric or recipe type. If this is filled in, quantity_dec will not be filled in"
|
|
98
|
+
curve_id:
|
|
99
|
+
type: Optional<ObjectId>
|
|
100
|
+
desc: "The id of the curve associated with the input if the input is of curve type."
|
|
95
101
|
actual_quantity_dec:
|
|
96
102
|
type: Decimal
|
|
97
103
|
desc: "The actual quantity of the input if it is numeric or recipe type. If this is filled in, actual_quantity_json will not be filled in"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
$endpoint:
|
|
2
|
+
is_sdk: true
|
|
3
|
+
method: post
|
|
4
|
+
path: ${external}/recipes/set_recipe_tags
|
|
5
|
+
function: main.site.app.external.recipes.set_recipe_tags.set_recipe_tags
|
|
6
|
+
desc: Modifies recipes tags for a recipe
|
|
7
|
+
|
|
8
|
+
RecipeTagUpdateType:
|
|
9
|
+
type: StringEnum
|
|
10
|
+
values:
|
|
11
|
+
- append
|
|
12
|
+
- override
|
|
13
|
+
- remove
|
|
14
|
+
|
|
15
|
+
RecipeTagUpdateBase:
|
|
16
|
+
type: Object
|
|
17
|
+
properties:
|
|
18
|
+
type:
|
|
19
|
+
type: RecipeTagUpdateType
|
|
20
|
+
|
|
21
|
+
RecipeTagAppend:
|
|
22
|
+
type: RecipeTagUpdateBase
|
|
23
|
+
properties:
|
|
24
|
+
type:
|
|
25
|
+
type: Literal<RecipeTagUpdateType.append>
|
|
26
|
+
recipe_tag_ids:
|
|
27
|
+
type: List<ObjectId>
|
|
28
|
+
|
|
29
|
+
RecipeTagRemove:
|
|
30
|
+
type: RecipeTagUpdateBase
|
|
31
|
+
properties:
|
|
32
|
+
type:
|
|
33
|
+
type: Literal<RecipeTagUpdateType.remove>
|
|
34
|
+
recipe_tag_ids:
|
|
35
|
+
type: List<ObjectId>
|
|
36
|
+
|
|
37
|
+
RecipeTagOverride:
|
|
38
|
+
type: RecipeTagUpdateBase
|
|
39
|
+
properties:
|
|
40
|
+
type:
|
|
41
|
+
type: Literal<RecipeTagUpdateType.override>
|
|
42
|
+
recipe_tag_ids:
|
|
43
|
+
type: List<ObjectId>
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
RecipeTagUpdate:
|
|
47
|
+
type: Alias
|
|
48
|
+
alias: Union<RecipeTagAppend, RecipeTagRemove, RecipeTagOverride>
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
Arguments:
|
|
52
|
+
type: Object
|
|
53
|
+
properties:
|
|
54
|
+
recipe_key:
|
|
55
|
+
type: identifier.IdentifierKey
|
|
56
|
+
desc: Identifier for the recipe
|
|
57
|
+
recipe_tag_update:
|
|
58
|
+
type: RecipeTagUpdate
|
|
59
|
+
desc: The update to perform on the recipe tags
|
|
60
|
+
|
|
61
|
+
Data:
|
|
62
|
+
type: Object
|
uncountable/core/__init__.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
from .client import AuthDetailsApiKey, Client
|
|
2
|
+
from .file_upload import MediaFileUpload, UploadedFile
|
|
3
|
+
from .async_batch import AsyncBatchProcessor
|
|
2
4
|
|
|
3
|
-
__all__: list[str] = ["AuthDetailsApiKey", "Client"]
|
|
5
|
+
__all__: list[str] = ["AuthDetailsApiKey", "AsyncBatchProcessor", "Client", "MediaFileUpload", "UploadedFile"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from uncountable.core.client import Client
|
|
2
|
+
from uncountable.types.async_batch import AsyncBatchRequest
|
|
3
|
+
from uncountable.types.async_batch_processor import AsyncBatchProcessorBase
|
|
4
|
+
from uncountable.types import async_batch_t, base_t
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AsyncBatchProcessor(AsyncBatchProcessorBase):
|
|
8
|
+
_client: Client
|
|
9
|
+
_queue: list[AsyncBatchRequest]
|
|
10
|
+
|
|
11
|
+
def __init__(self, *, client: Client) -> None:
|
|
12
|
+
super().__init__()
|
|
13
|
+
self._client = client
|
|
14
|
+
self._queue = []
|
|
15
|
+
|
|
16
|
+
def _enqueue(self, req: async_batch_t.AsyncBatchRequest) -> None:
|
|
17
|
+
self._queue.append(req)
|
|
18
|
+
|
|
19
|
+
def send(self) -> base_t.ObjectId:
|
|
20
|
+
job_id = self._client.execute_batch_load_async(requests=self._queue).job_id
|
|
21
|
+
self._queue = []
|
|
22
|
+
return job_id
|