Skip to content

Testing external contributor PR #313 - #315

Open
PlanXCyborg wants to merge 32 commits into
masterfrom
copy-of-external-pr-313
Open

Testing external contributor PR #313#315
PlanXCyborg wants to merge 32 commits into
masterfrom
copy-of-external-pr-313

Conversation

@PlanXCyborg

Copy link
Copy Markdown
Contributor

*** DO NOT MERGE ***
This PR was created automatically to test an external PR
Tested PR link here

@github-actions

Copy link
Copy Markdown

The style in this PR agrees with black. ✔️

This formatting comment was generated automatically by a script in uc-cdis/wool.

@github-actions

Copy link
Copy Markdown

Integration Tests

Test summary after running integration tests

filepath passed failed skipped SUBTOTAL
tests/test_dbgap.py 4 0 1 5
tests/test_ras_passport.py 0 0 2 2
tests/test_data_upload.py 8 0 1 9
tests/test_graph_submit_and_query.py 12 1 1 14
tests/test_presigned_url.py 8 0 0 8
tests/test_centralized_auth.py 5 0 0 5
tests/test_audit_service.py 1 0 0 1
tests/test_google_data_access.py 1 0 0 1
tests/test_gen3_sdk.py 1 0 0 1
TOTAL 40 1 5 46

Test summary after rerunning failed integration tests

filepath passed SUBTOTAL
tests/test_graph_submit_and_query.py 1 1
TOTAL 1 1

Please find the detailed integration test report here

Please find the detailed integration test report after rerunning failed tests here

Please find the Github Action logs here

Comment thread gen3/fhir.py Outdated
if force:
shutil.rmtree(output_dir, ignore_errors=True)

except BaseException as e:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't use BaseException here, it will completely absorb a CTRL+C from the terminal (KeyboardInterrupt). Ideally you shouldn't wrap so much code in a broad exception block. I would break the code out into a helper function so it's a bit cleaner to read, and while we don't encourage broad exception catching, if you do it, you should use the Exception class instead of BaseException.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this needs to exit(1) after all the logging. This currently shows a "success" exit message

@dsafarian dsafarian Aug 27, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case that someone hits CTRL+C at the start of the run, do we want to treat it as a force delete and delete the posisbly corrupt files immadiately?

The reason for the long try/except block and the broad exception is to be able to remove files if --force is passed at any point in the run for whatever the reason it is that the run failed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, ctrl+c is a kill immediately request from the user, so we should avoid capturing and doing anything else - consequences are the user's. So if they do that, then they may have to run force the next time for a clean run.

The broad exception is maybe okay, I'd just break it out to a helper function

Comment thread gen3/fhir.py Outdated
os.remove(f)


