Skip to content

Commit

Permalink
Merge pull request #1279 from data-team-uhn/CARDS-2017_CARDS-2019
Browse files Browse the repository at this point in the history
CARDS-2017 / CARDS-2019 - Add a `draftLifetime` field to the `Patient Access` configuration
  • Loading branch information
sdumitriu authored Feb 7, 2023
2 parents 8e2f7cb + 9a5c653 commit 4b419db
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@ export const PATIENT_ACCESS_CONFIG_PATH = "/Survey/PatientAccess";
export const DEFAULT_PATIENT_ACCESS_CONFIG = {
tokenlessAuthEnabled: false,
PIIAuthRequired: false,
allowedPostVisitCompletionTime: "0"
allowedPostVisitCompletionTime: "0",
draftLifetime: "-1"
};

const useStyles = makeStyles(theme => ({
textField: {
margin: theme.spacing(3, 0),
"& .MuiFormLabel-root" : {
color: theme.palette.text.primary,
},
"& .MuiTextField-root" : {
maxWidth: "250px",
"& .MuiInputBase-root" : {
maxWidth: "150px",
},
},
}));
Expand All @@ -53,16 +55,31 @@ function PatientAccessConfiguration() {

const [ patientAccessConfig, setPatientAccessConfig ] = useState();
const [ hasChanges, setHasChanges ] = useState(false);
const [ error, setError ] = useState({});

const labels = {
// Boolean fields can have one label
// Text fields can have one label, one optional helper text, one optional error text
const LABELS = {
tokenlessAuthEnabled: "Patients can answer surveys without a personalized link",
PIIAuthRequired: "Patients must confirm their identity by providing their date of birth and either MRN or HCN",
allowedPostVisitCompletionTime: "Patients can fill out surveys after the associated event for:"
allowedPostVisitCompletionTime: [
"Relatively to the associated event, patients can fill out surveys within:",
"Use a negative number when patient responses are due a number of days before the event, 0 for the day of the event, and a positive number when their responses are expected after the event."
],
draftLifetime: [
"Patients can edit unsubmitted responses for:",
"-1 means that drafts are kept until the patient is no longer able to access their surveys, 0 means drafts are deleted daily at midnight, 1 means they are kept until the next day at midmight, etc.",
"Please use a value of at least 0, or -1 to disable periodic draft deletion."
]
};

const LIMITS = {
draftLifetime: {min: -1}
}

let buildConfigData = (formData) => {
for (let key of Object.keys(patientAccessConfig)) {
!key.startsWith("jcr:") && formData.append(key, patientAccessConfig[key] || DEFAULT_PATIENT_ACCESS_CONFIG[key]);
!key.startsWith("jcr:") && formData.append(key, patientAccessConfig[key]);
}
}

Expand All @@ -78,25 +95,34 @@ function PatientAccessConfiguration() {
setHasChanges(true);
}}
/>}
label={labels[key]}
label={LABELS[key]}
/>
</ListItem>
);

let onInputValueChanged = (key, value) => {
setPatientAccessConfig(config => ({...config, [key]: (value || "")}));
setHasChanges(true);
setError(err => ({...err, [key]: (LIMITS[key]?.min > value || LIMITS[key]?.max < value)}));
}

let renderConfigInput = (key, unit) => (
<ListItem>
<FormGroup className={classes.textField}>
<FormLabel>{labels[key]}</FormLabel>
<FormLabel>{LABELS[key][0]}</FormLabel>
<TextField
variant="standard"
type="number"
onChange={event => { setPatientAccessConfig({...patientAccessConfig, [key]: event.target.value}); setHasChanges(true); }}
onBlur={event => { setPatientAccessConfig({...patientAccessConfig, [key]: event.target.value}); setHasChanges(true); }}
onChange={event => onInputValueChanged(key, event.target.value)}
onBlur={event => onInputValueChanged(key, event.target.value)}
placeholder={DEFAULT_PATIENT_ACCESS_CONFIG[key] || ""}
value={patientAccessConfig?.[key]}
error={error[key]}
helperText={error[key] ? LABELS[key][2] : LABELS[key][1]}
InputProps={{
endAdornment: unit && <InputAdornment position="end">{unit}</InputAdornment>,
}}
inputProps={LIMITS[key]}
/>
</FormGroup>
</ListItem>
Expand All @@ -116,6 +142,7 @@ function PatientAccessConfiguration() {
{ renderConfigCheckbox("tokenlessAuthEnabled") }
{ renderConfigCheckbox("PIIAuthRequired", patientAccessConfig?.tokenlessAuthEnabled) }
{ renderConfigInput("allowedPostVisitCompletionTime", "days") }
{ renderConfigInput("draftLifetime", "days") }
</List>
</AdminConfigScreen>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,14 @@ public interface PatientAccessConfiguration
* @return A number of days
*/
int getAllowedPostVisitCompletionTime();

/**
* Get the configured amount of time, in days, that patient's draft responses are kept in the database and the
* patient is allowed to continue filling them out. After the specified number of days, the draft responses would
* be deleted and the patient would have to start over. If a value is not specified for this setting or if it is
* {@code -1}, the draft will remain in the database until the patient no longer has access to it.
*
* @return A number of days
*/
int getDraftLifetime();
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public class PatientAccessConfigurationImpl extends AbstractNodeUtils implements
/** Property on config node for the number of days a token is valid for. */
private static final String TOKEN_LIFETIME_PROP = "allowedPostVisitCompletionTime";

/** Property on config node for the number of days draft responses from patients are kept. */
private static final String DRAFT_LIFETIME_PROP = "draftLifetime";

/** Whether or not tokenless auth is enabled by default (used in case of errors). */
private static final Boolean TOKENLESS_AUTH_ENABLED_DEFAULT = false;

Expand All @@ -63,6 +66,9 @@ public class PatientAccessConfigurationImpl extends AbstractNodeUtils implements
/** The number of days a token is valid for by default (used in case of errors). */
private static final int TOKEN_LIFETIME_DEFAULT = 0;

/** The number of days a patient's draft response is kept for by default (used in case of errors). */
private static final int DRAFT_LIFETIME_DEFAULT = -1;

@Reference(fieldOption = FieldOption.REPLACE, cardinality = ReferenceCardinality.OPTIONAL,
policyOption = ReferencePolicyOption.GREEDY)
private ThreadResourceResolverProvider rrp;
Expand Down Expand Up @@ -125,4 +131,16 @@ public int getAllowedPostVisitCompletionTime()
return TOKEN_LIFETIME_DEFAULT;
}
}

@Override
public int getDraftLifetime()
{
try
{
Property lifetime = getConfig(DRAFT_LIFETIME_PROP);
return lifetime == null ? DRAFT_LIFETIME_DEFAULT : (int) lifetime.getLong();
} catch (RepositoryException e) {
return DRAFT_LIFETIME_DEFAULT;
}
}
}

0 comments on commit 4b419db

Please sign in to comment.