Resolve explicit run/study/workspace selectors into artifact descriptions.
122 completed: bool = False) -> list:
123 """!
124 @brief Resolve explicit run/study/workspace selectors into artifact descriptions.
125 @param[in] run_dir Value supplied through the `run_dir` argument.
126 @param[in] study_dir Value supplied through the `study_dir` argument.
127 @param[in] case_ids Value supplied through the `case_ids` argument.
128 @param[in] workspace Workspace root to protect as its own artifact.
129 @param[in] include_inputs Whether workspace protection covers user-supplied inputs.
130 @param[in] completed Select every finished study member instead of naming them.
131 @return Result produced by this operation.
132 """
133 selectors = [bool(run_dir), bool(study_dir), bool(workspace)]
134 if sum(selectors) != 1:
135 raise StorageError("Select exactly one of --run-dir, --study-dir, or --workspace.")
136 if include_inputs and not workspace:
137 raise StorageError("--include-inputs is valid only with --workspace.")
138 if workspace:
139 if case_ids:
140 raise StorageError("--case-id is valid only with --study-dir.")
141 root = os.path.abspath(workspace)
142 if not os.path.isfile(os.path.join(root, WORKSPACE_CONFIG_FILENAME)):
143 raise StorageError(
144 f"Directory is not an initialized PICurv workspace (missing "
145 f"{WORKSPACE_CONFIG_FILENAME}): {root}"
146 )
147 identity = {}
148 try:
149 with open(os.path.join(root, WORKSPACE_CONFIG_FILENAME), "r", encoding="utf-8") as stream:
150 identity = (yaml.safe_load(stream) or {}).get("workspace") or {}
151 except (OSError, ValueError):
152 identity = {}
153 return [{
154 "artifact_type": "workspace",
155 "root_path": root,
156 "original_path": root,
157 "run_id": None,
158 "study_id": None,
159 "case_id": None,
160 "workspace_id": identity.get("id") or os.path.basename(root),
161 "include_inputs": bool(include_inputs),
162 }]
163 if run_dir:
164 if case_ids:
165 raise StorageError("--case-id is valid only with --study-dir.")
166 root = os.path.abspath(run_dir)
167 if not os.path.isdir(root):
168 raise StorageError(f"Run directory not found: {root}")
169
170
171
172
173 identity = read_artifact_identity(root)
174 if identity["identity_source"] != "manifest" and not os.path.isdir(
175 os.path.join(root, "config")):
176 raise StorageError(
177 f"Directory does not look like a PICurv run (no {RUN_MANIFEST_FILENAME} "
178 f"and no config/): {root}"
179 )
180 if identity["artifact_type"] == "study":
181 raise StorageError(f"That is a study, not a run; use --study-dir: {root}")
182 return [{
183 "artifact_type": "run",
184 "root_path": root,
185 "original_path": root,
186 "run_id": identity["run_id"],
187 "study_id": identity["study_id"] if identity["case_id"] else None,
188 "case_id": identity["case_id"],
189 "identity_source": identity["identity_source"],
190 }]
191
192 study_root = os.path.abspath(study_dir)
193 if not os.path.isdir(study_root):
194 raise StorageError(f"Study directory not found: {study_root}")
195 study_identity = read_artifact_identity(study_root)
196 if study_identity["identity_source"] != "manifest" and not os.path.isdir(
197 os.path.join(study_root, "cases")):
198 raise StorageError(
199 f"Directory does not look like a PICurv study (no {STUDY_MANIFEST_FILENAME} "
200 f"and no cases/): {study_root}"
201 )
202 if not os.path.isdir(os.path.join(study_root, "cases")):
203 raise StorageError(f"Study has no cases/ directory: {study_root}")
204 if completed:
205 if case_ids:
206 raise StorageError("--completed selects members itself; do not also pass --case-id.")
207 case_ids = _select_completed_case_ids(study_root)
208 if case_ids:
209 targets = []
210 for raw_case_id in case_ids:
211 case_id = _validate_case_id(raw_case_id)
212 case_root = os.path.join(study_root, "cases", case_id)
213 if not os.path.isdir(case_root):
214 raise StorageError(f"Study member not found: {case_root}")
215 member_identity = read_artifact_identity(case_root)
216 targets.append({
217 "artifact_type": "study-case",
218 "root_path": case_root,
219 "original_path": case_root,
220 "run_id": member_identity["run_id"] or case_id,
221 "study_id": study_identity["study_id"],
222 "case_id": member_identity["case_id"] or case_id,
223 "study_path": study_root,
224 "identity_source": member_identity["identity_source"],
225 })
226 return targets
227 return [{
228 "artifact_type": "study",
229 "root_path": study_root,
230 "original_path": study_root,
231 "run_id": None,
232 "study_id": study_identity["study_id"],
233 "case_id": None,
234 "identity_source": study_identity["identity_source"],
235 }]
236
237