calkit-python 0.50.1__py3-none-any.whl → 0.50.2__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.
- calkit/agent_skills/build-paper-pipeline/SKILL.md +1 -1
- calkit/agent_skills/check-questions/SKILL.md +76 -20
- calkit/agent_skills/conventions/SKILL.md +18 -17
- calkit/cli/list.py +116 -130
- calkit/cli/show.py +515 -1
- calkit/cli/update.py +163 -0
- calkit/detect.py +67 -2
- calkit/dvc/core.py +32 -0
- calkit/git.py +13 -0
- calkit/latex.py +26 -7
- calkit/models/core.py +60 -3
- calkit/pipeline.py +382 -1
- calkit/questions.py +717 -104
- calkit/resources/latex/calkit.sty +70 -17
- calkit/results.py +139 -0
- calkit/tables.py +260 -0
- calkit/tests/cli/test_check.py +22 -17
- calkit/tests/cli/test_latex.py +75 -0
- calkit/tests/cli/test_list.py +75 -84
- calkit/tests/cli/test_show.py +281 -0
- calkit/tests/cli/test_update.py +93 -0
- calkit/tests/test_detect.py +51 -0
- calkit/tests/test_latex.py +35 -1
- calkit/tests/test_pipeline.py +277 -3
- calkit/tests/test_questions.py +388 -2
- calkit/tests/test_results.py +121 -0
- calkit/tests/test_tables.py +169 -0
- {calkit_python-0.50.1.dist-info → calkit_python-0.50.2.dist-info}/METADATA +27 -13
- {calkit_python-0.50.1.dist-info → calkit_python-0.50.2.dist-info}/RECORD +45 -41
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/etc/jupyter/jupyter_server_config.d/calkit.json +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/package.json +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/static/506.c34b070098184e33.js +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/static/57d13a7399cec3c5.png +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/static/616.528427eb54a4a0c0.js +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/static/740.6bf87276e9e5788a.js +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/static/899.f9b9f9bf705a6493.js +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/static/935.46ecc6bf99aa593a.js +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/static/remoteEntry.109a3a8379365e59.js +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/static/style.js +0 -0
- {calkit_python-0.50.1.data → calkit_python-0.50.2.data}/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json +0 -0
- {calkit_python-0.50.1.dist-info → calkit_python-0.50.2.dist-info}/WHEEL +0 -0
- {calkit_python-0.50.1.dist-info → calkit_python-0.50.2.dist-info}/entry_points.txt +0 -0
- {calkit_python-0.50.1.dist-info → calkit_python-0.50.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -95,7 +95,7 @@ building a pipeline from scratch.
|
|
|
95
95
|
number in it that is not traceable to a pipeline output. A finding there
|
|
96
96
|
is a hole you closed by typing rather than by building, so go back to
|
|
97
97
|
step 3 for it. Show the user the rendered sentences
|
|
98
|
-
(`calkit
|
|
98
|
+
(`calkit show question <n>`) and the built document. Never commit a derived
|
|
99
99
|
output yourself, and never edit a results file by hand.
|
|
100
100
|
|
|
101
101
|
## Data a person has to collect
|
|
@@ -12,7 +12,21 @@ An answer in `calkit.yaml` is a claim about the evidence as it was when the
|
|
|
12
12
|
answer was last edited. The pipeline keeps the evidence current; nothing
|
|
13
13
|
keeps the prose current. Calkit's deterministic check catches evidence
|
|
14
14
|
changing underneath an answer. This skill covers what a check cannot:
|
|
15
|
-
whether the sentence still follows from the evidence
|
|
15
|
+
whether the sentence still follows from the evidence, and whether the
|
|
16
|
+
question's `approach` describes what its pipeline stages actually do.
|
|
17
|
+
|
|
18
|
+
People sign off on a question in two ways, and the two are easy to mix up:
|
|
19
|
+
|
|
20
|
+
- **Verification**, under `verifications`: the stages behind the evidence
|
|
21
|
+
implement the `approach` correctly, i.e., the answer was worked out
|
|
22
|
+
right. It is about code against the approach sentence, not about whether
|
|
23
|
+
the answer is true.
|
|
24
|
+
- **Validation**, under `validations`: the approach suits the question and
|
|
25
|
+
the evidence justifies the answer, i.e., the right thing was worked out.
|
|
26
|
+
It is about whether the claim is logical and defensible, not about the
|
|
27
|
+
code.
|
|
28
|
+
|
|
29
|
+
A sign-off is theirs, not yours: you read and report, and they sign off.
|
|
16
30
|
|
|
17
31
|
## Division of labor
|
|
18
32
|
|
|
@@ -26,6 +40,15 @@ Deterministic, done by `calkit check questions` — never re-derive by hand:
|
|
|
26
40
|
- every publication `label` still exists in the LaTeX source;
|
|
27
41
|
- no evidence has changed (Git history for Git-tracked outputs, `dvc.lock`
|
|
28
42
|
for DVC-tracked ones) since the commit that last edited the question;
|
|
43
|
+
- no verification is stale, i.e., neither the approach nor any of the
|
|
44
|
+
question's stages, their definitions or what `dvc.lock` lists as their
|
|
45
|
+
dependencies, has changed since the commit that added it;
|
|
46
|
+
- no validation is stale, i.e., neither the question, its hypothesis,
|
|
47
|
+
approach, or answer, nor its evidence has changed since the commit that
|
|
48
|
+
added it;
|
|
49
|
+
- which pipeline stages are behind each question's evidence, upstream
|
|
50
|
+
first, listed by `calkit check questions --verbose` and
|
|
51
|
+
`calkit show question <n>`;
|
|
29
52
|
- each `value` entry reads a file a pipeline stage produces, or one
|
|
30
53
|
declared with `imported_from`; anything else is an error, since a number
|
|
31
54
|
nothing computes is a magic number;
|
|
@@ -37,7 +60,9 @@ Judgment, done here — the check reads paths and hashes, and cannot read a
|
|
|
37
60
|
sentence:
|
|
38
61
|
|
|
39
62
|
- **does the evidence actually support the answer**, which is the reason
|
|
40
|
-
this skill exists;
|
|
63
|
+
this skill exists and what a validation vouches for;
|
|
64
|
+
- **does the `approach` describe what the stages implement**, which is what
|
|
65
|
+
a verification vouches for;
|
|
41
66
|
- does the answer still follow from the evidence, given what changed;
|
|
42
67
|
- are numbers retyped into the prose that should be `{name}` placeholders;
|
|
43
68
|
- does a claim resting on a threshold use a conditional answer, with a
|
|
@@ -45,7 +70,7 @@ sentence:
|
|
|
45
70
|
- is the answer concise, and does it point at the publication section that
|
|
46
71
|
carries the argument rather than repeating it.
|
|
47
72
|
|
|
48
|
-
## Does the evidence support the answer?
|
|
73
|
+
## Does the evidence support the answer? (validation)
|
|
49
74
|
|
|
50
75
|
A question can pass every deterministic check and still be wrong: the
|
|
51
76
|
paths resolve, the numbers render, nothing has changed since — and the
|
|
@@ -85,6 +110,29 @@ evidence does not support the sentence, propose the sentence it does
|
|
|
85
110
|
support and let the user decide; never edit the answer to match the
|
|
86
111
|
evidence without showing them both.
|
|
87
112
|
|
|
113
|
+
## Does the approach match the implementation? (verification)
|
|
114
|
+
|
|
115
|
+
`approach` is one sentence on how the question is answered. The stages it
|
|
116
|
+
summarizes are derived from the evidence, so they can't drift, but the
|
|
117
|
+
sentence can. For each question with an `approach`:
|
|
118
|
+
|
|
119
|
+
1. Get its stages from `calkit check questions --verbose --json` (the
|
|
120
|
+
`stages` list) and read each one's definition in `calkit.yaml`, then the
|
|
121
|
+
script, notebook, or command it runs.
|
|
122
|
+
2. Check that the method the sentence names is the one the code runs,
|
|
123
|
+
e.g., a "discrete-event simulation" that is really a closed-form
|
|
124
|
+
calculation, or "a regression on all sites" that filters half of them.
|
|
125
|
+
3. Check that anything the sentence states as fixed, e.g., a sample size,
|
|
126
|
+
a model, a dataset, a parameter, matches the code and its inputs.
|
|
127
|
+
4. Check that no stage in the chain does something the sentence would lead
|
|
128
|
+
a reader to rule out, e.g., an exclusion step or a different estimator.
|
|
129
|
+
|
|
130
|
+
Report mismatches with the line of code and the words they contradict, and
|
|
131
|
+
propose a corrected sentence. A question with an answer and no `approach`
|
|
132
|
+
is worth one: draft it from the stages and let the user decide. Whether
|
|
133
|
+
the approach is the right one for the question belongs to validation, not
|
|
134
|
+
here.
|
|
135
|
+
|
|
88
136
|
## Procedure
|
|
89
137
|
|
|
90
138
|
1. Run `calkit check questions --json` and read the report. Questions with
|
|
@@ -94,41 +142,49 @@ evidence without showing them both.
|
|
|
94
142
|
review or the answer is one the paper leans on.
|
|
95
143
|
2. For each **stale** question, the report names the evidence that changed
|
|
96
144
|
and the commit the answer dates from. Read the rendered answer
|
|
97
|
-
(`calkit
|
|
145
|
+
(`calkit show question <n>`) against the current evidence, and if it helps,
|
|
98
146
|
the old evidence (`git show <commit>:<path>`). Decide:
|
|
99
|
-
- **The claim still holds**: say so,
|
|
100
|
-
that
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
147
|
+
- **The claim still holds**: say so, with what you checked. Recording
|
|
148
|
+
that is the user's sign-off, not yours: suggest they run
|
|
149
|
+
`calkit update question <n> --validate "<what they checked>"`, or
|
|
150
|
+
`--verify` for the approach against the code, adding
|
|
151
|
+
`--with-ai "<your model>"` if your reading informed theirs. Never add
|
|
152
|
+
or refresh a sign-off yourself unless they ask you to, and then only
|
|
153
|
+
with their Git identity and that disclosure.
|
|
104
154
|
- **The claim no longer holds**: draft a corrected answer, show the user
|
|
105
155
|
the old and new text side by side with the values that changed, and
|
|
106
156
|
only after they agree, edit `calkit.yaml`.
|
|
107
157
|
- **The evidence changed because a stage is not reproducible** (same
|
|
108
158
|
inputs, different output): that is a pipeline defect, not an answer
|
|
109
159
|
defect. Report it as such and do not rewrite the answer to match noise.
|
|
110
|
-
3. For each **
|
|
160
|
+
3. For each **stale verification or validation**, the report names what
|
|
161
|
+
changed since the person signed off: for a verification, the approach,
|
|
162
|
+
a stage, or a dependency it ran; for a validation, evidence, with old
|
|
163
|
+
and new values, or the question's own text. Redo the matching reading
|
|
164
|
+
above against the version they signed off on
|
|
165
|
+
(`git show <commit>:calkit.yaml`), and tell the user who needs to look
|
|
166
|
+
again and at what.
|
|
167
|
+
4. For each **unattributed** evidence entry, ask where the file came
|
|
111
168
|
from. If a stage should produce it, that is a pipeline gap worth
|
|
112
169
|
reporting. If it was imported or made by hand, declare it under
|
|
113
170
|
`figures`, `datasets`, or `publications` with `imported_from` or
|
|
114
171
|
`created_by` so the project says so. A `value` entry with no stage is
|
|
115
172
|
reported as an error rather than `unattributed`: give it a stage.
|
|
116
|
-
|
|
173
|
+
5. For each **error**, fix the reference: a missing path means the pipeline
|
|
117
174
|
has not been run or pulled; a bad key or placeholder means a results
|
|
118
175
|
file was restructured; a missing label means the publication was
|
|
119
176
|
reorganized. Do not delete evidence to make an error go away.
|
|
120
|
-
|
|
177
|
+
6. For every question you touched, move any number in the prose that the
|
|
121
178
|
evidence carries into a `{name:...}` placeholder on a `value` entry, so
|
|
122
179
|
it is read from the results file rather than retyped.
|
|
123
180
|
|
|
124
|
-
|
|
125
|
-
scope, `ok` ones included. A question the CLI passed is where an
|
|
181
|
+
7. Run the validation and verification readings above on every question
|
|
182
|
+
in scope, `ok` ones included. A question the CLI passed is where an
|
|
126
183
|
unsupported claim survives, because nothing else looks at it.
|
|
127
184
|
|
|
128
|
-
Never edit a
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
project, not a tidy-up.
|
|
185
|
+
Never edit a question or add a sign-off just to clear the check. A
|
|
186
|
+
sign-off records a person having done the reading, and one added without
|
|
187
|
+
that is a false statement about the project, not a tidy-up.
|
|
132
188
|
|
|
133
189
|
## Writing answers
|
|
134
190
|
|
|
@@ -178,7 +234,7 @@ project, not a tidy-up.
|
|
|
178
234
|
|
|
179
235
|
## Reporting
|
|
180
236
|
|
|
181
|
-
Relay a short table: question index, status,
|
|
182
|
-
propose. Quote values, not adjectives. If the pipeline is stale
|
|
237
|
+
Relay a short table: question index, status, verification and validation
|
|
238
|
+
status, what changed, what you did or propose. Quote values, not adjectives. If the pipeline is stale
|
|
183
239
|
(`calkit status`), say so first, since the evidence may be about to change
|
|
184
240
|
again.
|
|
@@ -197,23 +197,24 @@ DVC handles:
|
|
|
197
197
|
|
|
198
198
|
## Key CLI commands
|
|
199
199
|
|
|
200
|
-
| Command | What it does
|
|
201
|
-
| ------------------------------- |
|
|
202
|
-
| `calkit run` | Run the pipeline (skips unchanged stages)
|
|
203
|
-
| `calkit run --force` | Force re-run all stages
|
|
204
|
-
| `calkit status` | Show which stages are stale
|
|
205
|
-
| `calkit xr <file>` | Auto-detect stage type, env, I/O, add to pipeline
|
|
206
|
-
| `calkit xenv -n <env> -- <cmd>` | Run a command in a named environment
|
|
207
|
-
| `calkit push` | Push Git commits and DVC-tracked files to remotes
|
|
208
|
-
| `calkit pull` | Pull latest code and data
|
|
209
|
-
| `calkit save` | Auto-add, commit, and push (Git + DVC)
|
|
210
|
-
| `calkit commit -m "msg"` | Commit all tracked changes (Git + DVC)
|
|
211
|
-
| `calkit add <file>` | Add a file to version control
|
|
212
|
-
| `calkit check env --name <env>` | Verify an environment matches its spec
|
|
213
|
-
| `calkit new` | Create new project objects (notebook, dataset, etc.)
|
|
214
|
-
| `calkit new question "<text>"` | Record a research question
|
|
215
|
-
| `calkit list questions` |
|
|
216
|
-
| `calkit
|
|
200
|
+
| Command | What it does |
|
|
201
|
+
| ------------------------------- | ------------------------------------------------------ |
|
|
202
|
+
| `calkit run` | Run the pipeline (skips unchanged stages) |
|
|
203
|
+
| `calkit run --force` | Force re-run all stages |
|
|
204
|
+
| `calkit status` | Show which stages are stale |
|
|
205
|
+
| `calkit xr <file>` | Auto-detect stage type, env, I/O, add to pipeline |
|
|
206
|
+
| `calkit xenv -n <env> -- <cmd>` | Run a command in a named environment |
|
|
207
|
+
| `calkit push` | Push Git commits and DVC-tracked files to remotes |
|
|
208
|
+
| `calkit pull` | Pull latest code and data |
|
|
209
|
+
| `calkit save` | Auto-add, commit, and push (Git + DVC) |
|
|
210
|
+
| `calkit commit -m "msg"` | Commit all tracked changes (Git + DVC) |
|
|
211
|
+
| `calkit add <file>` | Add a file to version control |
|
|
212
|
+
| `calkit check env --name <env>` | Verify an environment matches its spec |
|
|
213
|
+
| `calkit new` | Create new project objects (notebook, dataset, etc.) |
|
|
214
|
+
| `calkit new question "<text>"` | Record a research question |
|
|
215
|
+
| `calkit list questions` | List questions with what each has and lacks |
|
|
216
|
+
| `calkit show question <n>` | Show a question with its answer rendered from evidence |
|
|
217
|
+
| `calkit check questions` | Check answers against current evidence |
|
|
217
218
|
|
|
218
219
|
## `calkit xr`: The fastest path to a reproducible stage
|
|
219
220
|
|
calkit/cli/list.py
CHANGED
|
@@ -63,34 +63,37 @@ def _list_artifacts(
|
|
|
63
63
|
kind: Literal["figures", "datasets", "results", "presentations"],
|
|
64
64
|
json_output: bool,
|
|
65
65
|
declared_only: bool,
|
|
66
|
+
status: bool = False,
|
|
66
67
|
):
|
|
67
68
|
"""List figures or datasets, optionally including auto-detected ones.
|
|
68
69
|
|
|
69
70
|
By default, artifacts declared in ``calkit.yaml`` are merged with any
|
|
70
71
|
auto-detected from the project's files; ``--declared-only`` returns just the
|
|
71
72
|
declared ones. Each entry carries a ``detected`` flag so callers can tell
|
|
72
|
-
declared and auto-detected artifacts apart
|
|
73
|
+
declared and auto-detected artifacts apart, and with ``status``, the
|
|
74
|
+
stage that writes it and whether that stage is up to date.
|
|
73
75
|
"""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
import calkit.pipeline
|
|
77
|
+
import calkit.results
|
|
78
|
+
|
|
79
|
+
# Which stage writes each one is read from calkit.yaml and costs
|
|
80
|
+
# nothing; whether it's current needs DVC, so it's asked for
|
|
81
|
+
try:
|
|
82
|
+
result = calkit.pipeline.list_artifacts(
|
|
83
|
+
kind,
|
|
84
|
+
declared_only=declared_only,
|
|
85
|
+
status=status,
|
|
86
|
+
pending_extensions=(
|
|
87
|
+
calkit.results.EXTENSIONS if kind == "results" else None
|
|
88
|
+
),
|
|
89
|
+
)
|
|
90
|
+
except RuntimeError as e:
|
|
91
|
+
raise_error(f"Failed to determine status: {e}")
|
|
83
92
|
if json_output:
|
|
84
|
-
result = [
|
|
85
|
-
{**o, "detected": False} for o in declared if isinstance(o, dict)
|
|
86
|
-
]
|
|
87
|
-
result += [{**o, "detected": True} for o in detected]
|
|
88
93
|
echo_json(result)
|
|
89
94
|
return
|
|
90
|
-
for obj in
|
|
95
|
+
for obj in result:
|
|
91
96
|
_echo_object(obj)
|
|
92
|
-
for obj in detected:
|
|
93
|
-
_echo_object({**obj, "detected": True})
|
|
94
97
|
|
|
95
98
|
|
|
96
99
|
@list_app.command(name="notebooks|nb")
|
|
@@ -158,9 +161,21 @@ def list_results(
|
|
|
158
161
|
),
|
|
159
162
|
),
|
|
160
163
|
] = False,
|
|
164
|
+
status: Annotated[
|
|
165
|
+
bool,
|
|
166
|
+
typer.Option(
|
|
167
|
+
"--status",
|
|
168
|
+
"-s",
|
|
169
|
+
help="Also say whether each result's stage is up to date.",
|
|
170
|
+
),
|
|
171
|
+
] = False,
|
|
161
172
|
):
|
|
162
|
-
"""List results in the project.
|
|
163
|
-
|
|
173
|
+
"""List results in the project.
|
|
174
|
+
|
|
175
|
+
Shown with the stage that writes each one, which is what
|
|
176
|
+
'calkit run' would use to bring it up to date.
|
|
177
|
+
"""
|
|
178
|
+
_list_artifacts("results", json_output, declared_only, status)
|
|
164
179
|
|
|
165
180
|
|
|
166
181
|
@list_app.command(name="presentations|pres")
|
|
@@ -183,136 +198,107 @@ def list_presentations(
|
|
|
183
198
|
_list_artifacts("presentations", json_output, declared_only)
|
|
184
199
|
|
|
185
200
|
|
|
186
|
-
|
|
187
|
-
|
|
201
|
+
@list_app.command(name="tables|tbls")
|
|
202
|
+
def list_tables(
|
|
203
|
+
columns: Annotated[
|
|
204
|
+
bool,
|
|
205
|
+
typer.Option(
|
|
206
|
+
"--columns",
|
|
207
|
+
help="Also read each table's column names from its header.",
|
|
208
|
+
),
|
|
209
|
+
] = False,
|
|
210
|
+
status: Annotated[
|
|
211
|
+
bool,
|
|
212
|
+
typer.Option(
|
|
213
|
+
"--status",
|
|
214
|
+
"-s",
|
|
215
|
+
help="Also say whether each table's stage is up to date.",
|
|
216
|
+
),
|
|
217
|
+
] = False,
|
|
218
|
+
json_output: Annotated[
|
|
219
|
+
bool, typer.Option("--json", help="Output result as JSON.")
|
|
220
|
+
] = False,
|
|
221
|
+
declared_only: Annotated[
|
|
222
|
+
bool,
|
|
223
|
+
typer.Option(
|
|
224
|
+
"--declared-only",
|
|
225
|
+
help=(
|
|
226
|
+
"Only list tables declared in calkit.yaml; "
|
|
227
|
+
"skip auto-detection."
|
|
228
|
+
),
|
|
229
|
+
),
|
|
230
|
+
] = False,
|
|
231
|
+
) -> None:
|
|
232
|
+
"""List tables in the project.
|
|
188
233
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
``calkit check questions`` names them.
|
|
234
|
+
Tabular files are detected wherever they're kept, so a table that is
|
|
235
|
+
also a dataset or a result is listed here too. Each is shown with the
|
|
236
|
+
stage that writes it, if any, which is the one 'calkit run' would use
|
|
237
|
+
to bring it up to date.
|
|
194
238
|
"""
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
239
|
+
import calkit.tables
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
tables = calkit.tables.list_tables(
|
|
243
|
+
declared_only=declared_only, status=status
|
|
244
|
+
)
|
|
245
|
+
except RuntimeError as e:
|
|
246
|
+
raise_error(f"Failed to determine table status: {e}")
|
|
247
|
+
if columns:
|
|
248
|
+
for table in tables:
|
|
249
|
+
try:
|
|
250
|
+
table["columns"] = calkit.tables.read_columns(table["path"])
|
|
251
|
+
except Exception as e:
|
|
252
|
+
# A table that can't be read is still a table; say why
|
|
253
|
+
# here rather than failing the listing
|
|
254
|
+
table["columns_error"] = str(e)
|
|
255
|
+
if json_output:
|
|
256
|
+
echo_json(tables)
|
|
198
257
|
return
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
text = question.pop("question", "")
|
|
202
|
-
typer.echo(f"{where}{n}. question: {text}")
|
|
203
|
-
# Rendering fills every field, so leave out the ones that aren't set
|
|
204
|
-
for k, v in question.items():
|
|
205
|
-
if v is None:
|
|
206
|
-
continue
|
|
207
|
-
if isinstance(v, dict):
|
|
208
|
-
typer.echo(f" {k}:")
|
|
209
|
-
for k1, v1 in v.items():
|
|
210
|
-
typer.echo(f" {k1}: {v1}")
|
|
211
|
-
elif isinstance(v, list):
|
|
212
|
-
typer.echo(f" {k}:")
|
|
213
|
-
for item in v:
|
|
214
|
-
if isinstance(item, dict):
|
|
215
|
-
item = {
|
|
216
|
-
k1: v1 for k1, v1 in item.items() if v1 is not None
|
|
217
|
-
}
|
|
218
|
-
for n1, (k1, v1) in enumerate(item.items()):
|
|
219
|
-
prefix = " - " if n1 == 0 else " "
|
|
220
|
-
if isinstance(v1, dict):
|
|
221
|
-
typer.echo(f"{prefix}{k1}:")
|
|
222
|
-
for k2, v2 in v1.items():
|
|
223
|
-
typer.echo(f" {k2}: {v2}")
|
|
224
|
-
else:
|
|
225
|
-
typer.echo(f"{prefix}{k1}: {v1}")
|
|
226
|
-
else:
|
|
227
|
-
typer.echo(f" - {item}")
|
|
228
|
-
else:
|
|
229
|
-
typer.echo(f" {k}: {v}")
|
|
258
|
+
for table in tables:
|
|
259
|
+
_echo_object(table)
|
|
230
260
|
|
|
231
261
|
|
|
232
|
-
@list_app.command(name="questions")
|
|
262
|
+
@list_app.command(name="questions|q")
|
|
233
263
|
def list_questions(
|
|
234
264
|
json_output: Annotated[
|
|
235
265
|
bool, typer.Option("--json", help="Output result as JSON.")
|
|
236
266
|
] = False,
|
|
237
|
-
|
|
238
|
-
bool,
|
|
239
|
-
typer.Option(
|
|
240
|
-
"--raw",
|
|
241
|
-
help="Show the text as written, with its {name} placeholders, "
|
|
242
|
-
"instead of rendered from the evidence.",
|
|
243
|
-
),
|
|
244
|
-
] = False,
|
|
245
|
-
):
|
|
267
|
+
) -> None:
|
|
246
268
|
"""List the project's questions (1-indexed).
|
|
247
269
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
270
|
+
Each has a row of letters: H if it has a hypothesis, A if it's answered,
|
|
271
|
+
then V if it's verified and V if it's validated, colored as in the hub,
|
|
272
|
+
with a dash for each it doesn't have yet. Evidence isn't checked against
|
|
273
|
+
the pipeline here; 'calkit check questions' does that. Subprojects'
|
|
274
|
+
questions are listed too, numbered within each. Use 'calkit show
|
|
275
|
+
question' to see one in full.
|
|
253
276
|
"""
|
|
254
|
-
from
|
|
255
|
-
|
|
256
|
-
from calkit.questions import (
|
|
257
|
-
TEMPLATED_FIELDS,
|
|
258
|
-
placeholders,
|
|
259
|
-
render_question,
|
|
260
|
-
)
|
|
277
|
+
from calkit.questions import check_questions, format_letters
|
|
261
278
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
ev.get("explanation") or ""
|
|
266
|
-
for ev in evidence
|
|
267
|
-
if isinstance(ev, dict)
|
|
268
|
-
]
|
|
269
|
-
|
|
270
|
-
# Each with its 1-based index in its own project and that project's
|
|
271
|
-
# path, empty for this one
|
|
272
|
-
listed: list[tuple[int, str | dict, str]] = []
|
|
273
|
-
for wdir, info in calkit.iter_projects():
|
|
274
|
-
project = "" if wdir == "." else Path(wdir).as_posix()
|
|
275
|
-
for n, q in enumerate(info.get("questions", []) or [], start=1):
|
|
276
|
-
if not raw:
|
|
277
|
-
q = render_question(q, info, wdir=wdir)
|
|
278
|
-
listed.append((n, q, project))
|
|
279
|
-
questions = [q for _, q, _ in listed]
|
|
280
|
-
if not raw:
|
|
281
|
-
# A placeholder left as written looks exactly like text somebody
|
|
282
|
-
# meant literally, so say when one could not be filled rather than
|
|
283
|
-
# let a fresh clone read as a project that types its braces. Any
|
|
284
|
-
# placeholder left standing counts, whether it names evidence that
|
|
285
|
-
# could not be read or names nothing at all: a brace meant to stay
|
|
286
|
-
# in the text is written '{{' and never reaches here. A conditional
|
|
287
|
-
# answer still in clauses is one whose conditions could not be read.
|
|
288
|
-
unfilled = any(
|
|
289
|
-
isinstance(text, dict) or placeholders(text)
|
|
290
|
-
for q in questions
|
|
291
|
-
if isinstance(q, dict)
|
|
292
|
-
for text in _texts(q)
|
|
293
|
-
)
|
|
294
|
-
if unfilled:
|
|
295
|
-
warn(
|
|
296
|
-
"Some placeholders or conditions could not be filled from "
|
|
297
|
-
"the evidence. "
|
|
298
|
-
"Run 'calkit check questions' to see why; 'calkit pull' if "
|
|
299
|
-
"the results files are not here yet.",
|
|
300
|
-
err=json_output,
|
|
301
|
-
)
|
|
279
|
+
# Sign-offs are checked for their letters, but evidence isn't compared
|
|
280
|
+
# with its history, which nothing here shows
|
|
281
|
+
status = check_questions(check_pipeline=False, check_changes=False)
|
|
302
282
|
if json_output:
|
|
303
283
|
echo_json(
|
|
304
284
|
[
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
285
|
+
{
|
|
286
|
+
"project": q.project,
|
|
287
|
+
"index": q.index,
|
|
288
|
+
"question": q.question,
|
|
289
|
+
"hypothesis": q.hypothesis,
|
|
290
|
+
"answered": q.answered,
|
|
291
|
+
"status": q.status,
|
|
292
|
+
"verification_status": q.verification_status,
|
|
293
|
+
"validation_status": q.validation_status,
|
|
294
|
+
}
|
|
295
|
+
for q in status.questions
|
|
311
296
|
]
|
|
312
297
|
)
|
|
313
298
|
return
|
|
314
|
-
for
|
|
315
|
-
|
|
299
|
+
for q in status.questions:
|
|
300
|
+
where = f"{q.project}: " if q.project else ""
|
|
301
|
+
typer.echo(f"{where}{q.index}. {format_letters(q)} {q.question}")
|
|
316
302
|
|
|
317
303
|
|
|
318
304
|
@list_app.command(name="comments")
|