Skip to content

Commit eba338e

Browse files
added new status retracted which behaves the same as published. Misc other required changes
1 parent f487afc commit eba338e

6 files changed

Lines changed: 36 additions & 59 deletions

File tree

src/app.py

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ def get_ancestor_organs(id):
492492
public_entity = True
493493
if schema_manager.entity_type_instanceof(normalized_entity_type, 'Dataset'):
494494
# Only published/public datasets don't require token
495-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
495+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
496496
public_entity = False
497497
# Token is required and the user must belong to HuBMAP-READ group
498498
token = get_user_token(request, non_public_access_required = True)
@@ -981,7 +981,7 @@ def get_entity_provenance(id):
981981

982982
if schema_manager.entity_type_instanceof(normalized_entity_type, 'Dataset'):
983983
# Only published/public datasets don't require token
984-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
984+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
985985
# Token is required and the user must belong to HuBMAP-READ group
986986
token = get_user_token(request, non_public_access_required = True)
987987
else:
@@ -1262,7 +1262,7 @@ def create_entity(entity_type):
12621262

12631263
# Only published datasets can have revisions made of them. Verify that that status of the Dataset specified
12641264
# by previous_revision_uuid is published. Else, bad request error.
1265-
if previous_version_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
1265+
if previous_version_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
12661266
bad_request_error(f"The previous_revision_uuid specified for this dataset must be 'Published' in order to create a new revision from it")
12671267

12681268
# If the preceding "additional validations" did not raise an error,
@@ -1655,7 +1655,7 @@ def get_ancestors(id):
16551655

16561656
if schema_manager.entity_type_instanceof(normalized_entity_type, 'Dataset'):
16571657
# Only published/public datasets don't require token
1658-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
1658+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
16591659
public_entity = False
16601660
# Token is required and the user must belong to HuBMAP-READ group
16611661
token = get_user_token(request, non_public_access_required = True)
@@ -1862,7 +1862,7 @@ def get_parents(id):
18621862

18631863
if schema_manager.entity_type_instanceof(normalized_entity_type, 'Dataset'):
18641864
# Only published/public datasets don't require token
1865-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
1865+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
18661866
public_entity = False
18671867
# Token is required and the user must belong to HuBMAP-READ group
18681868
token = get_user_token(request, non_public_access_required = True)
@@ -2070,7 +2070,7 @@ def get_siblings(id):
20702070

20712071
if schema_manager.entity_type_instanceof(normalized_entity_type, 'Dataset'):
20722072
# Only published/public datasets don't require token
2073-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
2073+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
20742074
public_entity = False
20752075
# Token is required and the user must belong to HuBMAP-READ group
20762076
token = get_user_token(request, non_public_access_required = True)
@@ -2207,7 +2207,7 @@ def get_tuplets(id):
22072207

22082208
if schema_manager.entity_type_instanceof(normalized_entity_type, 'Dataset'):
22092209
# Only published/public datasets don't require token
2210-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
2210+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
22112211
public_entity = False
22122212
# Token is required and the user must belong to HuBMAP-READ group
22132213
token = get_user_token(request, non_public_access_required = True)
@@ -2459,7 +2459,7 @@ def get_collections(id):
24592459
if not schema_manager.entity_type_instanceof(normalized_entity_type, 'Dataset'):
24602460
bad_request_error(f"Unsupported entity type of id {id}: {normalized_entity_type}")
24612461

2462-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
2462+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
24632463
public_entity = False
24642464
# Token is required and the user must belong to HuBMAP-READ group
24652465
token = get_user_token(request, non_public_access_required = True)
@@ -2567,7 +2567,7 @@ def get_uploads(id):
25672567
if not schema_manager.entity_type_instanceof(normalized_entity_type, 'Dataset'):
25682568
bad_request_error(f"Unsupported entity type of id {id}: {normalized_entity_type}")
25692569

2570-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
2570+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
25712571
# Token is required and the user must belong to HuBMAP-READ group
25722572
token = get_user_token(request, non_public_access_required = True)
25732573

