-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Finally implement row review #146
base: dev
Are you sure you want to change the base?
Conversation
@jthompson-arcus Maybe good to discuss the conceptual UI implementation together. |
I am happy for all input on the UI implementation. There are several ways it could go and no clear best option. |
Merge branch 'jt-99-review_by_row' into jt-99-review_by_row_for_reals # Conflicts: # R/mod_review_forms.R
R/mod_study_forms.R
Outdated
observe({ | ||
reload_data(reload_data() + 1) | ||
session$userData$update_checkboxes[[form]] <- NULL | ||
session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character()) | ||
}) |> | ||
bindEvent(r$subject_id, r$review_data, | ||
ignoreInit = TRUE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, each form is it's own module. I would expect it to fire for every module whenever the subject is changed or the review data is updated. I think this can be moved outside of those modules though and be contained in the review forms module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On further inspection, I don't think we really want to change any of this observer. There doesn't appear to be any advantage to further abstracting this logic. It's important to ensure the datatable's server-side object is getting reloaded whenever the subject is changed of the review data is updated.
R/mod_study_forms.R
Outdated
observeEvent(input$table_review_selection, { | ||
# Update review values for session's user data | ||
session$userData$update_checkboxes[[form]] <- NULL | ||
session$userData$review_records[[form]] <- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, review_records are the records as per selection status in the checkboxes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The object r$review_data
was originally designed to pass through review status of each item. I am still struggling to see why we need so many review objects and can't simplify this. Can't o_reviewed
be part of the review_data object instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In essence it is still being used in that way because that's where the field reviewed
is coming from. The new field o_reviewed
is necessary for the datatable object. Each datatable is pivoted, so the datatable itself would contain multiple records, this needs to be consolidated in some way for a row level review to even be possible.
R/mod_common_forms.R
Outdated
}) |> | ||
bindEvent(r$subject_id, r$review_data, | ||
ignoreInit = TRUE) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering: do you have a specific reason why you sometimes use observe/bindevent and sometimes observeEvent
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer using bindEvent()
when there are multiple triggers. In my opinion it is more readable.
R/mod_common_forms.R
Outdated
dplyr::rows_upsert( | ||
session$userData$review_records[[form]], | ||
input[[review_selection]][c("id", "reviewed")], | ||
by = "id" | ||
) |> | ||
dplyr::filter(!is.na(reviewed)) |> | ||
# Ensure that only the current subject is being reviewed | ||
dplyr::semi_join( | ||
subset(r$review_data, subject_id == r$subject_id & item_group == form), | ||
by = "id" | ||
) |> | ||
dplyr::select(-dplyr::starts_with("SAE")) | ||
# Only update records where the review status is being changed | ||
dplyr::anti_join( | ||
subset(r$review_data, subject_id == r$subject_id & item_group == form), | ||
by = c("id", "reviewed") | ||
) |> | ||
dplyr::arrange(id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the upsert/filter/semi_join and anti_join are a bit hard to follow for me. Is the semi_join really needed for example? Can't you just add subject_id and form to the filter directly?
Also, I think this part could be a candidate to extract in a helper function since it is duplicated in mod_review_forms
and is more or less 'business logic'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will take a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot add subject_id
and item_group
to the filter directly because input[[review_selection]]
does not contain those values. It only contains the id
's. This was intentional redundancy. Even though session$userData$review_records[[form]]
gets reset whenever r$subject_id
is changed, this ensures that no unintended records are being assessed. The overhead is very little because this is only triggered when a checkbox in a datatable is clicked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clinsight/R/fct_form_helpers.R
Lines 1 to 39 in f5fa833
#' Update Review Records | |
#' | |
#' Updates the review records data frame when a datatable checkbox is clicked. | |
#' | |
#' @param review_records The review records data frame to update. | |
#' @param review_selection The review selection data frame input from the | |
#' datatable. | |
#' @param active_data The active review data frame. | |
#' | |
#' @return A data frame containing the updated records data. | |
#' | |
#' @details Three main steps are performed: UPSERT, SUBSET, and ANTI-JOIN The | |
#' UPSERT takes the review selection data frame and upserts it into the review | |
#' records data frame. (An upsert will insert a record if the unique | |
#' identifier is not yet present and update a record based on the unique | |
#' identifier if it already exists.) The SUBSET step removes an empty reviews | |
#' (partially review rows) and any records not part of the active review (as a | |
#' precautionary measure). The ANTI-JOIN step removes any records that match | |
#' the active review (records that will not be changing review status based on | |
#' user inputs). | |
#' | |
#' @noRd | |
update_review_records <- function(review_records, review_selection, active_data) { | |
if (is.null(review_records)) | |
review_records <- data.frame(id = integer(), reviewed = character()) | |
review_records |> | |
dplyr::rows_upsert( | |
review_selection, | |
by = "id" | |
) |> | |
# Remove empty reviews and inactive data IDs | |
subset(!is.na(reviewed) | !id %in% active_data$id) |> | |
# Only update records where the review status is being changed | |
dplyr::anti_join( | |
active_data, | |
by = c("id", "reviewed") | |
) |> | |
dplyr::arrange(id) | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a view comments. I will have to stop now, and and probably can only continue in the new year. In general, I like the way it is implemented, but I am just wondering if we can make it a bit simpler and easier to follow. It is a bit hard to follow what happens when exactly, in what order.
Have a nice Christmas and new year!
With all the requests for helper functions and such, I decided it was best to move all this logic to it's own module. Now all changes can be made in one location and the differences between the forms are essentially eliminated. The module itself still needs to be documented. The PR grows and grows... |
Closes #99