Skip to content

Commit 43b553d

Browse files
committed
Save preview params (#191)
Updated the launch command with -sp, --save_preview option. If used the launch request will be generated like usual, but the inputs selected will be merged into the local manifest and into the protocol that was selected. This allows the user to use real and up to date container information and state without manually editing your manifest.json. ``` transcriptic launch -p p1fjfghp4azvd2 --local -sp flowcytometry ``` ``` Usage: transcriptic launch [OPTIONS] PROTOCOL PARAMETERS_FILE Configure and launch a protocol either using the local manifest file or remotely. If no parameters are specified, uses the webapp to select the inputs. Options: -p, --project PROJECT_ID Project id or name context for configuring the protocol. Use `transcriptic projects` command to list existing projects. -t, --title RUN_TITLE If specified, will apply custom title to run created, default run titlewill be the DISPLAY- NAME_MM_DD_YYYY of the protocol selected. --save_input FILE Save the protocol or parameters input JSON in a file. This is useful for debugging a protocol. --local If specified, the protocol will launch a local protocol and submit a run. --accept_quote If specified, the quote will automatically be accepted, and a run will be directly submitted. --pm PAYMENT_METHOD_ID Payment id to be used for run submission. Use `transcriptic payments` command to list existing payment methods. --test Submit this run in test mode --pkg PACKAGE_ID Package ID for discriminating between protocols with identical names -sp, --save_preview Save the protocol preview parameters and refs <== New flag selected as input and merge into local manifest.json. This is useful for debugging a protocol. -h, --help Show this message and exit. ```
1 parent 3198b2f commit 43b553d

7 files changed

Lines changed: 598 additions & 94 deletions

File tree

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def run_tests(self):
3737
"pytest>=5.4, <6",
3838
"pytest-cov>=2, <3",
3939
"tox>=3.15, <4",
40+
"responses>=0.13.4",
4041
]
4142

