Skip to content

Allow studies to prevent new timepoint creation on data import.#5075

Merged
labkey-klum merged 16 commits intodevelopfrom
fb_issue_49069
Dec 29, 2023
Merged

Allow studies to prevent new timepoint creation on data import.#5075
labkey-klum merged 16 commits intodevelopfrom
fb_issue_49069

Conversation

@labkey-klum
Copy link
Copy Markdown
Contributor

Rationale

tracking issue

Studies will automatically create new timepoints when data (dataset or specimen) is introduced into the study. In limited situations clients may not want this to happen. Years ago we added a folder import check for visit based studies only to fail if the data would introduce any new visits.

This PR takes it a step farther by introducing a study wide setting to disallow the creation of new timepoints for all of the pathways that data can flow into a study:

  • Dataset import (bulk, single row, API)
  • Link to study
  • Folder import
  • Specimen import
  • Publish and ancillary study
  • REDCap or CDISC integration

The new configuration UI is under the manage visit/timepoint link off of the manage study page.

Changes

  • Support both visit and date based studies
  • Respect the configuration. StudyManager.ensureVisits provides a single choke point to allow/disallow the timepoint creation, but provided no way to report back errors. The method now accepts a parameter to fail for undefined timepoints and will return a ValidationException to indicate any errors. There were many code paths into this method that needed to thread through the parameter as well as consume the return value where appropriate.
  • Round trip the setting for folder export/import. When importing an export of a study that had no visit map and had the fail setting enabled, the behavior is to allow the import of the data but then set the fail flag at the end of the import. Otherwise it would be impossible to import one of these types of exports. Publish and ancillary behaves similarly.

@github-actions
Copy link
Copy Markdown

WARNING: This PR appears to have the default title generated by GitHub. Please use something more descriptive.

isFailForUndefinedVisits: <%=bean.isFailForUndefinedVisits()%>,
isCloudRoot: <%=bean.isCloudRoot()%>, // Remove as part of Issue #43835
showFailForUndefinedVisits: <%=timepointType == null || timepointType == TimepointType.VISIT%>
showFailForUndefinedVisits: <%=(timepointType == null || timepointType == TimepointType.VISIT) && ((study == null) || !study.isFailForUndefinedTimepoints())%>
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This hides the legacy advanced folder import UI to fail for undefined visits if this is already configured at the study level.

Study study = StudyService.get().getStudy(getContainer());
if (loadInfo.getRowCount() > 0 && failForUndefinedVisits && study.getTimepointType() == TimepointType.VISIT)
checkForUndefinedVisits(loadInfo, study);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is the legacy folder import check, the new check is below in resyncStudy

{
result = StudyManager.getInstance().ensureVisit(this, null, sequenceNumBD, null, false);
result = StudyManager.getInstance().getVisit(this, null, sequenceNumBD, null);
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Change this to getVisit; the last parameter was saveIfNew and this was the only usage of that method. Got rid of the old method.

{
List<Double> undefinedSequenceNums = StudyManager.getInstance().getUndefinedSequenceNumsForDataset(_datasetDefinition.getContainer(), _datasetDefinition.getDatasetId());
if (!undefinedSequenceNums.isEmpty())
// check for undefined visits and don't commit the data if there are errors
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Swap out the legacy check for the newer check.

<labkey:input type="radio" name="timepointType" id="continuousTimepointType" value="<%=TimepointType.CONTINUOUS%>" checked="<%=(form.getTimepointType() == TimepointType.CONTINUOUS)%>" onChange="document.getElementById('defaultDurationRow').style.display = document.getElementById('dateTimepointType').checked ? 'table-row' : 'none'; document.getElementById('defaultDateRow').style.display = document.getElementById('continuousTimepointType').checked ? 'none' : 'table-row';" /> Continuous
<labkey:input type="radio" formGroup="false" name="timepointType" id="dateTimepointType" value="<%=TimepointType.DATE%>" checked="<%=(form.getTimepointType() == TimepointType.DATE)%>" onChange="document.getElementById('defaultDurationRow').style.display = document.getElementById('dateTimepointType').checked ? 'table-row' : 'none'; document.getElementById('defaultDateRow').style.display = document.getElementById('continuousTimepointType').checked ? 'none' : 'table-row';" /> Dates &nbsp;&nbsp;
<labkey:input type="radio" formGroup="false" name="timepointType" value="<%=TimepointType.VISIT%>" checked="<%=(form.getTimepointType() == TimepointType.VISIT || form.getTimepointType() == null)%>" onChange="document.getElementById('defaultDurationRow').style.display = document.getElementById('dateTimepointType').checked ? 'table-row' : 'none'; document.getElementById('defaultDateRow').style.display = document.getElementById('continuousTimepointType').checked ? 'none' : 'table-row';" /> Assigned Visits &nbsp;&nbsp;
<labkey:input type="radio" formGroup="false" name="timepointType" id="continuousTimepointType" value="<%=TimepointType.CONTINUOUS%>" checked="<%=(form.getTimepointType() == TimepointType.CONTINUOUS)%>" onChange="document.getElementById('defaultDurationRow').style.display = document.getElementById('dateTimepointType').checked ? 'table-row' : 'none'; document.getElementById('defaultDateRow').style.display = document.getElementById('continuousTimepointType').checked ? 'none' : 'table-row';" /> Continuous
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unrelated to this ticket but the create study page layout was off.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Great, I'll merge develop into my branch to pick up your change.

* @param potentiallyDeletedParticipants optionally, the specific participants that may have been removed from the
* study. If null, all participants will be checked to see if they are still in the study.
* @param participantVisitResyncRequired If true, will force an update of the ParticipantVisit mapping for this study
* @param forceUpdateCohorts If true, forces updateParticipantCohorts() (specimen import case)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

forceUpdateCohorts was never used.

clickAndWait(Locator.linkWithText(CALC_COL_QUERY_SNAPSHOT));
waitForSnapshotUpdate("-2");
waitForSnapshotUpdate("999151515");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Test was failing because the QuerySnapshot was being deleted before the snapshot had a chance to update itself.

@labkey-klum labkey-klum changed the title Fb issue 49069 Allow studies to prevent new timepoint creation on data import. Dec 21, 2023
@labkey-klum labkey-klum linked an issue Dec 21, 2023 that may be closed by this pull request
30 tasks
if (target.getDefaultTimepointDuration() < 1)
errors.reject(ERROR_MSG, "Default timepoint duration must be a positive number.");
StudyImpl study = getStudy();
if (study.getTimepointType() == TimepointType.DATE)
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.

curious why we are checking the timepoint type for the study and the target props form object (one here and one a few lines below). would they really ever differ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good question, both JSP's (manageVisits.jsp, manageTimepoints.jsp) now use the same controller endpoint. I had to make it a little more fault tolerant of the values getting posted. Perhaps I should make it clearer to the endpoint what type of information is being posted.


// after the data has been imported, configure the new study setting for undefined timepoints
if (sourceStudy.isFailForUndefinedTimepoints())
mutableStudy.setFailForUndefinedTimepoints(true);
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.

good call on needing to apply this after the visit map is created for the new study (as mentioned in the PR message).

@labkey-klum labkey-klum merged commit 9031621 into develop Dec 29, 2023
@labkey-klum labkey-klum deleted the fb_issue_49069 branch December 29, 2023 21:13
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.

Issue 49069: Ability to require that visits exist before entering data for them

4 participants