UncountablePythonSDK 0.0.19__py3-none-any.whl → 0.0.21__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.19.dist-info → UncountablePythonSDK-0.0.21.dist-info}/METADATA +4 -2
- {UncountablePythonSDK-0.0.19.dist-info → UncountablePythonSDK-0.0.21.dist-info}/RECORD +50 -31
- 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 +13 -0
- pkgs/type_spec/config.py +1 -0
- pkgs/type_spec/emit_open_api.py +11 -0
- pkgs/type_spec/emit_open_api_util.py +1 -0
- pkgs/type_spec/emit_python.py +241 -55
- pkgs/type_spec/type_info/emit_type_info.py +129 -8
- type_spec/external/api/entity/create_entities.yaml +12 -1
- type_spec/external/api/entity/create_entity.yaml +12 -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 +88 -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 +2 -1
- uncountable/core/client.py +11 -9
- 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 +107 -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 +9 -0
- uncountable/types/async_batch_processor.py +154 -0
- uncountable/types/client_base.py +113 -48
- 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.19.dist-info → UncountablePythonSDK-0.0.21.dist-info}/WHEEL +0 -0
- {UncountablePythonSDK-0.0.19.dist-info → UncountablePythonSDK-0.0.21.dist-info}/top_level.txt +0 -0
|
@@ -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,88 @@
|
|
|
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.RecipeWorkflowStepIdentifierType
|
|
81
|
+
edits:
|
|
82
|
+
type: List<RecipeInputEdit>
|
|
83
|
+
|
|
84
|
+
Data:
|
|
85
|
+
type: Object
|
|
86
|
+
properties:
|
|
87
|
+
result_id:
|
|
88
|
+
type: ObjectId
|
|
@@ -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
uncountable/core/client.py
CHANGED
|
@@ -11,6 +11,9 @@ 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
|
+
from .file_upload import FileUpload, FileUploader, UploadedFile
|
|
15
|
+
from .types import AuthDetails, AuthDetailsApiKey
|
|
16
|
+
|
|
14
17
|
DT = typing.TypeVar("DT")
|
|
15
18
|
|
|
16
19
|
|
|
@@ -43,23 +46,16 @@ class HTTPPostRequest(HTTPRequestBase):
|
|
|
43
46
|
HTTPRequest = HTTPPostRequest | HTTPGetRequest
|
|
44
47
|
|
|
45
48
|
|
|
46
|
-
@dataclass(kw_only=True)
|
|
47
|
-
class AuthDetailsApiKey:
|
|
48
|
-
api_id: str
|
|
49
|
-
api_secret_key: str
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
AuthDetails = AuthDetailsApiKey
|
|
53
|
-
|
|
54
|
-
|
|
55
49
|
class Client(ClientMethods):
|
|
56
50
|
_parser_map: dict[type, CachedParser] = {}
|
|
57
51
|
_auth_details: AuthDetails
|
|
58
52
|
_base_url: str
|
|
53
|
+
_file_uploader: FileUploader
|
|
59
54
|
|
|
60
55
|
def __init__(self, *, base_url: str, auth_details: AuthDetails):
|
|
61
56
|
self._auth_details = auth_details
|
|
62
57
|
self._base_url = base_url
|
|
58
|
+
self._file_uploader = FileUploader(self._base_url, self._auth_details)
|
|
63
59
|
|
|
64
60
|
def do_request(self, *, api_request: APIRequest, return_type: type[DT]) -> DT:
|
|
65
61
|
http_request = self._build_http_request(api_request=api_request)
|
|
@@ -125,3 +121,9 @@ class Client(ClientMethods):
|
|
|
125
121
|
)
|
|
126
122
|
case _:
|
|
127
123
|
raise ValueError(f"unsupported request method: {method}")
|
|
124
|
+
|
|
125
|
+
def upload_files(
|
|
126
|
+
self: typing.Self, *, file_uploads: list[FileUpload]
|
|
127
|
+
) -> list[UploadedFile]:
|
|
128
|
+
"""Upload files to uncountable, returning file ids that are usable with other SDK operations."""
|
|
129
|
+
return self._file_uploader.upload_files(file_uploads=file_uploads)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Generator, Literal, Self
|
|
8
|
+
|
|
9
|
+
import aiohttp
|
|
10
|
+
import aiotus
|
|
11
|
+
|
|
12
|
+
from .types import AuthDetails
|
|
13
|
+
|
|
14
|
+
_CHUNK_SIZE = 5 * 1024 * 1024 # s3 requires 5MiB minimum
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FileUploadType(StrEnum):
|
|
18
|
+
MEDIA_FILE_UPLOAD = "MEDIA_FILE_UPLOAD"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(kw_only=True)
|
|
22
|
+
class MediaFileUpload:
|
|
23
|
+
"""Upload file from a path on disk"""
|
|
24
|
+
|
|
25
|
+
path: str
|
|
26
|
+
type: FileUploadType.MEDIA_FILE_UPLOAD = FileUploadType.MEDIA_FILE_UPLOAD
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
FileUpload = MediaFileUpload
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(kw_only=True)
|
|
33
|
+
class FileBytes:
|
|
34
|
+
name: str
|
|
35
|
+
bytes_data: BytesIO
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@contextmanager
|
|
39
|
+
def file_upload_data(file_upload: FileUpload) -> Generator[FileBytes, None, None]:
|
|
40
|
+
match file_upload.type:
|
|
41
|
+
case FileUploadType.MEDIA_FILE_UPLOAD:
|
|
42
|
+
with open(file_upload.path, "rb") as f:
|
|
43
|
+
yield FileBytes(
|
|
44
|
+
name=Path(file_upload.path).name, bytes_data=BytesIO(f.read())
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(kw_only=True)
|
|
49
|
+
class UploadedFile:
|
|
50
|
+
name: str
|
|
51
|
+
file_id: int
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class UploadFailed(Exception):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class FileUploader:
|
|
59
|
+
_auth_details: AuthDetails
|
|
60
|
+
_base_url: str
|
|
61
|
+
|
|
62
|
+
def __init__(self: Self, base_url: str, auth_details: AuthDetails) -> None:
|
|
63
|
+
self._base_url = base_url
|
|
64
|
+
self._auth_details = auth_details
|
|
65
|
+
|
|
66
|
+
async def _upload_file(self: Self, file_upload: FileUpload) -> UploadedFile:
|
|
67
|
+
creation_url = f"{self._base_url}/api/external/file_upload/files"
|
|
68
|
+
auth = aiohttp.BasicAuth(
|
|
69
|
+
self._auth_details.api_id, self._auth_details.api_secret_key
|
|
70
|
+
)
|
|
71
|
+
async with (
|
|
72
|
+
aiohttp.ClientSession(
|
|
73
|
+
auth=auth, headers={"Origin": self._base_url}
|
|
74
|
+
) as session,
|
|
75
|
+
):
|
|
76
|
+
with file_upload_data(file_upload) as file_bytes:
|
|
77
|
+
location = await aiotus.upload(
|
|
78
|
+
creation_url,
|
|
79
|
+
file_bytes.bytes_data,
|
|
80
|
+
{"filename": file_bytes.name.encode()},
|
|
81
|
+
client_session=session,
|
|
82
|
+
chunksize=_CHUNK_SIZE,
|
|
83
|
+
)
|
|
84
|
+
if location is None:
|
|
85
|
+
raise UploadFailed(f"Failed to upload: {file_bytes.name}")
|
|
86
|
+
return UploadedFile(
|
|
87
|
+
name=file_bytes.name, file_id=int(location.path.split("/")[-1])
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def upload_files(
|
|
91
|
+
self: Self, *, file_uploads: list[FileUpload]
|
|
92
|
+
) -> list[UploadedFile]:
|
|
93
|
+
return [
|
|
94
|
+
asyncio.run(self._upload_file(file_upload)) for file_upload in file_uploads
|
|
95
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import typing
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
from urllib.parse import urljoin
|
|
7
|
+
|
|
8
|
+
import aiohttp
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
from pkgs.argument_parser import CachedParser
|
|
12
|
+
from pkgs.serialization_util import serialize_for_api
|
|
13
|
+
from uncountable.types.client_base import APIRequest, ClientMethods
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(kw_only=True)
|
|
17
|
+
class AuthDetailsApiKey:
|
|
18
|
+
api_id: str
|
|
19
|
+
api_secret_key: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
AuthDetails = AuthDetailsApiKey
|
uncountable/types/__init__.py
CHANGED
|
@@ -17,6 +17,8 @@ from .api.recipes import create_recipe as create_recipe_t
|
|
|
17
17
|
from .api.recipe_links import create_recipe_link as create_recipe_link_t
|
|
18
18
|
from .api.recipes import create_recipes as create_recipes_t
|
|
19
19
|
from . import curves as curves_t
|
|
20
|
+
from .api.recipes import disassociate_recipe_as_input as disassociate_recipe_as_input_t
|
|
21
|
+
from .api.recipes import edit_recipe_inputs as edit_recipe_inputs_t
|
|
20
22
|
from . import entity as entity_t
|
|
21
23
|
from .api.batch import execute_batch as execute_batch_t
|
|
22
24
|
from .api.batch import execute_batch_load_async as execute_batch_load_async_t
|
|
@@ -47,21 +49,28 @@ from .api.entity import list_entities as list_entities_t
|
|
|
47
49
|
from .api.id_source import list_id_source as list_id_source_t
|
|
48
50
|
from .api.id_source import match_id_source as match_id_source_t
|
|
49
51
|
from . import outputs as outputs_t
|
|
52
|
+
from . import permissions as permissions_t
|
|
50
53
|
from . import phases as phases_t
|
|
54
|
+
from . import post_base as post_base_t
|
|
51
55
|
from . import recipe_identifiers as recipe_identifiers_t
|
|
56
|
+
from . import recipe_inputs as recipe_inputs_t
|
|
52
57
|
from . import recipe_links as recipe_links_t
|
|
53
58
|
from . import recipe_metadata as recipe_metadata_t
|
|
54
59
|
from . import recipe_output_metadata as recipe_output_metadata_t
|
|
55
60
|
from . import recipe_tags as recipe_tags_t
|
|
61
|
+
from . import recipe_workflow_steps as recipe_workflow_steps_t
|
|
56
62
|
from .api.entity import resolve_entity_ids as resolve_entity_ids_t
|
|
57
63
|
from .api.outputs import resolve_output_conditions as resolve_output_conditions_t
|
|
58
64
|
from . import response as response_t
|
|
59
65
|
from .api.triggers import run_trigger as run_trigger_t
|
|
66
|
+
from .api.permissions import set_core_permissions as set_core_permissions_t
|
|
60
67
|
from .api.inputs import set_input_attribute_values as set_input_attribute_values_t
|
|
61
68
|
from .api.recipes import set_recipe_inputs as set_recipe_inputs_t
|
|
62
69
|
from .api.recipes import set_recipe_metadata as set_recipe_metadata_t
|
|
63
70
|
from .api.recipes import set_recipe_outputs as set_recipe_outputs_t
|
|
71
|
+
from .api.recipes import set_recipe_tags as set_recipe_tags_t
|
|
64
72
|
from .api.entity import set_values as set_values_t
|
|
73
|
+
from .api.entity import transition_entity_phase as transition_entity_phase_t
|
|
65
74
|
from . import units as units_t
|
|
66
75
|
from . import users as users_t
|
|
67
76
|
from . import workflows as workflows_t
|
|
@@ -82,6 +91,8 @@ __all__: list[str] = [
|
|
|
82
91
|
"create_recipe_link_t",
|
|
83
92
|
"create_recipes_t",
|
|
84
93
|
"curves_t",
|
|
94
|
+
"disassociate_recipe_as_input_t",
|
|
95
|
+
"edit_recipe_inputs_t",
|
|
85
96
|
"entity_t",
|
|
86
97
|
"execute_batch_t",
|
|
87
98
|
"execute_batch_load_async_t",
|
|
@@ -112,21 +123,28 @@ __all__: list[str] = [
|
|
|
112
123
|
"list_id_source_t",
|
|
113
124
|
"match_id_source_t",
|
|
114
125
|
"outputs_t",
|
|
126
|
+
"permissions_t",
|
|
115
127
|
"phases_t",
|
|
128
|
+
"post_base_t",
|
|
116
129
|
"recipe_identifiers_t",
|
|
130
|
+
"recipe_inputs_t",
|
|
117
131
|
"recipe_links_t",
|
|
118
132
|
"recipe_metadata_t",
|
|
119
133
|
"recipe_output_metadata_t",
|
|
120
134
|
"recipe_tags_t",
|
|
135
|
+
"recipe_workflow_steps_t",
|
|
121
136
|
"resolve_entity_ids_t",
|
|
122
137
|
"resolve_output_conditions_t",
|
|
123
138
|
"response_t",
|
|
124
139
|
"run_trigger_t",
|
|
140
|
+
"set_core_permissions_t",
|
|
125
141
|
"set_input_attribute_values_t",
|
|
126
142
|
"set_recipe_inputs_t",
|
|
127
143
|
"set_recipe_metadata_t",
|
|
128
144
|
"set_recipe_outputs_t",
|
|
145
|
+
"set_recipe_tags_t",
|
|
129
146
|
"set_values_t",
|
|
147
|
+
"transition_entity_phase_t",
|
|
130
148
|
"units_t",
|
|
131
149
|
"users_t",
|
|
132
150
|
"workflows_t",
|
|
@@ -34,7 +34,7 @@ class EntityToCreate:
|
|
|
34
34
|
@dataclass(kw_only=True)
|
|
35
35
|
class Arguments:
|
|
36
36
|
definition_id: base_t.ObjectId
|
|
37
|
-
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS]]
|
|
37
|
+
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK]]
|
|
38
38
|
entities_to_create: list[EntityToCreate]
|
|
39
39
|
|
|
40
40
|
|
|
@@ -40,7 +40,7 @@ class EntityFieldInitialValue:
|
|
|
40
40
|
@dataclass(kw_only=True)
|
|
41
41
|
class Arguments:
|
|
42
42
|
definition_id: base_t.ObjectId
|
|
43
|
-
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS]]
|
|
43
|
+
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK]]
|
|
44
44
|
field_values: typing.Optional[typing.Optional[list[field_values_t.FieldRefNameValue]]] = None
|
|
45
45
|
|
|
46
46
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
2
|
+
# flake8: noqa: F821
|
|
3
|
+
# ruff: noqa: E402
|
|
4
|
+
# fmt: off
|
|
5
|
+
# isort: skip_file
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import typing # noqa: F401
|
|
8
|
+
import datetime # noqa: F401
|
|
9
|
+
from decimal import Decimal # noqa: F401
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pkgs.serialization import serial_class
|
|
12
|
+
from ... import entity as entity_t
|
|
13
|
+
from ... import identifier as identifier_t
|
|
14
|
+
from ... import response as response_t
|
|
15
|
+
|
|
16
|
+
__all__: list[str] = [
|
|
17
|
+
"Arguments",
|
|
18
|
+
"Data",
|
|
19
|
+
"ENDPOINT_METHOD",
|
|
20
|
+
"ENDPOINT_PATH",
|
|
21
|
+
"TransitionIdentifier",
|
|
22
|
+
"TransitionIdentifierPhases",
|
|
23
|
+
"TransitionIdentifierTransition",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
ENDPOINT_METHOD = "POST"
|
|
27
|
+
ENDPOINT_PATH = "api/external/entity/transition_entity_phase"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
31
|
+
@serial_class(
|
|
32
|
+
parse_require={"type"},
|
|
33
|
+
)
|
|
34
|
+
@dataclass(kw_only=True)
|
|
35
|
+
class TransitionIdentifierPhases:
|
|
36
|
+
type: typing.Literal["phases"] = "phases"
|
|
37
|
+
phase_from_key: identifier_t.IdentifierKey
|
|
38
|
+
phase_to_key: identifier_t.IdentifierKey
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
42
|
+
@serial_class(
|
|
43
|
+
parse_require={"type"},
|
|
44
|
+
)
|
|
45
|
+
@dataclass(kw_only=True)
|
|
46
|
+
class TransitionIdentifierTransition:
|
|
47
|
+
type: typing.Literal["transition"] = "transition"
|
|
48
|
+
transition_key: identifier_t.IdentifierKey
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
52
|
+
TransitionIdentifier = typing.Union[TransitionIdentifierPhases, TransitionIdentifierTransition]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
56
|
+
@dataclass(kw_only=True)
|
|
57
|
+
class Arguments:
|
|
58
|
+
entity: entity_t.Entity
|
|
59
|
+
transition: TransitionIdentifier
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
63
|
+
@dataclass(kw_only=True)
|
|
64
|
+
class Data(response_t.Response):
|
|
65
|
+
pass
|
|
66
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|