4243
doc_deps = [
@@ -76,6 +77,7 @@ def run_tests(self):
7677
"pycryptodome==3.9.6",
7778
"python-magic>=0.4,<1",
7879
"Jinja2>=2.0,<3",
80+
"responses>=0.13.4",
7981
],
8082
extras_require={
8183
"jupyter": jupyter_deps,

test/commands/launch_test.py

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import pytest
2+
import responses
3+
4+
from transcriptic import commands
5+
from transcriptic.util import PreviewParameters
6+
7+
8+
class TestLaunch:
9+
"""
10+
Note: Underlying helper functions are tested separately in `TestUtils` class. This
11+
uses monkeypatching to mock out those functions.
12+
"""
13+
14+
project_id = "p123"
15+
launch_request_id = "lr123"
16+
quick_launch_id = "quick123"
17+
protocolname = "protocolname"
18+
title = "launch_title"
19+
run_id = "r123"
20+
mock_baseurl = "http://mock-api/mock"
21+
22+
@pytest.fixture(scope="function")
23+
def simple_manifest(self, simple_protocol):
24+
yield {"format": "python", "license": "MIT", "protocols": [simple_protocol]}
25+
26+
@pytest.fixture(scope="function")
27+
def simple_protocol(self):
28+
yield {
29+
"name": self.protocolname,
30+
"display_name": "displayname",
31+
"categories": [],
32+
"description": "desc",
33+
"version": "1.0.0",
34+
"command_string": "python3 -m protocol.main",
35+
"inputs": {},
36+
"preview": {"parameters": {}, "refs": {}},
37+
}
38+
39+
@pytest.fixture
40+
def autoprotocol(self):
41+
"""Make a temp autoprotocol file"""
42+
yield {
43+
"instructions": [
44+
{"op": "provision", "x_human": True},
45+
{"op": "uncover"},
46+
{"op": "spin"},
47+
{"op": "cover"},
48+
]
49+
}
50+
51+
@pytest.fixture(scope="function")
52+
def run_protocol(self, autoprotocol):
53+
return lambda api, manifest, protocol_obj, inputs: autoprotocol
54+
55+
@pytest.fixture(scope="function")
56+
def load_manifest(self, simple_manifest):
57+
return lambda: simple_manifest
58+
59+
@pytest.fixture(scope="function")
60+
def load_protocol(self, simple_protocol):
61+
return lambda manifest, protocol_name: simple_protocol
62+
63+
@pytest.fixture(scope="function")
64+
def format_url(self):
65+
return lambda path: f"{self.mock_baseurl}/{self.project_id}/runs/{self.run_id}"
66+
67+
@pytest.fixture(scope="function")
68+
def _get_quick_launch(self):
69+
return lambda api, protocol_obj, project: {
70+
"raw_inputs": {"parameters": {"config": {"foo": "bar"}}, "refs": {}}
71+
}
72+
73+
@pytest.fixture(scope="function")
74+
def _get_launch_request(self):
75+
return lambda api, params, protocol_obj, test: [
76+
self.launch_request_id,
77+
{"generation_errors": []},
78+
]
79+
80+
@responses.activate
81+
def test_local(
82+
self,
83+
monkeypatch,
84+
test_connection,
85+
_get_launch_request,
86+
_get_quick_launch,
87+
format_url,
88+
load_manifest,
89+
load_protocol,
90+
run_protocol,
91+
local=True,
92+
):
93+
monkeypatch.setattr(commands, "load_manifest", load_manifest)
94+
monkeypatch.setattr(commands, "load_protocol", load_protocol)
95+
monkeypatch.setattr(
96+
commands, "get_project_id", lambda api, project: self.project_id
97+
)
98+
monkeypatch.setattr(commands, "_get_launch_request", _get_launch_request)
99+
monkeypatch.setattr(commands, "_get_quick_launch", _get_quick_launch)
100+
monkeypatch.setattr(test_connection, "url", format_url)
101+
monkeypatch.setattr(commands, "run_protocol", run_protocol)
102+
responses.add(
103+
responses.POST,
104+
test_connection.get_route(
105+
"create_quick_launch", project_id=self.project_id
106+
),
107+
json={"id": self.quick_launch_id},
108+
)
109+
responses.add(
110+
responses.POST,
111+
test_connection.get_route(
112+
"resolve_quick_launch_inputs",
113+
project_id=self.project_id,
114+
quick_launch_id=self.quick_launch_id,
115+
),
116+
json={"inputs": {"parameters": {}, "refs": {}}},
117+
)
118+
# Test that it will run without error since the autoprotocol generated
119+
# gets dumped out in the terminal and not returned
120+
commands.launch(
121+
test_connection,
122+
protocol=self.protocolname,
123+
project=self.project_id,
124+
title=self.title,
125+
save_input=False,
126+
local=local,
127+
accept_quote=True,
128+
params=None,
129+
)
130+
131+
@responses.activate
132+
def test_not_local(
133+
self,
134+
monkeypatch,
135+
test_connection,
136+
_get_launch_request,
137+
_get_quick_launch,
138+
format_url,
139+
local=False,
140+
):
141+
monkeypatch.setattr(
142+
commands, "get_project_id", lambda api, project: self.project_id
143+
)
144+
monkeypatch.setattr(commands, "_get_launch_request", _get_launch_request)
145+
monkeypatch.setattr(commands, "_get_quick_launch", _get_quick_launch)
146+
monkeypatch.setattr(test_connection, "url", format_url)
147+
148+
responses.add(
149+
responses.GET,
150+
test_connection.get_route("get_protocols", org_id="mock"),
151+
json=[
152+
{
153+
"id": "protocol123",
154+
"name": self.protocolname,
155+
"created_at": "today",
156+
"package_name": "pkgname",
157+
"package_id": "pkg123",
158+
"release_id": "rel123",
159+
"license": "MIT",
160+
"published": True,
161+
"display_name": "displayname",
162+
"categories": [],
163+
"description": "desc",
164+
"version": "1.0.0",
165+
"command_string": "python3 -m protocol.main",
166+
"inputs": {},
167+
"preview": {"parameters": {}, "refs": {}},
168+
}
169+
],
170+
)
171+
responses.add(
172+
responses.POST,
173+
test_connection.get_route(
174+
"submit_launch_request",
175+
launch_request_id=self.launch_request_id,
176+
project_id=self.project_id,
177+
protocol_id="protocol123",
178+
title=self.title,
179+
),
180+
json={"id": f"{self.run_id}"},
181+
)
182+
183+
actual = commands.launch(
184+
test_connection,
185+
protocol=self.protocolname,
186+
project=self.project_id,
187+
title=self.title,
188+
save_input=False,
189+
local=local,
190+
accept_quote=True,
191+
params=None,
192+
)
193+
expected = f"{self.mock_baseurl}/{self.project_id}/runs/{self.run_id}"
194+
assert actual == expected
195+
196+
@responses.activate
197+
def test_save_preview(
198+
self,
199+
monkeypatch,
200+
test_connection,
201+
load_manifest,
202+
load_protocol,
203+
_get_launch_request,
204+
_get_quick_launch,
205+
format_url,
206+
):
207+
monkeypatch.setattr(commands, "load_manifest", load_manifest)
208+
monkeypatch.setattr(commands, "load_protocol", load_protocol)
209+
monkeypatch.setattr(commands, "_get_quick_launch", _get_quick_launch)
210+
manifest = commands.load_manifest()
211+
protocol_obj = commands.load_protocol(
212+
manifest=manifest, protocol_name=self.protocolname
213+
)
214+
quick_launch = commands._get_quick_launch(
215+
test_connection, protocol_obj, self.project_id
216+
)
217+
params = dict(parameters=quick_launch["raw_inputs"])
218+
pp = PreviewParameters(test_connection, params["parameters"], protocol_obj)
219+
220+
# Assert that the PreviewParameters.preview does not match current manfest object
221+
assert manifest["protocols"][0].get("preview") == protocol_obj.get("preview")
222+
assert manifest["protocols"][0].get("preview") != pp.preview.get("preview")
223+
224+
# Merge PreviewParameters.preview into a copy of the manifest
225+
pp.merge(manifest)
226+
227+
# Assert that the merge placed the PreviewParameters.preview into the merged_manifest
228+
assert pp.merged_manifest["protocols"][0].get("preview") != protocol_obj.get(
229+
"preview"
230+
)
231+
assert pp.merged_manifest["protocols"][0].get("preview") == pp.preview.get(
232+
"preview"
233+
)

transcriptic/cli.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,14 @@ def compile_cmd(protocol_name, args):
565565
required=False,
566566
help="Package ID for discriminating between protocols with identical names",
567567
)
568+
@click.option(
569+
"--save_preview",
570+
"-sp",
571+
is_flag=True,
572+
required=False,
573+
help="Save the protocol preview parameters and refs selected as input and merge into local "
574+
"manifest.json. This is useful for debugging a protocol.",
575+
)
568576
@click.pass_context
569577
def launch_cmd(
570578
ctx,
@@ -578,6 +586,7 @@ def launch_cmd(
578586
pm=None,
579587
test=None,
580588
pkg=None,
589+
save_preview=False,
581590
):
582591
"""Configure and launch a protocol either using the local manifest file or remotely.
583592
If no parameters are specified, uses the webapp to select the inputs."""
@@ -594,6 +603,7 @@ def launch_cmd(
594603
pm=None,
595604
test=None,
596605
pkg=None,
606+
save_preview=save_preview,
597607
)
598608

599609

0 commit comments

Comments
 (0)