Skip to content

Commit

Permalink
[Candidate_parameters] Consent status - Add NA option (aces#8732)
Browse files Browse the repository at this point in the history
Adding a N/A option for consent status. This is the expected behaviour:

- Updating the consent status from Not Applicable to Yes/No is allowed
- Updating the consent status from Yes/No to Not Applicable is NOT allowed
- If Not Applicable is selected, no dates are required

Removes CCNA override aces/CCNA#3822
  • Loading branch information
charlottesce authored and kongtiaowang committed Jan 30, 2024
1 parent be39a57 commit 6c03c89
Show file tree
Hide file tree
Showing 20 changed files with 227 additions and 21 deletions.
Empty file added CANCELED
Empty file.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ changes in the following format: PR #1234***
#### Bug Fixes
- Fix examiner site display (PR #8967)
- bvl_feedback updates in real-time (PR #8966)
- DoB and DoD format respected in candidate parameters (PR #9001)

## LORIS 25.0 (Release Date: ????-??-??)
### Core
Expand Down
4 changes: 2 additions & 2 deletions SQL/0000-00-00-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2278,7 +2278,7 @@ CREATE TABLE `consent` (
CREATE TABLE `candidate_consent_rel` (
`CandidateID` int(6) NOT NULL,
`ConsentID` integer unsigned NOT NULL,
`Status` enum('yes','no') DEFAULT NULL,
`Status` enum('yes','no', 'not_applicable') DEFAULT NULL,
`DateGiven` date DEFAULT NULL,
`DateWithdrawn` date DEFAULT NULL,
CONSTRAINT `PK_candidate_consent_rel` PRIMARY KEY (`CandidateID`,`ConsentID`),
Expand All @@ -2294,7 +2294,7 @@ CREATE TABLE `candidate_consent_history` (
`PSCID` varchar(255) NOT NULL,
`ConsentName` varchar(255) NOT NULL,
`ConsentLabel` varchar(255) NOT NULL,
`Status` enum('yes','no') DEFAULT NULL,
`Status` enum('yes','no', 'not_applicable') DEFAULT NULL,
`EntryStaff` varchar(255) DEFAULT NULL,
CONSTRAINT `PK_candidate_consent_history` PRIMARY KEY (`CandidateConsentHistoryID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Expand Down
2 changes: 2 additions & 0 deletions SQL/New_patches/2023-06-06-add_NA_to_consent_status.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE candidate_consent_rel MODIFY COLUMN `Status` enum('yes', 'no', 'not_applicable') DEFAULT NULL;
ALTER TABLE candidate_consent_history MODIFY COLUMN `Status` enum('yes', 'no', 'not_applicable') DEFAULT NULL;
Empty file added a
Empty file.
16 changes: 16 additions & 0 deletions modules/candidate_list/php/candidatelistrowprovisioner.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace LORIS\candidate_list;
*/
class CandidateListRowProvisioner extends \LORIS\Data\Provisioners\DBRowProvisioner
{
private string $dobFormat;
/**
* Create a CandidateListRowProvisioner, which gets rows for
* the candidate_list menu table.
Expand All @@ -43,6 +44,8 @@ class CandidateListRowProvisioner extends \LORIS\Data\Provisioners\DBRowProvisio
if ($config->getSetting("useEDC") === "true") {
$maybeEDC = ", DATE_FORMAT((c.EDC),'%Y-%m-%d') AS EDC";
}
$this->dobFormat = $config->getSetting("dobFormat");

parent::__construct(
"SELECT
c.PSCID,
Expand Down Expand Up @@ -98,6 +101,19 @@ class CandidateListRowProvisioner extends \LORIS\Data\Provisioners\DBRowProvisio
// one of the user's sites.
$cid = new \CenterID($row['RegistrationCenterID']);
$pid = new \ProjectID($row['RegistrationProjectID']);
if ($row['DoB'] !== null) {
$dob = new \DateTimeImmutable($row['DoB']);
switch ($this->dobFormat) {
case 'Ym':
$row['DoB'] = $dob->format('Y-m');
break;
case 'Ymd':
$row['DoB'] = $dob->format('Y-m-d');
break;
default:
throw new \Exception("Unhandled DoB format: $this->dobFormat");
}
}
unset($row['RegistrationCenterID']);
unset($row['RegistrationProjectID']);
return new CandidateListRow($row, $cid, $pid);
Expand Down
17 changes: 17 additions & 0 deletions modules/candidate_parameters/ajax/formHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -501,13 +501,30 @@ function editConsentStatusFields(\Database $db)
) { // Withdrawing from 'yes' status required consent date
// and withdrawal date
$validated = true;
} else if ($oldStatus === 'not_applicable' && !empty($date)
&& empty($withdrawal)
) { // Add N/A option
$validated = true;
} else {
http_response_code(400);
echo('Data failed validation. Resolve errors and try again.');
return;
}
}
break;
case 'not_applicable':
// If status is N/A, date is not required.
if (empty($date) && empty($withdrawal)
&& ($oldStatus !== 'yes' || $oldStatus !== 'no')
) {
$validated = true;
} else {
http_response_code(400);
echo('Answering not applicable to a consent type
does not require a date of consent.');
return;
}
break;
default:
// If status is empty, and date fields are also empty,
// validated is still false
Expand Down
47 changes: 39 additions & 8 deletions modules/candidate_parameters/ajax/getData.php
Original file line number Diff line number Diff line change
Expand Up @@ -524,10 +524,22 @@ function getDOBFields(): array
);
$pscid = $candidateData['PSCID'] ?? null;
$dob = $candidateData['DoB'] ?? null;
$result = [
'pscid' => $pscid,
'candID' => $candID->__toString(),
'dob' => $dob,

// Get DoB format
$factory = \NDB_Factory::singleton();
$config = $factory->config();

$dobFormat = $config->getSetting('dobFormat');

$dobProcessedFormat = implode("-", str_split($dobFormat, 1));
$dobDate = DateTime::createFromFormat('Y-m-d', $dob);
$formattedDate = $dobDate ? $dobDate->format($dobProcessedFormat) : null;

$result = [
'pscid' => $pscid,
'candID' => $candID->__toString(),
'dob' => $formattedDate,
'dobFormat' => $dobFormat,
];
return $result;
}
Expand All @@ -549,11 +561,30 @@ function getDODFields(): array
if ($candidateData === null) {
throw new \LorisException("Invalid candidate");
}

$factory = \NDB_Factory::singleton();
$config = $factory->config();

// Get formatted dod
$dodFormat = $config->getSetting('dodFormat');

$dodProcessedFormat = implode("-", str_split($dodFormat, 1));
$dodDate = DateTime::createFromFormat('Y-m-d', $candidateData['DoD']);
$dod = $dodDate ? $dodDate->format($dodProcessedFormat) : null;

// Get formatted dob
$dobFormat = $config->getSetting('dobFormat');

$dobProcessedFormat = implode("-", str_split($dobFormat, 1));
$dobDate = DateTime::createFromFormat('Y-m-d', $candidateData['DoB']);
$dob = $dobDate ? $dobDate->format($dobProcessedFormat) : null;

$result = [
'pscid' => $candidateData['PSCID'],
'candID' => $candID->__toString(),
'dod' => $candidateData['DoD'],
'dob' => $candidateData['DoB'],
'pscid' => $candidateData['PSCID'],
'candID' => $candID->__toString(),
'dod' => $dod,
'dob' => $dob,
'dodFormat' => $config->getSetting('dodFormat'),
];
return $result;
}
Expand Down
2 changes: 2 additions & 0 deletions modules/candidate_parameters/jsx/CandidateDOB.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class CandidateDOB extends Component {
return <Loader/>;
}

let dateFormat = this.state.data.dobFormat;
let disabled = true;
let updateButton = null;
if (loris.userHasPermission('candidate_dob_edit')) {
Expand Down Expand Up @@ -116,6 +117,7 @@ class CandidateDOB extends Component {
<DateElement
label='Date Of Birth:'
name='dob'
dateFormat={dateFormat}
value={this.state.formData.dob}
onUserInput={this.setFormData}
disabled={disabled}
Expand Down
2 changes: 2 additions & 0 deletions modules/candidate_parameters/jsx/CandidateDOD.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class CandidateDOD extends Component {
return <Loader/>;
}

let dateFormat = this.state.data.dodFormat;
let disabled = true;
let updateButton = null;
if (loris.userHasPermission('candidate_dod_edit')) {
Expand Down Expand Up @@ -114,6 +115,7 @@ class CandidateDOD extends Component {
<DateElement
label='Date Of Death:'
name='dod'
dateFormat={dateFormat}
value={this.state.formData.dod}
onUserInput={this.setFormData}
disabled={disabled}
Expand Down
24 changes: 18 additions & 6 deletions modules/candidate_parameters/jsx/ConsentStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ class ConsentStatus extends Component {
constructor(props) {
super(props);
this.state = {
consentOptions: {
yes: 'Yes',
no: 'No',
},
Data: [],
formData: {},
error: false,
Expand Down Expand Up @@ -68,6 +64,7 @@ class ConsentStatus extends Component {
let consents = data.consents;
for (let cStatus in consents) {
if (consents.hasOwnProperty(cStatus)) {
let cOptions = cStatus + '_options';
let cDate = cStatus + '_date';
let cDate2 = cStatus + '_date2';
let cWithdrawal = cStatus + '_withdrawal';
Expand All @@ -77,6 +74,20 @@ class ConsentStatus extends Component {
formData[cDate2] = data.consentDates[cStatus];
formData[cWithdrawal] = data.withdrawals[cStatus];
formData[cWithdrawal2] = data.withdrawals[cStatus];
if (data.consentStatuses[cStatus] === 'yes' ||
data.consentStatuses[cStatus] === 'no'
) {
formData[cOptions] = {
yes: 'Yes',
no: 'No',
};
} else {
formData[cOptions] = {
yes: 'Yes',
no: 'No',
not_applicable: 'Not applicable',
};
}
}
}
this.setState({
Expand Down Expand Up @@ -285,7 +296,7 @@ class ConsentStatus extends Component {
{dataEntry} - {user}
</b> updated for <i>{label}</i>:
<b> Status</b> to {' '}
{this.state.consentOptions[consentStatus]}
{consentStatus}
{dateHistory}
{withdrawalHistory}
</p>
Expand Down Expand Up @@ -354,6 +365,7 @@ class ConsentStatus extends Component {

// Set up elements
const label = this.state.Data.consents[consentName];
const consentOptions = consentName + '_options';
const statusLabel = 'Response';
const consentDate = consentName + '_date';
const consentDate2 = consentName + '_date2';
Expand All @@ -373,7 +385,7 @@ class ConsentStatus extends Component {
<SelectElement
label={statusLabel}
name={consentName}
options={this.state.consentOptions}
options={this.state.formData[consentOptions]}
value={this.state.formData[consentName]}
onUserInput={this.setFormData}
disabled={disabled}
Expand Down
4 changes: 4 additions & 0 deletions modules/candidate_parameters/test/TestPlan.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
5. Try updating the consent information. Do not fill out all required fields. Ensure that an error appears when you try to save.
6. For each of the date fields, try entering only one part of the date (eg. the year). Make sure there is an error when you try to save.
7. Enter the following combinations:
* Consent to Study = Not Applicable
* No error
* Make sure they update properly in the front-end and backend
* Consent to Study = No (error: must enter Date of 'No' Consent)
* Consent to Study = Yes (error: must enter Date of 'Yes' Consent)
* Consent to Study = Yes/No; Date of Consent = random date
Expand All @@ -66,6 +69,7 @@
* Consent to Study = No (changing/updating an already existing 'Yes' consent); Date (Withdrawal) of Consent = valid random date; Confirmation (Withdrawal) Date of Consent = same random date
* No error
* Make sure they update properly in the front-end and backend
* Check that Consent to Study = Not Applicable is not an option when changing/updating an already existing 'Yes' or 'No' consent

### Date of Birth Tab
1. Check that date of birth can only be amended if user has candidate_dob_edit permission.
Expand Down
1 change: 1 addition & 0 deletions modules/instrument_list/php/instrument_list.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class Instrument_List extends \NDB_Menu_Filter
// Don't call parent setup, we don't want it to try to run any SQL

// set template data
$this->tpl_data['candidate'] = $this->candidate;
$this->tpl_data['candID'] = $this->candidate->getCandID();
$this->tpl_data['sessionID'] = $this->timePoint->getSessionID();
$this->tpl_data['stage'] = \Utility::getStageUsingCandID(
Expand Down
5 changes: 3 additions & 2 deletions modules/instrument_list/templates/menu_instrument_list.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
<!-- column headings -->
<thead>
<tr class="info">
{assign var="DoB" value=$candidate->getDisplayDoB()}
<th>
DOB
{$DoB['label']}
</th>
{if $display.EDC!=""}
<th>
Expand Down Expand Up @@ -57,7 +58,7 @@
<tbody>
<tr>
<td>
{$display.DoB}
{$DoB['value']}
</td>
{if $display.EDC!=""}
<td>
Expand Down
20 changes: 20 additions & 0 deletions modules/my_preferences/php/my_preferences.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,25 @@ class My_Preferences extends \NDB_Form
$this->tpl_data['notification_rows'] = $notification_rows;
//------------------------------------------------------------

$widgets = [];
$modules = $this->loris->getActiveModules();
foreach ($modules as $module) {
if ($module->hasAccess($user)) {
$mwidgets = $module->getWidgets(
'userpreference',
$user,
[],
);
foreach ($mwidgets as $widget) {
if (!($widget instanceof UserPreferenceWidget)) {
continue;
}
$widgets[] = $widget;
}
}
}
$this->tpl_data['module_userpreference_widgets'] = $widgets;

// unique key and password rules
$this->form->addFormRule([&$this, '_validateMyPreferences']);
}
Expand Down Expand Up @@ -468,6 +487,7 @@ class My_Preferences extends \NDB_Form
[
$baseurl . '/js/passwordVisibility.js',
$baseurl . '/my_preferences/js/my_preferences_helper.js',
$baseurl . '/js/components/CSSGrid.js',
]
);
}
Expand Down
42 changes: 42 additions & 0 deletions modules/my_preferences/php/userpreferencewidget.class.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types=1);
namespace LORIS\my_preferences;

/**
* A \LORIS\candidate_profile\UserPreferenceWidget is a type of \LORIS\GUI\Widget
* used by the my preference page to register extra preference types.
*
* All UserPreferenceWidgets consist of React components which are loaded on the fly.
* The React component can have arbitrary props sent to it from LORIS.
*
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
*/
class UserPreferenceWidget implements \LORIS\GUI\Widget
{
/**
* Construct a dashboard widget with the specified properties.
*
* @param string $title The title of the card to display.
* @param string $jsurl The URL containing the React component.
* @param string $componentname The React component name for this widget.
* @param array $props Additional React props to pass to the React
* component.
*/
public function __construct(
public string $title,
public string $jsurl,
public string $componentname,
public array $props,
) {
}

/**
* Renders the widget within a preference panel and implements
* the \LORIS\GUI\Widget interface.
*
* @return string the URL to the javascript which contains the React component
*/
public function __toString()
{
return $this->jsurl;
}
}
Loading

0 comments on commit 6c03c89

Please # to comment.