podojo-cli 1.2.0__tar.gz → 1.4.0__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.
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/CHANGELOG.md +10 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/PKG-INFO +1 -1
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/pyproject.toml +1 -1
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/client.py +24 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/commands/usertests.py +37 -8
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/uv.lock +1 -1
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/.github/workflows/publish.yml +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/.gitignore +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/CLAUDE.md +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/LICENSE +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/README.md +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/__init__.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/commands/__init__.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/commands/auth.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/commands/interviews.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/commands/projects.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/commands/showreel.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/commands/synth.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/commands/transcripts.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/commands/videos.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/config.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/main.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/synth/__init__.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/synth/driver.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/synth/session.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/version_check.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/video/__init__.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/src/podojo_cli/video/showreel.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/tests/conftest.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/tests/test_auth.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/tests/test_interviews.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/tests/test_projects.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/tests/test_showreel.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/tests/test_transcripts.py +0 -0
- {podojo_cli-1.2.0 → podojo_cli-1.4.0}/tests/test_usertests.py +0 -0
|
@@ -5,6 +5,16 @@ All notable changes to the Podojo CLI will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org).
|
|
7
7
|
|
|
8
|
+
## [1.4.0] - 2026-05-31
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Support `image_file:` in usertest step config — a local image path that is uploaded to Podojo-hosted storage on `create`/`update` and replaced with the hosted URL. Relative paths resolve against the YAML file's location. Externally-hosted `image:` URLs continue to work.
|
|
12
|
+
|
|
13
|
+
## [1.3.0] - 2026-05-25
|
|
14
|
+
|
|
15
|
+
### Removed
|
|
16
|
+
- Drop `privacy_text` field from usertest config. It was never used in practice, and the participant frontend no longer renders it.
|
|
17
|
+
|
|
8
18
|
## [1.2.0] - 2026-05-25
|
|
9
19
|
|
|
10
20
|
### Removed
|
|
@@ -132,6 +132,30 @@ class PodojoClient:
|
|
|
132
132
|
r.raise_for_status()
|
|
133
133
|
return r.json()
|
|
134
134
|
|
|
135
|
+
IMAGE_CONTENT_TYPES = {
|
|
136
|
+
".png": "image/png",
|
|
137
|
+
".jpg": "image/jpeg",
|
|
138
|
+
".jpeg": "image/jpeg",
|
|
139
|
+
".webp": "image/webp",
|
|
140
|
+
".gif": "image/gif",
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
def upload_usertest_image(self, file_path: Path) -> dict:
|
|
144
|
+
content_type = self.IMAGE_CONTENT_TYPES.get(file_path.suffix.lower())
|
|
145
|
+
if content_type is None:
|
|
146
|
+
raise ValueError(
|
|
147
|
+
f"Unsupported image type '{file_path.suffix}'. Allowed: PNG, JPEG, WebP, GIF."
|
|
148
|
+
)
|
|
149
|
+
with file_path.open("rb") as f:
|
|
150
|
+
r = httpx.post(
|
|
151
|
+
f"{self.base_url}/usertests/images",
|
|
152
|
+
files={"file": (file_path.name, f, content_type)},
|
|
153
|
+
headers=self._headers(),
|
|
154
|
+
timeout=httpx.Timeout(None),
|
|
155
|
+
)
|
|
156
|
+
r.raise_for_status()
|
|
157
|
+
return r.json()
|
|
158
|
+
|
|
135
159
|
def create_usertest(self, data: dict) -> dict:
|
|
136
160
|
r = httpx.post(
|
|
137
161
|
f"{self.base_url}/usertests",
|
|
@@ -36,7 +36,7 @@ EXAMPLE_YAML = """\
|
|
|
36
36
|
# Podojo Unmoderated User Test Configuration
|
|
37
37
|
#
|
|
38
38
|
# Required fields: usertest_id, title, logo, prototype_url, steps
|
|
39
|
-
# Optional fields: welcome_text,
|
|
39
|
+
# Optional fields: welcome_text, promo_code, promo_code_info,
|
|
40
40
|
# project_name, live, collect_contact
|
|
41
41
|
|
|
42
42
|
usertest_id: checkout-usability-v1
|
|
@@ -53,11 +53,6 @@ welcome_text: |
|
|
|
53
53
|
|
|
54
54
|
This session takes about 10 minutes.
|
|
55
55
|
|
|
56
|
-
# Optional: privacy notice
|
|
57
|
-
privacy_text: |
|
|
58
|
-
Your responses are anonymous and will only be used to improve
|
|
59
|
-
our product. You can stop at any time.
|
|
60
|
-
|
|
61
56
|
# Optional: reward for participants
|
|
62
57
|
promo_code: THANKS10
|
|
63
58
|
promo_code_info: "Use this code for 10% off your next purchase"
|
|
@@ -75,7 +70,10 @@ project_name: checkout-redesign-q1
|
|
|
75
70
|
# Each step requires: type ("screen" or "prototype") and title
|
|
76
71
|
# Screen steps should have a variant: "question" (open-ended), "task" (action-based),
|
|
77
72
|
# or "instruction" (briefing screen shown before a prototype step — uses a "Continue" button)
|
|
78
|
-
# Optional per step: text (markdown),
|
|
73
|
+
# Optional per step: text (markdown), and either:
|
|
74
|
+
# image: a URL to an externally-hosted image, or
|
|
75
|
+
# image_file: a path to a local image (uploaded to Podojo storage on create/update;
|
|
76
|
+
# relative paths resolve against this YAML file's location)
|
|
79
77
|
steps:
|
|
80
78
|
- type: screen
|
|
81
79
|
variant: question
|
|
@@ -83,7 +81,7 @@ steps:
|
|
|
83
81
|
text: |
|
|
84
82
|
Look at this homepage screenshot.
|
|
85
83
|
What stands out to you first?
|
|
86
|
-
|
|
84
|
+
image_file: ./screenshots/homepage.png
|
|
87
85
|
|
|
88
86
|
- type: screen
|
|
89
87
|
variant: instruction
|
|
@@ -165,6 +163,35 @@ def _load_yaml(path: Path) -> dict:
|
|
|
165
163
|
return data
|
|
166
164
|
|
|
167
165
|
|
|
166
|
+
def _resolve_image_files(data: dict, base_dir: Path, client: PodojoClient) -> None:
|
|
167
|
+
"""Upload any step `image_file` (local path) and replace it with the hosted URL."""
|
|
168
|
+
steps = data.get("steps")
|
|
169
|
+
if not isinstance(steps, list):
|
|
170
|
+
return
|
|
171
|
+
for i, step in enumerate(steps, 1):
|
|
172
|
+
if not isinstance(step, dict) or "image_file" not in step:
|
|
173
|
+
continue
|
|
174
|
+
raw_path = step.pop("image_file")
|
|
175
|
+
if not raw_path:
|
|
176
|
+
continue
|
|
177
|
+
image_path = Path(raw_path)
|
|
178
|
+
if not image_path.is_absolute():
|
|
179
|
+
image_path = (base_dir / image_path).resolve()
|
|
180
|
+
if not image_path.exists():
|
|
181
|
+
console.print(f"[red]Error:[/red] Step {i}: image file not found: {image_path}")
|
|
182
|
+
raise typer.Exit(1)
|
|
183
|
+
try:
|
|
184
|
+
result = client.upload_usertest_image(image_path)
|
|
185
|
+
except ValueError as ve:
|
|
186
|
+
console.print(f"[red]Error:[/red] Step {i}: {ve}")
|
|
187
|
+
raise typer.Exit(1)
|
|
188
|
+
except httpx.HTTPStatusError as he:
|
|
189
|
+
console.print(f"[red]Error:[/red] Step {i}: image upload failed: {_format_api_error(he)}")
|
|
190
|
+
raise typer.Exit(1)
|
|
191
|
+
step["image"] = result["url"]
|
|
192
|
+
console.print(f"[dim]Uploaded image for step {i}: {result['url']}[/dim]")
|
|
193
|
+
|
|
194
|
+
|
|
168
195
|
def _format_api_error(e: httpx.HTTPStatusError) -> str:
|
|
169
196
|
"""Format API error into actionable message."""
|
|
170
197
|
try:
|
|
@@ -262,6 +289,7 @@ def create_usertest(
|
|
|
262
289
|
data["project_name"] = data["usertest_id"]
|
|
263
290
|
|
|
264
291
|
client = PodojoClient()
|
|
292
|
+
_resolve_image_files(data, from_file.parent, client)
|
|
265
293
|
try:
|
|
266
294
|
result = client.create_usertest(data)
|
|
267
295
|
except httpx.HTTPStatusError as e:
|
|
@@ -293,6 +321,7 @@ def update_usertest(
|
|
|
293
321
|
data = _load_yaml(from_file)
|
|
294
322
|
|
|
295
323
|
client = PodojoClient()
|
|
324
|
+
_resolve_image_files(data, from_file.parent, client)
|
|
296
325
|
try:
|
|
297
326
|
result = client.update_usertest(usertest_id, data)
|
|
298
327
|
except httpx.HTTPStatusError as e:
|
|
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
|
|
File without changes
|
|
File without changes
|