@@ -2883,7 +2883,7 @@ def get_globus_url(id):
28832883
globus_server_uuid = app.config['GLOBUS_PROTECTED_ENDPOINT_UUID']
28842884
access_dir = access_level_prefix_dir(app.config['PROTECTED_DATA_SUBDIR'])
28852885
dir_path = dir_path + access_dir + group_name + "/"
2886-
elif (entity_data_access_level == ACCESS_LEVEL_PROTECTED) and (entity_dict['status'] == 'Published'):
2886+
elif (entity_data_access_level == ACCESS_LEVEL_PROTECTED) and (entity_dict['status'] in ['Published', 'Retracted']):
28872887
globus_server_uuid = app.config['GLOBUS_PUBLIC_ENDPOINT_UUID']
28882888
access_dir = access_level_prefix_dir(app.config['PUBLIC_DATA_SUBDIR'])
28892889
dir_path = dir_path + access_dir + "/"
@@ -3016,7 +3016,7 @@ def get_dataset_revision_number(id):
30163016
bad_request_error("The entity of given id is not a Dataset or Publication")
30173017

30183018
# Only published/public datasets don't require token
3019-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
3019+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
30203020
# Token is required and the user must belong to HuBMAP-READ group
30213021
token = get_user_token(request, non_public_access_required = True)
30223022

@@ -3170,7 +3170,7 @@ def get_revisions_list(id):
31703170
bad_request_error("The entity is not a Dataset. Found entity type:" + normalized_entity_type)
31713171

31723172
# Only published/public datasets don't require token
3173-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
3173+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
31743174
# Token is required and the user must belong to HuBMAP-READ group
31753175
token = get_user_token(request, non_public_access_required=True)
31763176

@@ -3200,7 +3200,7 @@ def get_revisions_list(id):
32003200
normalized_revisions_list.pop(0)
32013201

32023202
# Also hide the 'next_revision_uuid' of the second last revision from response
3203-
if 'next_revision_uuid' in normalized_revisions_list[0]:
3203+
if normalized_revisions_list and 'next_revision_uuid' in normalized_revisions_list[0]:
32043204
normalized_revisions_list[0].pop('next_revision_uuid')
32053205

32063206
# Now all we need to do is to compose the result list
@@ -3255,7 +3255,7 @@ def get_associated_organs_from_dataset(id):
32553255
excluded_fields = schema_manager.get_fields_to_exclude('Sample')
32563256
public_entity = True
32573257
# published/public datasets don't require token
3258-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
3258+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
32593259
public_entity = False
32603260
# Token is required and the user must belong to HuBMAP-READ group
32613261
token = get_user_token(request, non_public_access_required=True)
@@ -3316,7 +3316,7 @@ def get_associated_samples_from_dataset(id):
33163316
bad_request_error("The entity of given id is not a Dataset or Publication")
33173317
public_entity = True
33183318
# published/public datasets don't require token
3319-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
3319+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
33203320
# Token is required and the user must belong to HuBMAP-READ group
33213321
public_entity = False
33223322
token = get_user_token(request, non_public_access_required=True)
@@ -3376,7 +3376,7 @@ def get_associated_donors_from_dataset(id):
33763376
bad_request_error("The entity of given id is not a Dataset or Publication")
33773377
public_entity = True
33783378
# published/public datasets don't require token
3379-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
3379+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
33803380
public_entity = False
33813381
# Token is required and the user must belong to HuBMAP-READ group
33823382
token = get_user_token(request, non_public_access_required=True)
@@ -3451,7 +3451,7 @@ def get_prov_info_for_dataset(id):
34513451
bad_request_error("The entity of given id is not a Dataset")
34523452

34533453
# published/public datasets don't require token
3454-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
3454+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
34553455
# Token is required and the user must belong to HuBMAP-READ group
34563456
token = get_user_token(request, non_public_access_required=True)
34573457

