metafold 0.13.dev3__tar.gz → 0.13.1.dev0__tar.gz
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.
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/PKG-INFO +1 -1
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/jobs.py +0 -76
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/projects.py +3 -1
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/simulation/compression_simulation.py +34 -1
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold.egg-info/PKG-INFO +1 -1
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/pyproject.toml +1 -1
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_compression_simulation.py +78 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_jobs.py +0 -137
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/LICENSE +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/README.md +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/__init__.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/api.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/assets.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/auth.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/client.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/exceptions.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/materials.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/simulation/__init__.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/simulation/compression_experiment.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/simulation/run_experiment.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/utils.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold/workflows.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold.egg-info/SOURCES.txt +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold.egg-info/dependency_links.txt +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold.egg-info/requires.txt +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/metafold.egg-info/top_level.txt +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/setup.cfg +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_assets.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_compession_experiment.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_materials.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_projects.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_run_experiment.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_shear_simulation.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_utils.py +0 -0
- {metafold-0.13.dev3 → metafold-0.13.1.dev0}/tests/test_workflows.py +0 -0
|
@@ -135,82 +135,6 @@ class JobsEndpoint:
|
|
|
135
135
|
r: Response = self._client.get(url)
|
|
136
136
|
return Job(**r.json())
|
|
137
137
|
|
|
138
|
-
def run(
|
|
139
|
-
self, type: str, params: dict[str, Any],
|
|
140
|
-
name: str | None = None,
|
|
141
|
-
timeout: int | float = 120,
|
|
142
|
-
project_id: str | None = None,
|
|
143
|
-
) -> Job:
|
|
144
|
-
"""Dispatch a new job and wait for a result.
|
|
145
|
-
|
|
146
|
-
See Metafold API docs for the full list of jobs.
|
|
147
|
-
|
|
148
|
-
Args:
|
|
149
|
-
type: Job type.
|
|
150
|
-
params: Job parameters.
|
|
151
|
-
name: Optional job name.
|
|
152
|
-
timeout: Time in seconds to wait for a result.
|
|
153
|
-
project_id: Job project ID.
|
|
154
|
-
|
|
155
|
-
Returns:
|
|
156
|
-
Completed job resource.
|
|
157
|
-
"""
|
|
158
|
-
url = self.run_status(type, params, name=name, project_id=project_id)
|
|
159
|
-
try:
|
|
160
|
-
r: Response = self.poll(url, timeout)
|
|
161
|
-
except PollTimeout as e:
|
|
162
|
-
raise RuntimeError(
|
|
163
|
-
f"Job '{name or type}' failed to complete within {timeout} seconds"
|
|
164
|
-
) from e
|
|
165
|
-
return Job(**r.json())
|
|
166
|
-
|
|
167
|
-
def run_status(
|
|
168
|
-
self, type: str, params: dict[str, Any],
|
|
169
|
-
name: str | None = None,
|
|
170
|
-
project_id: str | None = None,
|
|
171
|
-
) -> str:
|
|
172
|
-
"""Dispatch a new job and return immediately without waiting for result.
|
|
173
|
-
|
|
174
|
-
See Metafold API docs for the full list of jobs.
|
|
175
|
-
|
|
176
|
-
Args:
|
|
177
|
-
type: Job type.
|
|
178
|
-
params: Job parameters.
|
|
179
|
-
name: Optional job name.
|
|
180
|
-
project_id: Job project ID.
|
|
181
|
-
|
|
182
|
-
Returns:
|
|
183
|
-
Job status url.
|
|
184
|
-
"""
|
|
185
|
-
project_id = self._client.project_id(project_id)
|
|
186
|
-
payload = asdict(type=type, parameters=params, name=name)
|
|
187
|
-
r: Response = self._client.post(f"/projects/{project_id}/jobs", json=payload)
|
|
188
|
-
return r.json()["link"]
|
|
189
|
-
|
|
190
|
-
def poll(self, *args, **kwargs):
|
|
191
|
-
return self._client.poll(*args, **kwargs)
|
|
192
|
-
|
|
193
|
-
def update(
|
|
194
|
-
self, job_id: str,
|
|
195
|
-
name: str | None = None,
|
|
196
|
-
project_id: str | None = None,
|
|
197
|
-
) -> Job:
|
|
198
|
-
"""Update a job.
|
|
199
|
-
|
|
200
|
-
Args:
|
|
201
|
-
job_id: ID of job to update.
|
|
202
|
-
name: New job name. The existing name remains unchanged if None.
|
|
203
|
-
project_id: Job project ID.
|
|
204
|
-
|
|
205
|
-
Returns:
|
|
206
|
-
Updated job resource.
|
|
207
|
-
"""
|
|
208
|
-
project_id = self._client.project_id(project_id)
|
|
209
|
-
url = f"/projects/{project_id}/jobs/{job_id}"
|
|
210
|
-
payload = asdict(name=name)
|
|
211
|
-
r: Response = self._client.patch(url, data=payload)
|
|
212
|
-
return Job(**r.json())
|
|
213
|
-
|
|
214
138
|
def delete(self, job_id: str, project_id: str | None = None):
|
|
215
139
|
"""Delete a job.
|
|
216
140
|
|
|
@@ -13,9 +13,11 @@ class Access(Enum):
|
|
|
13
13
|
Attributes:
|
|
14
14
|
PRIVATE: Project is private to owner.
|
|
15
15
|
PUBLIC: Project may be accessed by unauthenticated users.
|
|
16
|
+
ORG: Project may be accessed by members of the same organization.
|
|
16
17
|
"""
|
|
17
18
|
PRIVATE = "private"
|
|
18
19
|
PUBLIC = "public"
|
|
20
|
+
ORG = "org"
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
class ProjectType(Enum):
|
|
@@ -65,7 +67,7 @@ class Project:
|
|
|
65
67
|
access: Access = field(converter=check_access)
|
|
66
68
|
created: datetime = field(converter=asdatetime)
|
|
67
69
|
modified: datetime = field(converter=asdatetime)
|
|
68
|
-
type: ProjectType = field(converter=check_project_type, default=ProjectType.UNSPECIFIED)
|
|
70
|
+
type: ProjectType = field(converter=check_project_type, default=ProjectType.UNSPECIFIED)
|
|
69
71
|
thumbnail: str | None = None
|
|
70
72
|
project: dict[str, Any] | None = None
|
|
71
73
|
graph: dict[str, Any] | None = None
|
|
@@ -72,6 +72,14 @@ ZERO_VELOCITY = [
|
|
|
72
72
|
]
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def _has_motion(velocity: Optional[list]) -> bool:
|
|
76
|
+
"""True if a velocity profile has any non-zero component.
|
|
77
|
+
[time, vx, vy, vz]."""
|
|
78
|
+
if not velocity:
|
|
79
|
+
return False
|
|
80
|
+
return any(any(c for c in row[1:4]) for row in velocity)
|
|
81
|
+
|
|
82
|
+
|
|
75
83
|
class BoundaryCondition(Enum):
|
|
76
84
|
SYMMETRIC = "symmetric" # symmetry plane (acts as frictionless wall)
|
|
77
85
|
VELOCITY_DIRICHLET = "velocity_dirichlet" # velocity fixed to zero at the face
|
|
@@ -482,6 +490,30 @@ class CompressionSimulation:
|
|
|
482
490
|
else:
|
|
483
491
|
self.part_infos.append(inner_part)
|
|
484
492
|
|
|
493
|
+
# A rigid part drives the compression.
|
|
494
|
+
if parts:
|
|
495
|
+
has_piston = False
|
|
496
|
+
has_support = False
|
|
497
|
+
piston_moves = False
|
|
498
|
+
for p in parts:
|
|
499
|
+
if isinstance(p, ExperimentPistonBase):
|
|
500
|
+
has_piston = True
|
|
501
|
+
piston_moves = piston_moves or _has_motion(p.velocity)
|
|
502
|
+
elif isinstance(p, ExperimentSupportBase):
|
|
503
|
+
has_support = True
|
|
504
|
+
if not has_piston and not has_support:
|
|
505
|
+
raise ValueError(
|
|
506
|
+
"No piston part: a compression simulation needs "
|
|
507
|
+
"a rigid part to apply load. Add a part of type piston_mesh, "
|
|
508
|
+
"piston_cylinder, or piston_box."
|
|
509
|
+
)
|
|
510
|
+
# Supports are stationary by design, so only pistons must move.
|
|
511
|
+
if has_piston and not piston_moves:
|
|
512
|
+
raise ValueError(
|
|
513
|
+
"Piston velocity is zero. Set a velocity "
|
|
514
|
+
"profile of [time, vx, vy, vz] rows with a non-zero component."
|
|
515
|
+
)
|
|
516
|
+
|
|
485
517
|
self.setup_ply_files(stl_folder_path)
|
|
486
518
|
self.setup_results(output_path)
|
|
487
519
|
if client is None:
|
|
@@ -611,6 +643,7 @@ class CompressionSimulation:
|
|
|
611
643
|
auth_domain: str = "metafold3d.us.auth0.com",
|
|
612
644
|
base_url: str = "https://api.metafold3d.com/",
|
|
613
645
|
cancel_existing_workflows: bool = True,
|
|
646
|
+
project_access: Access = Access.PRIVATE,
|
|
614
647
|
) -> str:
|
|
615
648
|
"""Find an existing project by name or create a new one.
|
|
616
649
|
|
|
@@ -642,7 +675,7 @@ class CompressionSimulation:
|
|
|
642
675
|
return project_id
|
|
643
676
|
created = client.projects.create(
|
|
644
677
|
project_name,
|
|
645
|
-
access=
|
|
678
|
+
access=project_access,
|
|
646
679
|
type=ProjectType.DIGITAL_TEST_BENCH_EXPERIMENT,
|
|
647
680
|
)
|
|
648
681
|
return created.id
|
|
@@ -15,6 +15,7 @@ from metafold.simulation.compression_simulation import (
|
|
|
15
15
|
ExperimentPistonBase,
|
|
16
16
|
ExperimentPistonCylinder,
|
|
17
17
|
ExperimentPistonMesh,
|
|
18
|
+
ExperimentSupportMesh,
|
|
18
19
|
SimulationParameters,
|
|
19
20
|
WorkflowStep,
|
|
20
21
|
WorkflowStepType,
|
|
@@ -84,6 +85,7 @@ class TestConstruction:
|
|
|
84
85
|
|
|
85
86
|
def test_missing_ply_raises(self, ply_folder, tmp_path):
|
|
86
87
|
parts = [
|
|
88
|
+
ExperimentPistonCylinder(),
|
|
87
89
|
ExperimentMesh("midsole", DEFAULT_MIDSOLE_NOMINAL, "nonexistent.ply"),
|
|
88
90
|
]
|
|
89
91
|
with pytest.raises(ValueError, match="not found"):
|
|
@@ -95,6 +97,82 @@ class TestConstruction:
|
|
|
95
97
|
client=MagicMock(),
|
|
96
98
|
)
|
|
97
99
|
|
|
100
|
+
def test_no_piston_raises(self, ply_folder, tmp_path):
|
|
101
|
+
# Without a rigid part nothing drives the compression
|
|
102
|
+
parts = [
|
|
103
|
+
ExperimentMesh("midsole", DEFAULT_MIDSOLE_NOMINAL, "mid.ply"),
|
|
104
|
+
ExperimentMesh("outsole", DEFAULT_OUTSOLE, "out.ply"),
|
|
105
|
+
]
|
|
106
|
+
with pytest.raises(ValueError, match="piston"):
|
|
107
|
+
CompressionSimulation(
|
|
108
|
+
parts=parts,
|
|
109
|
+
simulation_name="t",
|
|
110
|
+
stl_folder_path=str(ply_folder),
|
|
111
|
+
output_path=str(tmp_path / "out"),
|
|
112
|
+
client=MagicMock(),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def test_zero_piston_velocity_raises(self, ply_folder, tmp_path):
|
|
116
|
+
# A piston that never moves applies no load
|
|
117
|
+
parts = [
|
|
118
|
+
ExperimentPistonCylinder(velocity=[[0.0, 0, 0, 0.0], [0.04, 0, 0, 0.0]]),
|
|
119
|
+
ExperimentMesh("midsole", DEFAULT_MIDSOLE_NOMINAL, "mid.ply"),
|
|
120
|
+
]
|
|
121
|
+
with pytest.raises(ValueError, match="velocity is zero"):
|
|
122
|
+
CompressionSimulation(
|
|
123
|
+
parts=parts,
|
|
124
|
+
simulation_name="t",
|
|
125
|
+
stl_folder_path=str(ply_folder),
|
|
126
|
+
output_path=str(tmp_path / "out"),
|
|
127
|
+
client=MagicMock(),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def test_empty_piston_velocity_raises(self, ply_folder, tmp_path):
|
|
131
|
+
parts = [
|
|
132
|
+
ExperimentPistonCylinder(velocity=[]),
|
|
133
|
+
ExperimentMesh("midsole", DEFAULT_MIDSOLE_NOMINAL, "mid.ply"),
|
|
134
|
+
]
|
|
135
|
+
with pytest.raises(ValueError, match="velocity is zero"):
|
|
136
|
+
CompressionSimulation(
|
|
137
|
+
parts=parts,
|
|
138
|
+
simulation_name="t",
|
|
139
|
+
stl_folder_path=str(ply_folder),
|
|
140
|
+
output_path=str(tmp_path / "out"),
|
|
141
|
+
client=MagicMock(),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def test_stationary_support_with_moving_piston_ok(self, ply_folder, tmp_path):
|
|
145
|
+
# Supports are stationary by design; only the piston must move.
|
|
146
|
+
parts = [
|
|
147
|
+
ExperimentPistonCylinder(), # default velocity moves
|
|
148
|
+
ExperimentSupportMesh(name="base", filename="top.ply"),
|
|
149
|
+
ExperimentMesh("midsole", DEFAULT_MIDSOLE_NOMINAL, "mid.ply"),
|
|
150
|
+
]
|
|
151
|
+
sim = CompressionSimulation(
|
|
152
|
+
parts=parts,
|
|
153
|
+
simulation_name="t",
|
|
154
|
+
stl_folder_path=str(ply_folder),
|
|
155
|
+
output_path=str(tmp_path / "out"),
|
|
156
|
+
client=MagicMock(),
|
|
157
|
+
)
|
|
158
|
+
assert len(sim.part_infos) == 3
|
|
159
|
+
|
|
160
|
+
def test_support_part_satisfies_rigid_requirement(self, ply_folder, tmp_path):
|
|
161
|
+
# A support is also a rigid driver, so it's a valid alternative to a piston.
|
|
162
|
+
parts = [
|
|
163
|
+
ExperimentSupportMesh(name="base", filename="top.ply"),
|
|
164
|
+
ExperimentMesh("midsole", DEFAULT_MIDSOLE_NOMINAL, "mid.ply"),
|
|
165
|
+
]
|
|
166
|
+
sim = CompressionSimulation(
|
|
167
|
+
parts=parts,
|
|
168
|
+
simulation_name="t",
|
|
169
|
+
stl_folder_path=str(ply_folder),
|
|
170
|
+
output_path=str(tmp_path / "out"),
|
|
171
|
+
client=MagicMock(),
|
|
172
|
+
)
|
|
173
|
+
assert len(sim.part_infos) == 2
|
|
174
|
+
|
|
175
|
+
|
|
98
176
|
class TestWorkflowStepsInput:
|
|
99
177
|
def test_accepts_workflow_step_instance(self):
|
|
100
178
|
steps = CompressionSimulation._clean_workflow_steps_input([
|
|
@@ -102,38 +102,6 @@ job_list = [
|
|
|
102
102
|
},
|
|
103
103
|
]
|
|
104
104
|
|
|
105
|
-
new_job = {
|
|
106
|
-
"id": "1",
|
|
107
|
-
"name": "My Job",
|
|
108
|
-
"type": "test_job",
|
|
109
|
-
"created": "Mon, 01 Jan 2024 00:00:00 GMT",
|
|
110
|
-
"started": "Mon, 01 Jan 2024 00:00:00 GMT",
|
|
111
|
-
"finished": "Mon, 01 Jan 2024 00:00:00 GMT",
|
|
112
|
-
"error": None,
|
|
113
|
-
"inputs": {
|
|
114
|
-
"params": {
|
|
115
|
-
"foo": "1",
|
|
116
|
-
"bar": "a",
|
|
117
|
-
"baz": "[2, \"b\"]",
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
"outputs": {
|
|
121
|
-
"params": None,
|
|
122
|
-
},
|
|
123
|
-
"needs": [],
|
|
124
|
-
"project_id": "1",
|
|
125
|
-
"workflow_id": None,
|
|
126
|
-
"assets": [],
|
|
127
|
-
"parameters": {
|
|
128
|
-
"foo": "1",
|
|
129
|
-
"bar": "a",
|
|
130
|
-
"baz": "[2, \"b\"]",
|
|
131
|
-
},
|
|
132
|
-
"meta": None,
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
poll_count: int = 0
|
|
136
|
-
|
|
137
105
|
|
|
138
106
|
class MockRequestHandler(BaseHTTPRequestHandler):
|
|
139
107
|
def do_GET(self):
|
|
@@ -156,51 +124,6 @@ class MockRequestHandler(BaseHTTPRequestHandler):
|
|
|
156
124
|
payload = deepcopy(job_list[-1])
|
|
157
125
|
payload["assets"] = [asset_json]
|
|
158
126
|
self.wfile.write(json.dumps(payload).encode())
|
|
159
|
-
elif u.path == "/projects/1/jobs/1/status":
|
|
160
|
-
global poll_count
|
|
161
|
-
poll_count += 1
|
|
162
|
-
if poll_count < 3:
|
|
163
|
-
self.send_response(HTTPStatus.ACCEPTED)
|
|
164
|
-
payload = deepcopy(new_job)
|
|
165
|
-
payload["state"] = "started"
|
|
166
|
-
else:
|
|
167
|
-
self.send_response(HTTPStatus.CREATED)
|
|
168
|
-
payload = deepcopy(new_job)
|
|
169
|
-
payload.update({
|
|
170
|
-
"state": "success",
|
|
171
|
-
"assets": [asset_json],
|
|
172
|
-
})
|
|
173
|
-
self.send_header("Content-Type", "application/json")
|
|
174
|
-
self.end_headers()
|
|
175
|
-
self.wfile.write(json.dumps(payload).encode())
|
|
176
|
-
else:
|
|
177
|
-
self.send_error(HTTPStatus.NOT_FOUND)
|
|
178
|
-
|
|
179
|
-
def do_POST(self):
|
|
180
|
-
if self.path == "/projects/1/jobs":
|
|
181
|
-
self.send_response(HTTPStatus.ACCEPTED)
|
|
182
|
-
self.send_header("Content-Type", "application/json")
|
|
183
|
-
self.end_headers()
|
|
184
|
-
payload = deepcopy(new_job)
|
|
185
|
-
payload.update({
|
|
186
|
-
"state": "pending",
|
|
187
|
-
"link": "http://localhost:8000/projects/1/jobs/1/status",
|
|
188
|
-
})
|
|
189
|
-
self.wfile.write(json.dumps(payload).encode())
|
|
190
|
-
else:
|
|
191
|
-
self.send_error(HTTPStatus.NOT_FOUND)
|
|
192
|
-
|
|
193
|
-
def do_PATCH(self):
|
|
194
|
-
if self.path == "/projects/1/jobs/1":
|
|
195
|
-
self.send_response(HTTPStatus.OK)
|
|
196
|
-
self.send_header("Content-Type", "application/json")
|
|
197
|
-
self.end_headers()
|
|
198
|
-
payload = deepcopy(job_list[-1])
|
|
199
|
-
payload.update({
|
|
200
|
-
"name": "baz",
|
|
201
|
-
"assets": [asset_json],
|
|
202
|
-
})
|
|
203
|
-
self.wfile.write(json.dumps(payload).encode())
|
|
204
127
|
else:
|
|
205
128
|
self.send_error(HTTPStatus.NOT_FOUND)
|
|
206
129
|
|
|
@@ -245,63 +168,3 @@ def test_get_job(client):
|
|
|
245
168
|
parameters=default_params,
|
|
246
169
|
meta=None,
|
|
247
170
|
)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
def test_run_job(client):
|
|
251
|
-
params = {
|
|
252
|
-
"foo": "1",
|
|
253
|
-
"bar": "a",
|
|
254
|
-
"baz": "[2, \"b\"]",
|
|
255
|
-
}
|
|
256
|
-
j = client.jobs.run("test_job", params, name="My Job")
|
|
257
|
-
assert j == Job(
|
|
258
|
-
id="1",
|
|
259
|
-
name="My Job",
|
|
260
|
-
type="test_job",
|
|
261
|
-
state="success",
|
|
262
|
-
created=default_dt,
|
|
263
|
-
started=default_dt,
|
|
264
|
-
finished=default_dt,
|
|
265
|
-
inputs=IO(params=params),
|
|
266
|
-
outputs=IO(),
|
|
267
|
-
needs=[],
|
|
268
|
-
project_id="1",
|
|
269
|
-
workflow_id=None,
|
|
270
|
-
assets=[asset_obj],
|
|
271
|
-
parameters=params,
|
|
272
|
-
meta=None,
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
def test_poll_job(client):
|
|
277
|
-
params = {
|
|
278
|
-
"foo": "1",
|
|
279
|
-
"bar": "a",
|
|
280
|
-
"baz": "[2, \"b\"]",
|
|
281
|
-
}
|
|
282
|
-
url = client.jobs.run_status("test_job", params, name="My Job")
|
|
283
|
-
assert url == "http://localhost:8000/projects/1/jobs/1/status"
|
|
284
|
-
|
|
285
|
-
r = client.jobs.poll(url)
|
|
286
|
-
assert Job(**r.json()) == Job(
|
|
287
|
-
id="1",
|
|
288
|
-
name="My Job",
|
|
289
|
-
type="test_job",
|
|
290
|
-
state="success",
|
|
291
|
-
created=default_dt,
|
|
292
|
-
started=default_dt,
|
|
293
|
-
finished=default_dt,
|
|
294
|
-
inputs=IO(params=params),
|
|
295
|
-
outputs=IO(),
|
|
296
|
-
needs=[],
|
|
297
|
-
project_id="1",
|
|
298
|
-
workflow_id=None,
|
|
299
|
-
assets=[asset_obj],
|
|
300
|
-
parameters=params,
|
|
301
|
-
meta=None,
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
def test_update_job(client):
|
|
306
|
-
j = client.jobs.update("1", name="baz")
|
|
307
|
-
assert j.name == "baz"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|