Testing external contributor PR #313 - #315
Conversation
… remove unecessary dependencies
|
The style in this PR agrees with This formatting comment was generated automatically by a script in uc-cdis/wool. |
Integration TestsTest summary after running integration tests
Test summary after rerunning failed integration tests
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 |
| if force: | ||
| shutil.rmtree(output_dir, ignore_errors=True) | ||
|
|
||
| except BaseException as e: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
also this needs to exit(1) after all the logging. This currently shows a "success" exit message
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
| os.remove(f) | ||
|
|
||
|
|
||
| def fhir_tagger( |
There was a problem hiding this comment.
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
| # 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]) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Done, but we can also check the resource type per record in transform chunk if necessary since we are already loading it
| if hook_result: | ||
| return hook_result | ||
|
|
||
| # check global catch-all fallback |
There was a problem hiding this comment.
it's not really a fallback b/c the rules never evaluate. it's an override
| return 0 | ||
|
|
||
| # force to delete everything disregarding status | ||
| if force: |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
nope it's okay to leave it out
| self.config = yaml.safe_load(f) | ||
| self.custom_hook = custom_hook | ||
|
|
||
| def relevant_authz_rules(self, resource_type: str): |
There was a problem hiding this comment.
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"]).
|
|
||
| @click.command( | ||
| context_settings={"help_option_names": ["-h", "--help"]}, | ||
| help="Tag Bulk FHIR data with Gen3 compatible authorization tags", |
There was a problem hiding this comment.
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
| make_folders_for_filename(TMP_ROOT) | ||
|
|
||
| # make temp directories | ||
| hash = get_md5hash(input_file) |
There was a problem hiding this comment.
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
| raise click.UsageError("input_file and output_file must be different") | ||
|
|
||
| # only necessary the first time its run | ||
| make_folders_for_filename(TMP_ROOT) |
There was a problem hiding this comment.
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)
| from collections.abc import Callable | ||
|
|
||
| logging = get_logger(__name__) | ||
| TMP_ROOT = pathlib.Path(".fhir_transform/tmp") |
There was a problem hiding this comment.
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
…d functions, included more details in --help
Integration TestsTest summary after running integration tests
Test summary after rerunning failed integration tests
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 |
…h, update function descriptions
…t in the unit tests
Integration TestsTest summary after running integration tests
Test summary after rerunning failed integration tests
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 |
… through API instead
Integration TestsTest summary after running integration tests
Test summary after rerunning failed integration tests
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 |
*** DO NOT MERGE ***
This PR was created automatically to test an external PR
Tested PR link here