def fhir_tagger(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should rename this function to describe better the verb / action it takes. Currently it sounds like a class name (a thing that does something). I would argue for something like tag_fhir_resources_with_authz

Comment thread gen3/fhir.py Outdated
# initialize tagger
tagger = Gen3FHIRAuthzTagger(config_path=config)
# keep only relevant rules for the resource type
tagger.relevant_authz_rules(os.path.basename(input_file).split(".")[0])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that we rely explicitly on the filename for the FHIR resource type instead of checking the actual resourceType in the resource itself... I think we should reconsider this and instead read the first few lines of the file (and maybe last few) - you'll need this to be done without reading the whole file for efficiency, then check for that resourceType of those lines and if all the same: use that. and then make sure it's clear in docs that we expect 1 resource type per file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, but we can also check the resource type per record in transform chunk if necessary since we are already loading it

Comment thread gen3/fhir.py Outdated
if hook_result:
return hook_result

# check global catch-all fallback

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not really a fallback b/c the rules never evaluate. it's an override

Comment thread gen3/fhir.py
return 0

# force to delete everything disregarding status
if force:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should maybe only do this if --dry-run was not passed? I don't think it make sense to delete everything if it's a dry run - but if there's an argument for it, I'm open to it

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The force option is for someone to pass in a normal run if they don't want any intermediates remaining. Otherwise, all intermediates remain in the temp folder. The dry-run option is in cleanup, where the user can choose whether they just want a list of files that would be deleted if they were to run the cleanup. Let me know if you want the dry-run as an option for the main pipeline as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope it's okay to leave it out

Comment thread gen3/fhir.py
self.config = yaml.safe_load(f)
self.custom_hook = custom_hook

def relevant_authz_rules(self, resource_type: str):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can make this even faster b/c fhirpathpy allows you to "compile" rules once and apply them over and over (which saves a good chunk of time). https://github.com/beda-software/fhirpath-py#compile

evaluate(resource, "Patient.gender = 'male'") takes the expression as a string and re-parses it every call. compile() does the parse once and hands back a callable that just applies the pre-parsed rule. Right now we reparse on every record for every rule.

You could build the callables once in relevant_authz_rules (where the rules are already being filtered) and call them in determine_authz instead of evaluate(resource, rule["condition"]).

Comment thread gen3/cli/fhir.py Outdated

@click.command(
context_settings={"help_option_names": ["-h", "--help"]},
help="Tag Bulk FHIR data with Gen3 compatible authorization tags",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add more detail here about the input_file, what type - what should be in it, etc
samre for output_file and config. The user needs all the detail about what to provide when they run poetry run gen3 fhir transform --help

Comment thread gen3/fhir.py Outdated
make_folders_for_filename(TMP_ROOT)

# make temp directories
hash = get_md5hash(input_file)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may want to think through how to check _is_done before hashing where possible. B/c if this input is like 100GB, this is going to take a while even if it's already been run previuosly. Maybe that's okay

Comment thread gen3/fhir.py Outdated
raise click.UsageError("input_file and output_file must be different")

# only necessary the first time its run
make_folders_for_filename(TMP_ROOT)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't do this b/c I think this treats the final /tmp as a filename.

just do: TMP_ROOT.mkdir(parents=True, exist_ok=True)

Comment thread gen3/fhir.py Outdated
from collections.abc import Callable

logging = get_logger(__name__)
TMP_ROOT = pathlib.Path(".fhir_transform/tmp")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't define this twice (here and CLI). ALso note that this is going to be scoped to the current directory
So resume only works if you re-run from the same directory, every directory you ever run from accumulates state, and cleanup only really sees the current directory.

Let's change it to a static location and allow it to be configured by an env var or passed argument (--work-dir). And maybe make a helper to get the right working dir.

DEFAULT_WORK_DIR = "~/.cache/gen3/fhir_transform"

def resolve_work_dir(work_dir: str | os.PathLike[str] | None = None) -> pathlib.Path:
    root = pathlib.Path(
        work_dir or os.environ.get("GEN3_FHIR_WORK_DIR") or DEFAULT_WORK_DIR
    ).expanduser()

    root.mkdir(parents=True, exist_ok=True)

    # 0o700 makes it only only readable by owner and not everyone else (which is important 
    # for potentially shared machines and potential FHIR PHI)
    root.chmod(0o700)

    return root

We'll need to update fhir cleanup (and everywhere else that was relying on this path) to go through this same resolver

@Avantol13 Avantol13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comments

…d functions, included more details in --help
@github-actions

Copy link
Copy Markdown

Integration Tests

Test summary after running integration tests

filepath passed failed skipped SUBTOTAL
tests/test_dbgap.py 4 0 1 5
tests/test_ras_passport.py 0 0 2 2
tests/test_data_upload.py 8 0 1 9
tests/test_graph_submit_and_query.py 12 1 1 14
tests/test_presigned_url.py 8 0 0 8
tests/test_centralized_auth.py 5 0 0 5
tests/test_audit_service.py 1 0 0 1
tests/test_google_data_access.py 1 0 0 1
tests/test_gen3_sdk.py 1 0 0 1
TOTAL 40 1 5 46

Test summary after rerunning failed integration tests

filepath passed SUBTOTAL
tests/test_graph_submit_and_query.py 1 1
TOTAL 1 1

Please find the detailed integration test report here

Please find the detailed integration test report after rerunning failed tests here

Please find the Github Action logs here

@github-actions

Copy link
Copy Markdown

Integration Tests

Test summary after running integration tests

filepath passed failed skipped SUBTOTAL
tests/test_dbgap.py 4 0 1 5
tests/test_ras_passport.py 0 0 2 2
tests/test_data_upload.py 8 0 1 9
tests/test_graph_submit_and_query.py 12 1 1 14
tests/test_presigned_url.py 8 0 0 8
tests/test_centralized_auth.py 5 0 0 5
tests/test_audit_service.py 1 0 0 1
tests/test_google_data_access.py 1 0 0 1
tests/test_gen3_sdk.py 1 0 0 1
TOTAL 40 1 5 46

Test summary after rerunning failed integration tests

filepath passed SUBTOTAL
tests/test_graph_submit_and_query.py 1 1
TOTAL 1 1

Please find the detailed integration test report here

Please find the detailed integration test report after rerunning failed tests here

Please find the Github Action logs here

@github-actions

Copy link
Copy Markdown

Integration Tests

Test summary after running integration tests

filepath passed failed skipped SUBTOTAL
tests/test_dbgap.py 4 0 1 5
tests/test_ras_passport.py 0 0 2 2
tests/test_data_upload.py 8 0 1 9
tests/test_graph_submit_and_query.py 12 1 1 14
tests/test_presigned_url.py 8 0 0 8
tests/test_centralized_auth.py 5 0 0 5
tests/test_audit_service.py 1 0 0 1
tests/test_google_data_access.py 1 0 0 1
tests/test_gen3_sdk.py 1 0 0 1
TOTAL 40 1 5 46

Test summary after rerunning failed integration tests

filepath passed SUBTOTAL
tests/test_graph_submit_and_query.py 1 1
TOTAL 1 1

Please find the detailed integration test report here

Please find the detailed integration test report after rerunning failed tests here

Please find the Github Action logs here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants