nmdc-runtime 2.8.0__py3-none-any.whl → 2.10.0__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.
Potentially problematic release.
This version of nmdc-runtime might be problematic. Click here for more details.
- nmdc_runtime/api/__init__.py +0 -0
- nmdc_runtime/api/analytics.py +70 -0
- nmdc_runtime/api/boot/__init__.py +0 -0
- nmdc_runtime/api/boot/capabilities.py +9 -0
- nmdc_runtime/api/boot/object_types.py +126 -0
- nmdc_runtime/api/boot/triggers.py +84 -0
- nmdc_runtime/api/boot/workflows.py +116 -0
- nmdc_runtime/api/core/__init__.py +0 -0
- nmdc_runtime/api/core/auth.py +208 -0
- nmdc_runtime/api/core/idgen.py +170 -0
- nmdc_runtime/api/core/metadata.py +788 -0
- nmdc_runtime/api/core/util.py +109 -0
- nmdc_runtime/api/db/__init__.py +0 -0
- nmdc_runtime/api/db/mongo.py +447 -0
- nmdc_runtime/api/db/s3.py +37 -0
- nmdc_runtime/api/endpoints/__init__.py +0 -0
- nmdc_runtime/api/endpoints/capabilities.py +25 -0
- nmdc_runtime/api/endpoints/find.py +794 -0
- nmdc_runtime/api/endpoints/ids.py +192 -0
- nmdc_runtime/api/endpoints/jobs.py +143 -0
- nmdc_runtime/api/endpoints/lib/__init__.py +0 -0
- nmdc_runtime/api/endpoints/lib/helpers.py +274 -0
- nmdc_runtime/api/endpoints/lib/path_segments.py +165 -0
- nmdc_runtime/api/endpoints/metadata.py +260 -0
- nmdc_runtime/api/endpoints/nmdcschema.py +581 -0
- nmdc_runtime/api/endpoints/object_types.py +38 -0
- nmdc_runtime/api/endpoints/objects.py +277 -0
- nmdc_runtime/api/endpoints/operations.py +105 -0
- nmdc_runtime/api/endpoints/queries.py +679 -0
- nmdc_runtime/api/endpoints/runs.py +98 -0
- nmdc_runtime/api/endpoints/search.py +38 -0
- nmdc_runtime/api/endpoints/sites.py +229 -0
- nmdc_runtime/api/endpoints/triggers.py +25 -0
- nmdc_runtime/api/endpoints/users.py +214 -0
- nmdc_runtime/api/endpoints/util.py +774 -0
- nmdc_runtime/api/endpoints/workflows.py +353 -0
- nmdc_runtime/api/main.py +401 -0
- nmdc_runtime/api/middleware.py +43 -0
- nmdc_runtime/api/models/__init__.py +0 -0
- nmdc_runtime/api/models/capability.py +14 -0
- nmdc_runtime/api/models/id.py +92 -0
- nmdc_runtime/api/models/job.py +37 -0
- nmdc_runtime/api/models/lib/__init__.py +0 -0
- nmdc_runtime/api/models/lib/helpers.py +78 -0
- nmdc_runtime/api/models/metadata.py +11 -0
- nmdc_runtime/api/models/minter.py +0 -0
- nmdc_runtime/api/models/nmdc_schema.py +146 -0
- nmdc_runtime/api/models/object.py +180 -0
- nmdc_runtime/api/models/object_type.py +20 -0
- nmdc_runtime/api/models/operation.py +66 -0
- nmdc_runtime/api/models/query.py +246 -0
- nmdc_runtime/api/models/query_continuation.py +111 -0
- nmdc_runtime/api/models/run.py +161 -0
- nmdc_runtime/api/models/site.py +87 -0
- nmdc_runtime/api/models/trigger.py +13 -0
- nmdc_runtime/api/models/user.py +140 -0
- nmdc_runtime/api/models/util.py +253 -0
- nmdc_runtime/api/models/workflow.py +15 -0
- nmdc_runtime/api/openapi.py +242 -0
- nmdc_runtime/config.py +55 -4
- nmdc_runtime/core/db/Database.py +1 -3
- nmdc_runtime/infrastructure/database/models/user.py +0 -9
- nmdc_runtime/lib/extract_nmdc_data.py +0 -8
- nmdc_runtime/lib/nmdc_dataframes.py +3 -7
- nmdc_runtime/lib/nmdc_etl_class.py +1 -7
- nmdc_runtime/minter/adapters/repository.py +1 -2
- nmdc_runtime/minter/config.py +2 -0
- nmdc_runtime/minter/domain/model.py +35 -1
- nmdc_runtime/minter/entrypoints/fastapi_app.py +1 -1
- nmdc_runtime/mongo_util.py +1 -2
- nmdc_runtime/site/backup/nmdcdb_mongodump.py +1 -1
- nmdc_runtime/site/backup/nmdcdb_mongoexport.py +1 -3
- nmdc_runtime/site/export/ncbi_xml.py +1 -2
- nmdc_runtime/site/export/ncbi_xml_utils.py +1 -1
- nmdc_runtime/site/graphs.py +33 -28
- nmdc_runtime/site/ops.py +97 -237
- nmdc_runtime/site/repair/database_updater.py +8 -0
- nmdc_runtime/site/repository.py +7 -117
- nmdc_runtime/site/resources.py +4 -4
- nmdc_runtime/site/translation/gold_translator.py +22 -21
- nmdc_runtime/site/translation/neon_benthic_translator.py +0 -1
- nmdc_runtime/site/translation/neon_soil_translator.py +4 -5
- nmdc_runtime/site/translation/neon_surface_water_translator.py +0 -2
- nmdc_runtime/site/translation/submission_portal_translator.py +64 -54
- nmdc_runtime/site/translation/translator.py +63 -1
- nmdc_runtime/site/util.py +8 -3
- nmdc_runtime/site/validation/util.py +10 -5
- nmdc_runtime/util.py +9 -321
- {nmdc_runtime-2.8.0.dist-info → nmdc_runtime-2.10.0.dist-info}/METADATA +57 -6
- nmdc_runtime-2.10.0.dist-info/RECORD +138 -0
- nmdc_runtime/site/translation/emsl.py +0 -43
- nmdc_runtime/site/translation/gold.py +0 -53
- nmdc_runtime/site/translation/jgi.py +0 -32
- nmdc_runtime/site/translation/util.py +0 -132
- nmdc_runtime/site/validation/jgi.py +0 -43
- nmdc_runtime-2.8.0.dist-info/RECORD +0 -84
- {nmdc_runtime-2.8.0.dist-info → nmdc_runtime-2.10.0.dist-info}/WHEEL +0 -0
- {nmdc_runtime-2.8.0.dist-info → nmdc_runtime-2.10.0.dist-info}/entry_points.txt +0 -0
- {nmdc_runtime-2.8.0.dist-info → nmdc_runtime-2.10.0.dist-info}/licenses/LICENSE +0 -0
- {nmdc_runtime-2.8.0.dist-info → nmdc_runtime-2.10.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
from typing import Any, List, Set, Annotated
|
|
4
|
+
|
|
5
|
+
import pymongo
|
|
6
|
+
from bson import ObjectId
|
|
7
|
+
from fastapi import APIRouter, Depends, HTTPException, Path
|
|
8
|
+
from pymongo.database import Database as MongoDatabase
|
|
9
|
+
from pymongo.errors import BulkWriteError
|
|
10
|
+
from starlette import status
|
|
11
|
+
|
|
12
|
+
from nmdc_runtime.api.core.util import raise404_if_none
|
|
13
|
+
from nmdc_runtime.api.endpoints.queries import (
|
|
14
|
+
_run_mdb_cmd,
|
|
15
|
+
check_can_update_and_delete,
|
|
16
|
+
_run_delete_nonschema,
|
|
17
|
+
)
|
|
18
|
+
from nmdc_runtime.api.db.mongo import get_mongo_db, validate_json
|
|
19
|
+
from nmdc_runtime.api.models.capability import Capability
|
|
20
|
+
from nmdc_runtime.api.models.object_type import ObjectType
|
|
21
|
+
from nmdc_runtime.api.models.query import DeleteCommand, DeleteStatement
|
|
22
|
+
from nmdc_runtime.api.models.site import Site, get_current_client_site
|
|
23
|
+
from nmdc_runtime.api.models.user import User, get_current_active_user
|
|
24
|
+
from nmdc_runtime.api.models.util import DeleteResponse
|
|
25
|
+
from nmdc_runtime.api.models.workflow import Workflow
|
|
26
|
+
from nmdc_runtime.site.resources import MongoDB
|
|
27
|
+
from nmdc_schema.nmdc import (
|
|
28
|
+
MetagenomeAnnotation,
|
|
29
|
+
MetaproteomicsAnalysis,
|
|
30
|
+
MetatranscriptomeAnnotation,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
router = APIRouter()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@router.get("/workflows", response_model=List[Workflow])
|
|
38
|
+
def list_workflows(
|
|
39
|
+
mdb: pymongo.database.Database = Depends(get_mongo_db),
|
|
40
|
+
):
|
|
41
|
+
return list(mdb.workflows.find())
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@router.get("/workflows/{workflow_id}", response_model=Workflow)
|
|
45
|
+
def get_workflow(
|
|
46
|
+
workflow_id: str,
|
|
47
|
+
mdb: pymongo.database.Database = Depends(get_mongo_db),
|
|
48
|
+
):
|
|
49
|
+
return raise404_if_none(mdb.workflows.find_one({"id": workflow_id}))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@router.get("/workflows/{workflow_id}/object_types", response_model=List[ObjectType])
|
|
53
|
+
def list_workflow_object_types(
|
|
54
|
+
workflow_id: str, mdb: pymongo.database.Database = Depends(get_mongo_db)
|
|
55
|
+
):
|
|
56
|
+
object_type_ids = [
|
|
57
|
+
doc["object_type_id"] for doc in mdb.triggers.find({"workflow_id": workflow_id})
|
|
58
|
+
]
|
|
59
|
+
return list(mdb.object_types.find({"id": {"$in": object_type_ids}}))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@router.get("/workflows/{workflow_id}/capabilities", response_model=List[Capability])
|
|
63
|
+
def list_workflow_capabilities(
|
|
64
|
+
workflow_id: str, mdb: pymongo.database.Database = Depends(get_mongo_db)
|
|
65
|
+
):
|
|
66
|
+
doc = raise404_if_none(mdb.workflows.find_one({"id": workflow_id}))
|
|
67
|
+
return list(mdb.capabilities.find({"id": {"$in": doc.get("capability_ids", [])}}))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@router.post("/workflows/activities", status_code=status.HTTP_410_GONE, deprecated=True)
|
|
71
|
+
async def post_activity(
|
|
72
|
+
activity_set: dict[str, Any],
|
|
73
|
+
site: Site = Depends(get_current_client_site),
|
|
74
|
+
mdb: MongoDatabase = Depends(get_mongo_db),
|
|
75
|
+
):
|
|
76
|
+
"""
|
|
77
|
+
DEPRECATED: migrate all workflows from this endpoint to `/workflows/workflow_executions`.
|
|
78
|
+
"""
|
|
79
|
+
return f"DEPRECATED: POST your request to `/workflows/workflow_executions` instead."
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@router.post("/workflows/workflow_executions")
|
|
83
|
+
async def post_workflow_execution(
|
|
84
|
+
workflow_execution_set: dict[str, Any],
|
|
85
|
+
site: Site = Depends(get_current_client_site),
|
|
86
|
+
mdb: MongoDatabase = Depends(get_mongo_db),
|
|
87
|
+
):
|
|
88
|
+
"""
|
|
89
|
+
Post workflow execution set to database and claim job.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
-------
|
|
93
|
+
workflow_execution_set: dict[str,Any]
|
|
94
|
+
Set of workflow executions for specific workflows, in the form of a nmdc:Database.
|
|
95
|
+
Other collections (such as data_object_set) are allowed, as they may be associated
|
|
96
|
+
with the workflow executions submitted.
|
|
97
|
+
|
|
98
|
+
site: Site
|
|
99
|
+
mdb: MongoDatabase
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
dict[str,str]
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
_ = site # must be authenticated
|
|
107
|
+
try:
|
|
108
|
+
# validate request JSON
|
|
109
|
+
rv = validate_json(
|
|
110
|
+
workflow_execution_set, mdb, check_inter_document_references=True
|
|
111
|
+
)
|
|
112
|
+
if rv["result"] == "errors":
|
|
113
|
+
raise HTTPException(
|
|
114
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
115
|
+
detail=str(rv),
|
|
116
|
+
)
|
|
117
|
+
# create mongodb instance for dagster
|
|
118
|
+
mongo_resource = MongoDB(
|
|
119
|
+
host=os.getenv("MONGO_HOST"),
|
|
120
|
+
dbname=os.getenv("MONGO_DBNAME"),
|
|
121
|
+
username=os.getenv("MONGO_USERNAME"),
|
|
122
|
+
password=os.getenv("MONGO_PASSWORD"),
|
|
123
|
+
)
|
|
124
|
+
mongo_resource.add_docs(workflow_execution_set, validate=False, replace=True)
|
|
125
|
+
return {"message": "jobs accepted"}
|
|
126
|
+
except BulkWriteError as e:
|
|
127
|
+
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))
|
|
128
|
+
except ValueError as e:
|
|
129
|
+
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@router.delete(
|
|
133
|
+
"/workflows/workflow_executions/{workflow_execution_id}",
|
|
134
|
+
response_model=DeleteResponse,
|
|
135
|
+
description="Delete a workflow execution and its downstream workflow executions, data objects, "
|
|
136
|
+
"functional annotation aggregation members, and related job records.\n\n"
|
|
137
|
+
"This endpoint performs recursive deletion of the specified workflow execution, "
|
|
138
|
+
"all downstream workflow executions that depend on this workflow execution's outputs, "
|
|
139
|
+
"all functional annotation aggregation members generated by deleted workflow executions, "
|
|
140
|
+
"all data objects that are outputs of deleted workflow executions, "
|
|
141
|
+
"and all job records that have the workflow execution ID as their config.activity_id.",
|
|
142
|
+
)
|
|
143
|
+
async def delete_workflow_execution(
|
|
144
|
+
workflow_execution_id: Annotated[
|
|
145
|
+
str,
|
|
146
|
+
Path(
|
|
147
|
+
title="Workflow Execution ID",
|
|
148
|
+
description="The `id` of the `WorkflowExecution` you want to delete.\n\n_Example_: `nmdc:wfmgan-11-abc123.1`",
|
|
149
|
+
examples=["nmdc:wfmgan-11-abc123.1"],
|
|
150
|
+
),
|
|
151
|
+
],
|
|
152
|
+
user: User = Depends(get_current_active_user),
|
|
153
|
+
mdb: MongoDatabase = Depends(get_mongo_db),
|
|
154
|
+
):
|
|
155
|
+
"""
|
|
156
|
+
Delete a given workflow execution and its downstream workflow executions, data objects,
|
|
157
|
+
functional annotation aggregation members, and related job records.
|
|
158
|
+
|
|
159
|
+
This endpoint performs recursive deletion of:
|
|
160
|
+
1. The specified workflow execution
|
|
161
|
+
2. All downstream workflow executions that depend on this execution's outputs
|
|
162
|
+
3. All functional annotation aggregation members generated by deleted workflow executions
|
|
163
|
+
4. All data objects that are outputs of deleted workflow executions
|
|
164
|
+
5. All job records that have the workflow execution ID as their config.activity_id
|
|
165
|
+
|
|
166
|
+
Input data objects (has_input) are preserved as they may be used by other workflow executions.
|
|
167
|
+
TODO: Consider deleting input data objects that are _not_ used by other workflow executions
|
|
168
|
+
(otherwise, they may accumulate in the database as so-called "orphaned documents").
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
workflow_execution_id : str
|
|
173
|
+
ID of the workflow execution to delete
|
|
174
|
+
user : User
|
|
175
|
+
Authenticated user (required)
|
|
176
|
+
mdb : MongoDatabase
|
|
177
|
+
MongoDB database connection
|
|
178
|
+
|
|
179
|
+
Returns
|
|
180
|
+
-------
|
|
181
|
+
dict
|
|
182
|
+
Catalog of deleted workflow executions, data objects, functional annotation aggregation members, and job records
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
# Check user permissions for delete operations
|
|
186
|
+
# TODO: Decouple this endpoint's authorization criteria from that of the `/queries:run` endpoint.
|
|
187
|
+
# Currently, both endpoints rely on the "/queries:run(query_cmd:DeleteCommand)" allowance.
|
|
188
|
+
check_can_update_and_delete(user)
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
# Check if workflow execution exists
|
|
192
|
+
workflow_execution = mdb.workflow_execution_set.find_one(
|
|
193
|
+
{"id": workflow_execution_id}
|
|
194
|
+
)
|
|
195
|
+
if not workflow_execution:
|
|
196
|
+
raise HTTPException(
|
|
197
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
198
|
+
detail=f"Workflow execution {workflow_execution_id} not found",
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Track what we've deleted to avoid cycles and provide summary
|
|
202
|
+
deleted_workflow_execution_ids: Set[str] = set()
|
|
203
|
+
deleted_data_object_ids: Set[str] = set()
|
|
204
|
+
deleted_functional_annotation_agg_oids: Set[str] = set()
|
|
205
|
+
deleted_job_ids: Set[str] = set()
|
|
206
|
+
|
|
207
|
+
def find_linked_workflow_executions(
|
|
208
|
+
data_object_ids: List[str],
|
|
209
|
+
) -> List[str]:
|
|
210
|
+
"""Find workflow executions that use any of the given data objects as inputs."""
|
|
211
|
+
if not data_object_ids:
|
|
212
|
+
return []
|
|
213
|
+
|
|
214
|
+
linked_wfes = list(
|
|
215
|
+
mdb.workflow_execution_set.find(
|
|
216
|
+
{"has_input": {"$in": data_object_ids}}, {"id": 1}
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
return [wfe["id"] for wfe in linked_wfes]
|
|
220
|
+
|
|
221
|
+
def recursive_delete_workflow_execution(wfe_id: str) -> None:
|
|
222
|
+
"""Recursively delete a workflow execution and all its downstream dependencies."""
|
|
223
|
+
if wfe_id in deleted_workflow_execution_ids:
|
|
224
|
+
return # Already deleted or in progress
|
|
225
|
+
|
|
226
|
+
# Get the workflow execution
|
|
227
|
+
wfe = mdb.workflow_execution_set.find_one({"id": wfe_id})
|
|
228
|
+
if not wfe:
|
|
229
|
+
return # Already deleted or doesn't exist
|
|
230
|
+
|
|
231
|
+
# Mark as being processed to prevent cycles
|
|
232
|
+
deleted_workflow_execution_ids.add(wfe_id)
|
|
233
|
+
|
|
234
|
+
# Get output data objects from this workflow execution
|
|
235
|
+
output_data_object_ids = wfe.get("has_output", [])
|
|
236
|
+
|
|
237
|
+
# Check if this is an AnnotatingWorkflow (e.g., metagenome annotation)
|
|
238
|
+
# If so, we need to also delete functional_annotation_agg records
|
|
239
|
+
wfe_type = wfe.get("type", "")
|
|
240
|
+
is_annotating_workflow = wfe_type in [
|
|
241
|
+
MetagenomeAnnotation.class_class_curie,
|
|
242
|
+
MetatranscriptomeAnnotation.class_class_curie,
|
|
243
|
+
MetaproteomicsAnalysis.class_class_curie,
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
# Find linked workflow executions that use these data objects as inputs
|
|
247
|
+
linked_wfe_ids = find_linked_workflow_executions(output_data_object_ids)
|
|
248
|
+
|
|
249
|
+
# Recursively delete linked workflow executions first
|
|
250
|
+
for linked_wfe_id in linked_wfe_ids:
|
|
251
|
+
if linked_wfe_id not in deleted_workflow_execution_ids:
|
|
252
|
+
recursive_delete_workflow_execution(linked_wfe_id)
|
|
253
|
+
|
|
254
|
+
# Add data objects to deletion set
|
|
255
|
+
deleted_data_object_ids.update(output_data_object_ids)
|
|
256
|
+
|
|
257
|
+
# If this is an AnnotatingWorkflow, mark functional annotation records for deletion
|
|
258
|
+
if is_annotating_workflow:
|
|
259
|
+
func_annotation_records = list(
|
|
260
|
+
mdb.functional_annotation_agg.find(
|
|
261
|
+
{"was_generated_by": wfe_id}, {"_id": 1}
|
|
262
|
+
)
|
|
263
|
+
)
|
|
264
|
+
if func_annotation_records:
|
|
265
|
+
# Store the ObjectIds for deletion from functional_annotation_agg
|
|
266
|
+
deleted_functional_annotation_agg_oids.update(
|
|
267
|
+
[str(record["_id"]) for record in func_annotation_records]
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Find and mark job records for deletion that have this workflow execution as activity_id
|
|
271
|
+
job_records = list(mdb.jobs.find({"config.activity_id": wfe_id}, {"id": 1}))
|
|
272
|
+
if job_records:
|
|
273
|
+
deleted_job_ids.update([job["id"] for job in job_records])
|
|
274
|
+
|
|
275
|
+
# Start recursive deletion from the target workflow execution
|
|
276
|
+
recursive_delete_workflow_execution(workflow_execution_id)
|
|
277
|
+
|
|
278
|
+
# Prepare deletion payload
|
|
279
|
+
docs_to_delete = {}
|
|
280
|
+
if deleted_workflow_execution_ids:
|
|
281
|
+
docs_to_delete["workflow_execution_set"] = list(
|
|
282
|
+
deleted_workflow_execution_ids
|
|
283
|
+
)
|
|
284
|
+
if deleted_data_object_ids:
|
|
285
|
+
docs_to_delete["data_object_set"] = list(deleted_data_object_ids)
|
|
286
|
+
if deleted_functional_annotation_agg_oids:
|
|
287
|
+
docs_to_delete["functional_annotation_agg"] = list(
|
|
288
|
+
deleted_functional_annotation_agg_oids
|
|
289
|
+
)
|
|
290
|
+
if deleted_job_ids:
|
|
291
|
+
docs_to_delete["jobs"] = list(deleted_job_ids)
|
|
292
|
+
|
|
293
|
+
# Perform the actual deletion using `_run_mdb_cmd`, so the operations
|
|
294
|
+
# undergo schema, validation and referential integrity checking, and
|
|
295
|
+
# deleted documents are backed up to the `nmdc_deleted` database.
|
|
296
|
+
deletion_results = {}
|
|
297
|
+
|
|
298
|
+
for collection_name, doc_ids in docs_to_delete.items():
|
|
299
|
+
if not doc_ids:
|
|
300
|
+
continue
|
|
301
|
+
|
|
302
|
+
# Handle special case for functional_annotation_agg which uses _id instead of id
|
|
303
|
+
if collection_name == "functional_annotation_agg":
|
|
304
|
+
# Convert string ObjectIds back to ObjectId instances for the filter
|
|
305
|
+
object_ids = [ObjectId(doc_id) for doc_id in doc_ids]
|
|
306
|
+
filter_dict = {"_id": {"$in": object_ids}}
|
|
307
|
+
else:
|
|
308
|
+
# Standard case - use id field
|
|
309
|
+
filter_dict = {"id": {"$in": doc_ids}}
|
|
310
|
+
|
|
311
|
+
# Create delete command
|
|
312
|
+
delete_cmd = DeleteCommand(
|
|
313
|
+
delete=collection_name,
|
|
314
|
+
deletes=[
|
|
315
|
+
DeleteStatement(q=filter_dict, limit=0)
|
|
316
|
+
], # limit=0 means delete all matching
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
logging.warning(
|
|
320
|
+
f"Executing cascading delete command for {collection_name} - you may temporarily encounter broken references."
|
|
321
|
+
)
|
|
322
|
+
# Execute the delete command
|
|
323
|
+
if collection_name == "jobs":
|
|
324
|
+
response = _run_delete_nonschema(delete_cmd, mdb)
|
|
325
|
+
else:
|
|
326
|
+
response = _run_mdb_cmd(delete_cmd, mdb, allow_broken_refs=True)
|
|
327
|
+
|
|
328
|
+
# Store the result
|
|
329
|
+
deletion_results[collection_name] = {
|
|
330
|
+
"deleted_count": response.n,
|
|
331
|
+
"doc_ids": doc_ids,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
"message": "Workflow execution and dependencies deleted successfully",
|
|
336
|
+
"deleted_workflow_execution_ids": list(deleted_workflow_execution_ids),
|
|
337
|
+
"deleted_data_object_ids": list(deleted_data_object_ids),
|
|
338
|
+
"deleted_functional_annotation_agg_oids": [
|
|
339
|
+
str(oid) for oid in deleted_functional_annotation_agg_oids
|
|
340
|
+
],
|
|
341
|
+
"deleted_job_ids": list(deleted_job_ids),
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
except HTTPException:
|
|
345
|
+
raise
|
|
346
|
+
except Exception as e:
|
|
347
|
+
logging.error(
|
|
348
|
+
f"Error during workflow execution deletion: {str(e)}", exc_info=True
|
|
349
|
+
)
|
|
350
|
+
raise HTTPException(
|
|
351
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
352
|
+
detail=f"Error during deletion: {str(e)}",
|
|
353
|
+
)
|