@@ -3959,14 +3959,14 @@ def paired_dataset(id):
39593959
if normalized_entity_type != 'Dataset':
39603960
bad_request_error("The target entity of the specified id is not a Dataset")
39613961

3962-
if entity_dict['status'].lower() != DATASET_STATUS_PUBLISHED:
3962+
if entity_dict['status'].lower() not in [DATASET_STATUS_PUBLISHED, 'retracted']:
39633963
if not user_in_hubmap_read_group(request):
39643964
forbidden_error("Access not granted")
39653965

39663966
paired_dataset = app_neo4j_queries.get_paired_dataset(neo4j_driver_instance, uuid, data_type, search_depth)
39673967
out_list = []
39683968
for result in paired_dataset:
3969-
if user_in_hubmap_read_group(request) or result['status'].lower() == 'published':
3969+
if user_in_hubmap_read_group(request) or result['status'].lower() in ['published', 'retracted']:
39703970
out_list.append(result['uuid'])
39713971
if len(out_list) < 1:
39723972
not_found_error(f"Search for paired datasets of type {data_type} for dataset with id {uuid} returned no results")
@@ -4512,7 +4512,7 @@ def _get_entity_visibility(normalized_entity_type, entity_dict):
45124512
# it can be used along with the user's authorization to determine access.
45134513
entity_visibility=DataVisibilityEnum.NONPUBLIC
45144514
if schema_manager.entity_type_instanceof(normalized_entity_type, 'Dataset') and \
4515-
entity_dict['status'].lower() == DATASET_STATUS_PUBLISHED:
4515+
entity_dict['status'].lower() in [DATASET_STATUS_PUBLISHED, 'retracted']:
45164516
entity_visibility=DataVisibilityEnum.PUBLIC
45174517
elif schema_manager.entity_type_instanceof(normalized_entity_type, 'Collection') and \
45184518
'registered_doi' in entity_dict and \
@@ -4524,12 +4524,10 @@ def _get_entity_visibility(normalized_entity_type, entity_dict):
45244524
# Get the data_access_level for each Dataset in the Collection from Neo4j
45254525
collection_dataset_statuses = schema_neo4j_queries.get_collection_datasets_statuses(neo4j_driver_instance
45264526
,entity_dict['uuid'])
4527+
PUBLIC_STATUSES = {SchemaConstants.DATASET_STATUS_PUBLISHED, "retracted"}
45274528

4528-
# If the list of distinct statuses for Datasets in the Collection only has one entry, and
4529-
# it is 'published', the Collection is public
4530-
if len(collection_dataset_statuses) == 1 and \
4531-
collection_dataset_statuses[0].lower() == SchemaConstants.DATASET_STATUS_PUBLISHED:
4532-
entity_visibility=DataVisibilityEnum.PUBLIC
4529+
if all(status.lower() in PUBLIC_STATUSES for status in collection_dataset_statuses):
4530+
entity_visibility = DataVisibilityEnum.PUBLIC
45334531
elif normalized_entity_type == 'Upload':
45344532
# Upload entities require authorization to access, so keep the
45354533
# entity_visibility as non-public, as initialized outside block.

src/app_neo4j_queries.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ def get_sorted_revisions(neo4j_driver, uuid):
349349

350350
def get_sorted_multi_revisions(neo4j_driver, uuid, fetch_all=True, property_key=False):
351351
results = []
352-
match_case = '' if fetch_all is True else 'AND prev.status = "Published" AND next.status = "Published" '
352+
match_case = '' if fetch_all is True else 'AND prev.status IN ["Published", "Retracted"] AND next.status IN ["Published", "Retracted"] '
353353
collect_prop = f".{property_key}" if property_key else ''
354354

