Skip to content

Commit 9b9ba68

Browse files
Merge branch 'main' into Derek-Furst/retracted-status
2 parents eba338e + c7f8ace commit 9b9ba68

4 files changed

Lines changed: 503 additions & 1 deletion

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.6.16
1+
2.6.17

src/app.py

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,286 @@ def get_entities_by_type(entity_type):
11141114
# Response with the final result
11151115
return jsonify(final_result)
11161116

1117+
1118+
"""
1119+
Retrieve the document info needed for a given entity's ancestors. Result filtering for this
1120+
endpoint is required and is given by the required parameter 'include'.
1121+
For example /ancestors-info?include=uuid,status,entity_type
1122+
1123+
1124+
Parameters
1125+
----------
1126+
include : str
1127+
A comma delimited string of all the properties to be retrieved by the endpoint
1128+
1129+
Returns
1130+
-------
1131+
json
1132+
A list of dicts where each dict contains the requested fields for the given ancestor.
1133+
"""
1134+
1135+
@app.route('/ancestors-info/<uuid>', methods=['GET'])
1136+
def get_ancestors_info(uuid):
1137+
validate_token_if_auth_header_exists(request)
1138+
include_fields = None
1139+
if bool(request.args):
1140+
included = request.args.get('include')
1141+
if included:
1142+
include_fields = [
1143+
f.strip().strip("'").strip('"')
1144+
for f in included.split(',')
1145+
if f.strip()
1146+
]
1147+
valid_fields = set(schema_manager.get_persistent_fields())
1148+
invalid = [f for f in include_fields if f not in valid_fields]
1149+
if invalid:
1150+
return bad_request_error(f"Invalid include fields: {invalid}")
1151+
else:
1152+
return bad_request_error(f"Missing required parameter: 'include'. Must include a list of properties to be returned.")
1153+
else:
1154+
return bad_request_error(f"Missing required parameter: 'include'. Must include a list of properties to be returned.")
1155+
result = app_neo4j_queries.get_ancestors_trimmed(neo4j_driver_instance, uuid, included_fields=include_fields)
1156+
if result is None:
1157+
return not_found_error(f"Entity {uuid} not found")
1158+
cleaned_result = [schema_manager.remove_none_values(entity) for entity in result]
1159+
complete = [schema_manager.normalize_document_result_for_response(entity) for entity in cleaned_result]
1160+
ordered_response = [alphabetize_dict_recursive(entity) for entity in complete]
1161+
return jsonify(ordered_response)
1162+
1163+
1164+
"""
1165+
Retrieve the document info needed for a given entity's descendant. Result filtering for this
1166+
endpoint is required and is given by the required parameter 'include'.
1167+
For example /descendants-info?include=uuid,status,entity_type
1168+
1169+
1170+
Parameters
1171+
----------
1172+
include : str
1173+
A comma delimited string of all the properties to be retrieved by the endpoint
1174+
1175+
Returns
1176+
-------
1177+
json
1178+
A list of dicts where each dict contains the requested fields for the given descendant.
1179+
"""
1180+
@app.route('/descendants-info/<uuid>', methods=['GET'])
1181+
def get_descendants_info(uuid):
1182+
validate_token_if_auth_header_exists(request)
1183+
include_fields = None
1184+
if bool(request.args):
1185+
included = request.args.get('include')
1186+
if included:
1187+
include_fields = [
1188+
f.strip().strip("'").strip('"')
1189+
for f in included.split(',')
1190+
if f.strip()
1191+
]
1192+
valid_fields = set(schema_manager.get_persistent_fields())
1193+
invalid = [f for f in include_fields if f not in valid_fields]
1194+
if invalid:
1195+
return bad_request_error(f"Invalid include fields: {invalid}")
1196+
else:
1197+
return bad_request_error(f"Missing required parameter: 'include'. Must include a list of properties to be returned.")
1198+
else:
1199+
return bad_request_error(f"Missing required parameter: 'include'. Must include a list of properties to be returned.")
1200+
result = app_neo4j_queries.get_descendants_trimmed(neo4j_driver_instance, uuid, included_fields=include_fields)
1201+
if result is None:
1202+
return not_found_error(f"Entity {uuid} not found")
1203+
cleaned_result = [schema_manager.remove_none_values(entity) for entity in result]
1204+
complete = [schema_manager.normalize_document_result_for_response(entity) for entity in cleaned_result]
1205+
ordered_response = [alphabetize_dict_recursive(entity) for entity in complete]
1206+
return jsonify(ordered_response)
1207+
1208+
1209+
"""
1210+
Retrieve the document info needed for a given entity's parents (immediate ancestors). Result filtering for this
1211+
endpoint is allowed and is given by the required parameter 'include'.
1212+
For example /parents-info?include=uuid,status,entity_type
1213+
1214+
1215+
Parameters
1216+
----------
1217+
include : str
1218+
A comma delimited string of all the properties to be retrieved by the endpoint
1219+
1220+
Returns
1221+
-------
1222+
json
1223+
A list of dicts where each dict contains the requested fields for the given parent.
1224+
"""
1225+
@app.route('/parents-info/<uuid>', methods=['GET'])
1226+
def get_parents_info(uuid):
1227+
validate_token_if_auth_header_exists(request)
1228+
included_fields = None
1229+
if bool(request.args):
1230+
included = request.args.get('include')
1231+
if included:
1232+
included_fields = [
1233+
f.strip().strip("'").strip('"')
1234+
for f in included.split(',')
1235+
if f.strip()
1236+
]
1237+
valid_fields = set(schema_manager.get_persistent_fields())
1238+
invalid = [f for f in included_fields if f not in valid_fields]
1239+
if invalid:
1240+
return bad_request_error(f"Invalid include fields: {invalid}")
1241+
result = app_neo4j_queries.get_parents_info(neo4j_driver_instance, uuid, included_fields=included_fields)
1242+
if result is None:
1243+
return not_found_error(f"Entity {uuid} not found")
1244+
cleaned_result = [schema_manager.remove_none_values(entity) for entity in result]
1245+
complete = [schema_manager.normalize_document_result_for_response(entity) for entity in cleaned_result]
1246+
ordered_response = [alphabetize_dict_recursive(entity) for entity in complete]
1247+
return jsonify(ordered_response)
1248+
1249+
1250+
"""
1251+
Retrieve the document info needed for a given entity's children (immediate descendants). Result filtering for this
1252+
endpoint is allowed and is given by the required parameter 'include'.
1253+
For example /children-info?include=uuid,status,entity_type
1254+
1255+
1256+
Parameters
1257+
----------
1258+
include : str
1259+
A comma delimited string of all the properties to be retrieved by the endpoint
1260+
1261+
Returns
1262+
-------
1263+
json
1264+
A list of dicts where each dict contains the requested fields for the given child.
1265+
"""
1266+
@app.route('/children-info/<uuid>', methods=['GET'])
1267+
def get_children_info(uuid):
1268+
validate_token_if_auth_header_exists(request)
1269+
included_fields = None
1270+
if bool(request.args):
1271+
included = request.args.get('include')
1272+
if included:
1273+
included_fields = [
1274+
f.strip().strip("'").strip('"')
1275+
for f in included.split(',')
1276+
if f.strip()
1277+
]
1278+
valid_fields = set(schema_manager.get_persistent_fields())
1279+
invalid = [f for f in included_fields if f not in valid_fields]
1280+
if invalid:
1281+
return bad_request_error(f"Invalid include fields: {invalid}")
1282+
result = app_neo4j_queries.get_children_info(neo4j_driver_instance, uuid, included_fields=included_fields)
1283+
if result is None:
1284+
return not_found_error(f"Entity {uuid} not found")
1285+
cleaned_result = [schema_manager.remove_none_values(entity) for entity in result]
1286+
complete = [schema_manager.normalize_document_result_for_response(entity) for entity in cleaned_result]
1287+
ordered_response = [alphabetize_dict_recursive(entity) for entity in complete]
1288+
return jsonify(ordered_response)
1289+
1290+
@app.route('/sources-info/<uuid>', methods=['GET'])
1291+
def get_sources_info(uuid):
1292+
validate_token_if_auth_header_exists(request)
1293+
result = app_neo4j_queries.get_source_samples(neo4j_driver_instance, uuid)
1294+
if result is None:
1295+
return not_found_error(f"Entity {uuid} not found")
1296+
cleaned_result = [schema_manager.remove_none_values(entity) for entity in result]
1297+
complete = [schema_manager.normalize_document_result_for_response(entity) for entity in cleaned_result]
1298+
ordered_response = [alphabetize_dict_recursive(entity) for entity in complete]
1299+
return jsonify(ordered_response)
1300+
1301+
@app.route('/origins-info/<uuid>', methods=['GET'])
1302+
def get_origin_info(uuid):
1303+
validate_token_if_auth_header_exists(request)
1304+
result = app_neo4j_queries.get_origin_samples(neo4j_driver_instance, uuid)
1305+
if result is None:
1306+
return not_found_error(f"Entity {uuid} not found")
1307+
cleaned_result = [schema_manager.remove_none_values(entity) for entity in result]
1308+
complete = [schema_manager.normalize_document_result_for_response(entity) for entity in cleaned_result]
1309+
ordered_response = [alphabetize_dict_recursive(entity) for entity in complete]
1310+
return jsonify(ordered_response)
1311+
1312+
@app.route('/donors-info/<uuid>', methods=['GET'])
1313+
def get_donors_info(uuid):
1314+
validate_token_if_auth_header_exists(request)
1315+
result = app_neo4j_queries.get_donor_info(neo4j_driver_instance, uuid)
1316+
if result is None:
1317+
return not_found_error(f"Entity {uuid} not found")
1318+
cleaned_result = [schema_manager.remove_none_values(entity) for entity in result]
1319+
complete = [schema_manager.normalize_document_result_for_response(entity) for entity in cleaned_result]
1320+
ordered_response = [alphabetize_dict_recursive(entity) for entity in complete]
1321+
return jsonify(ordered_response)
1322+
1323+
def alphabetize_dict_recursive(obj):
1324+
if isinstance(obj, dict):
1325+
return {k: alphabetize_dict_recursive(obj[k]) for k in sorted(obj.keys())}
1326+
elif isinstance(obj, list):
1327+
return [alphabetize_dict_recursive(item) for item in obj]
1328+
else:
1329+
return obj
1330+
1331+
1332+
"""
1333+
Retrieve processed dataset documents associated with a collection or upload
1334+
1335+
Parameters
1336+
----------
1337+
uuid : str
1338+
The UUID of the target entity (Collection, Epicollection, or Upload)
1339+
1340+
Returns
1341+
-------
1342+
json
1343+
A JSON object mapping dataset UUIDs to their processed document representations.
1344+
Each dataset is enriched via the trigger pipeline (ON_INDEX), normalized for response,
1345+
and stripped of selected large or unnecessary fields (e.g., ingest_metadata, metadata, files).
1346+
Returns a 404 error if the entity is not found.
1347+
"""
1348+
@app.route('/entities/<uuid>/dataset-documents', methods=['GET'])
1349+
def get_dataset_documents(uuid):
1350+
validate_token_if_auth_header_exists(request)
1351+
token = get_internal_token()
1352+
include_fields = None
1353+
if bool(request.args):
1354+
included = request.args.get('include')
1355+
if included:
1356+
include_fields = [
1357+
f.strip().strip("'").strip('"')
1358+
for f in included.split(',')
1359+
if f.strip()
1360+
]
1361+
# Validation step to ensure fields are real property names
1362+
valid_fields = set(schema_manager.get_persistent_fields())
1363+
invalid = [f for f in include_fields if f not in valid_fields]
1364+
if invalid:
1365+
return bad_request_error(f"Invalid include fields: {invalid}")
1366+
else:
1367+
return bad_request_error("Missing required parameter: 'include'. Must include a list of properties to be returned.")
1368+
else:
1369+
return bad_request_error("Missing required parameter: 'include'. Must include a list of properties to be returned.")
1370+
1371+
entity_record = app_neo4j_queries.get_dataset_documents_raw(
1372+
neo4j_driver_instance,
1373+
uuid,
1374+
included_fields=include_fields
1375+
)
1376+
if entity_record is None:
1377+
return not_found_error(f"Entity {uuid} not found")
1378+
1379+
result = {}
1380+
for dataset_uuid, entity_dict in entity_record.items():
1381+
try:
1382+
complete = schema_manager.remove_none_values({**entity_dict})
1383+
final = schema_manager.normalize_document_result_for_response(entity_dict=complete)
1384+
for field in ['ingest_metadata', 'metadata', 'files']:
1385+
final.pop(field, None)
1386+
result[dataset_uuid] = final
1387+
except Exception as e:
1388+
logger.error(f"Failed to process document for {dataset_uuid}: {e}")
1389+
continue
1390+
1391+
resp_body = json.dumps(result).encode('utf-8')
1392+
try_resp = try_stash_response_body(resp_body)
1393+
if try_resp is not None:
1394+
return try_resp
1395+
return jsonify(result)
1396+
11171397
"""
11181398
Create an entity of the target type in neo4j
11191399

0 commit comments

Comments
 (0)