355355
query = (
@@ -902,7 +902,7 @@ def get_all_dataset_samples(neo4j_driver, dataset_uuid):
902902
def get_sankey_info(neo4j_driver, public_only):
903903
public_only_query = " "
904904
if public_only:
905-
public_only_query = f"AND toLower(ds.status) = 'published' "
905+
public_only_query = f"AND toLower(ds.status) IN ['published', 'retracted'] "
906906
query = (f"MATCH (donor:Donor)-[:ACTIVITY_INPUT]->(organ_activity:Activity)-[:ACTIVITY_OUTPUT]-> "
907907
f"(organ:Sample {{sample_category:'organ'}})-[*]->(a:Activity)-[:ACTIVITY_OUTPUT]->(ds:Dataset) "
908908
f"WHERE toLower(a.creation_action) = 'create dataset activity' "
@@ -945,7 +945,7 @@ def get_sankey_info(neo4j_driver, public_only):
945945
def get_unpublished(neo4j_driver):
946946
query = (
947947
"MATCH (ds:Dataset)<-[*]-(d:Donor) "
948-
"WHERE ds.status <> 'Published' and ds.status <> 'Hold' "
948+
"WHERE NOT ds.status IN ['Published', 'Hold', 'Retracted'] "
949949
# specimen_type -> sample_category 12/15/2022
950950
"OPTIONAL MATCH (ds)<-[*]-(s:Sample {sample_category:'organ'}) "
951951
"RETURN distinct ds.data_types as data_types, ds.group_name as organization, ds.uuid as uuid, "

src/schema/provenance_schema.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -640,9 +640,6 @@ ENTITIES:
640640
retraction_reason:
641641
type: string
642642
indexed: true
643-
before_property_update_validators:
644-
- validate_if_retraction_permitted
645-
- validate_sub_status_provided
646643
description: 'Information recorded about why a the dataset was retracted.'
647644
provider_info:
648645
type: string

src/schema/schema_neo4j_queries.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1692,7 +1692,7 @@ def get_component_dataset_uuids(neo4j_driver, uuid):
16921692
def count_attached_published_datasets(neo4j_driver, entity_type, uuid):
16931693
query = (f"MATCH (e:{entity_type})-[:ACTIVITY_INPUT|ACTIVITY_OUTPUT*]->(d:Dataset) "
16941694
# Use the string function toLower() to avoid case-sensetivity issue
1695-
f"WHERE e.uuid='{uuid}' AND toLower(d.status) = 'published' "
1695+
f"WHERE e.uuid='{uuid}' AND toLower(d.status) IN ['published', 'retracted'] "
16961696
# COLLECT() returns a list
16971697
# apoc.coll.toSet() reruns a set containing unique nodes
16981698
f"RETURN COUNT(d) AS {record_field_name}")

src/schema/schema_triggers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1766,7 +1766,7 @@ def update_status(property_key, normalized_type, request_args, user_token, exist
17661766
set_status_history(property_key, normalized_type, request_args, user_token, existing_data_dict, new_data_dict)
17671767

17681768
# Only apply to non-published parent datasets
1769-
if status.lower() != 'published':
1769+
if status.lower() not in ['published', 'retracted']:
17701770
# Only sync the child component datasets status for Multi-Assay Split
17711771
component_dataset_uuids = schema_neo4j_queries.get_component_dataset_uuids(schema_manager.get_neo4j_driver_instance(), uuid)
17721772

src/schema/schema_validators.py

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,9 @@ def halt_DOI_if_unpublished_dataset(property_key, normalized_entity_type, reques
298298
# simply get the existing, distinct 'data_access_level' setting for all the Datasets in the Collection
299299
distinct_dataset_statuses = schema_neo4j_queries.get_collection_datasets_statuses(neo4j_driver_instance
300300
,existing_data_dict['uuid'])
301-
if len( distinct_dataset_statuses) != 1 or \
302-
distinct_dataset_statuses[0].lower() != SchemaConstants.DATASET_STATUS_PUBLISHED:
301+
PUBLIC_STATUSES = {SchemaConstants.DATASET_STATUS_PUBLISHED, 'retracted'}
302+
303+
if not all(status.lower() in PUBLIC_STATUSES for status in distinct_dataset_statuses):
303304
raise ValueError(f"Unable to modify existing {existing_data_dict['entity_type']}"
304305
f" {existing_data_dict['uuid']} for DOI since it contains unpublished Datasets.")
305306

@@ -414,8 +415,9 @@ def validate_application_header_before_property_update(property_key, normalized_
414415
def validate_dataset_status_value(property_key, normalized_entity_type, request, existing_data_dict, new_data_dict):
415416
# Use lowercase for comparison
416417
accepted_status_values = [
417-
'new', 'processing', 'published', 'qa', 'error', 'hold', 'invalid', 'submitted', 'incomplete', 'approval', 'retracted'
418+
'new', 'processing', 'published', 'qa', 'error', 'hold', 'invalid', 'submitted', 'incomplete', 'approval'
418419
]
420+
# Retracted intentionally omitted. Status will be set to retracted only on a manual basis
419421
new_status = new_data_dict[property_key].lower()
420422

421423
if new_status not in accepted_status_values:
@@ -426,7 +428,7 @@ def validate_dataset_status_value(property_key, normalized_entity_type, request,
426428

427429
# If status == 'Published' already in Neo4j, then fail for any changes at all
428430
# Because once published, the dataset should be read-only
429-
if existing_data_dict['status'].lower() == SchemaConstants.DATASET_STATUS_PUBLISHED:
431+
if existing_data_dict['status'].lower() in [SchemaConstants.DATASET_STATUS_PUBLISHED, 'retracted']:
430432
raise ValueError(f"The status of this {normalized_entity_type} is already 'Published', status change is not allowed")
431433

432434
# HTTP header names are case-insensitive
@@ -480,6 +482,7 @@ def validate_status_changed(property_key, normalized_entity_type, request, exist
480482
The json data in request body, already after the regular validations
481483
"""
482484
def validate_if_retraction_permitted(property_key, normalized_entity_type, request, existing_data_dict, new_data_dict):
485+
# This validator is currently unused. Keeping it in case we decide we want an api endpoint for retraction at some point.
483486
if 'status' not in existing_data_dict:
484487
raise KeyError("Missing 'status' key in 'existing_data_dict' during calling 'validate_if_retraction_permitted()' validator method.")
485488

@@ -506,27 +509,6 @@ def validate_if_retraction_permitted(property_key, normalized_entity_type, reque
506509
raise ValueError("Permission denied, retraction is not allowed")
507510

508511

509-
"""
510-
Validate the sub_status field is also provided when Dataset.retraction_reason is provided on update via PUT
511-
512-
Parameters
513-
----------
514-
property_key : str
515-
The target property key
516-
normalized_type : str
517-
Submission
518-
request: Flask request object
519-
The instance of Flask request passed in from application request
520-
existing_data_dict : dict
521-
A dictionary that contains all existing entity properties
522-
new_data_dict : dict
523-
The json data in request body, already after the regular validations
524-
"""
525-
def validate_sub_status_provided(property_key, normalized_entity_type, request, existing_data_dict, new_data_dict):
526-
if 'sub_status' not in new_data_dict:
527-
raise ValueError("Missing sub_status field when retraction_reason is provided")
528-
529-
530512
"""
531513
Validate the reaction_reason field is also provided when Dataset.sub_status is provided on update via PUT
532514
@@ -1019,7 +1001,7 @@ def _validate_application_header(applications_allowed, request_headers):
10191001
def _is_entity_locked_against_update(existing_entity_dict):
10201002
entity_type = existing_entity_dict['entity_type']
10211003
if entity_type in ['Publication','Dataset']:
1022-
if 'status' in existing_entity_dict and existing_entity_dict['status'] == 'Published':
1004+
if 'status' in existing_entity_dict and existing_entity_dict['status'] in ['Published', 'Retracted']:
10231005
raise schema_errors.LockedEntityUpdateException(f"Permission denied to change a published/public {entity_type}.")
10241006
elif entity_type in ['Donor','Sample']:
10251007
if 'data_access_level' in existing_entity_dict and existing_entity_dict['data_access_level'] == 'public':

0 commit comments

Comments
 (0)