Skip to content

Conversation

@zaliqarosli
Copy link
Contributor

@zaliqarosli zaliqarosli commented Feb 20, 2018

This PR transforms the current implementation of consent into a normalized structure. Current behaviour on the front-end shouldn't be affected.

Changes targeted:

  • create normalized tables
  • implement SQL modelling standard
  • move data into new tables / update calls to DB
  • move config settings from config.xml to DB

Discussions required:

  • on options for statuses of consent (to enum or not to enum)
  • on default status and coalescing when querying consent in DQT

Moving forward:

The ajax scripts are a little hacky in order to be compatible with changes in the back-end while keeping the front-end untouched. The next step would be to clean up how consent is used in the front-end and fix up the ajax scripts in parallel.

See also: #3263, #3315, #3348

How to test:

  1. Test front-end Candidate Parameters->Consent Status (pre-PR)
  2. Source SQL patch - check tables and settings are created
  3. Run Import_Data_Normalise_Consent.php - check new tables are populated when successful
  4. Make backup of participant_status and consent_info_history and drop deprecated columns/tables in DB as instructed by above script
  5. Test front-end on PR branch - there should be no changes in functionality/display
  6. Test consent status tab only shows when ‘Use Consent’ in Config module enabled
  7. Run CouchDB_Import_Demographics.php
  8. Query demographics instrument in DQT - check results are what you expect from DB

@ridz1208
Copy link
Collaborator

@llevitis just putting #3315 here so it doesn't get forgotten

@zaliqarosli zaliqarosli removed the State: Needs work PR awaiting additional work by the author to proceed label Feb 21, 2018
foreach ($consents as $key=>$consent) {
// Do consent columns exist in old table?
$consentName = $consent['name'];
$columnQuery = 'SHOW COLUMNS FROM participant_status LIKE "' . $consentName . '"';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ridz1208 at the moment, only the columns with status information is being checked for existence in participant_status. do you think this is enough or should we also be checking for the existence of the _date and _withdrawal columns too?

Copy link
Collaborator

Choose a reason for hiding this comment

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

the check shouldbe done on all columns since you dont want to assume that they exist... just to be sure.

// Do consent columns exist in old table?
$consentName = $consent['name'];
$columnQuery = 'SHOW COLUMNS FROM participant_status LIKE "' . $consentName . '"';
$columnExists = $db->pselect($columnQuery, array());
Copy link
Collaborator

Choose a reason for hiding this comment

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

You might wan to use the database column exist function !!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ahh!! okay

CREATE TABLE `candidate_consent_type_history` (
`CandidateConsentHistoryID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`PSCID` varchar(255) NOT NULL,
`ConsentName` varchar(255) NOT NULL,
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be a foreign key on consent_type. right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@xlecours similar to the old consent_info_history table, this table should only be a log of data submitted on the front-end, unaffected by changes in data of the other consent tables, but with more meaningful fields for users reading the history on the front-end

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok. The primary goal of this PR is to remove the consent from config.xml and not to redesign the way we log events.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@xlecours a secondary goal, which I think is just as important, is to implement the new SQL modelling standard, hence the renovation of the existing consent history table

`CandidateID` int(6) NOT NULL,
`ConsentTypeID` int(2) NOT NULL,
`Status` enum('yes', 'no', 'not_answered') NOT NULL,
`DateGiven` date DEFAULT NULL,
Copy link
Contributor

Choose a reason for hiding this comment

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

as per https://github.com/aces/Loris/blob/19.0-dev/docs/SQLModelingStandard.md (Which should be propagated to minor),
ENUM are not permitted.

Could use this occasion to add a table "option" with "TableName", "AttributeName", "Value" attributes. This would create the table to replace all ENUM within the DB. Don't forget the (artificial) primary key.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Im not sure about this design... I actually told zaliqa to keep it as an enum until we discuss how to tackle it.

Sever places will use the yes/no options in loris and im not sure if those options should also include "not answered" or other options.

Id like to bring this up at the loris meeting so it can be discussed with everyone

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yepp, let's put it through as enum for now until a standard for all options in the database is decided on

Copy link
Contributor

Choose a reason for hiding this comment

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

Excellent idea, we could create a class for that so it be easy on projects to use the feature. I should work on that after I'm finish with visit

Copy link
Collaborator

Choose a reason for hiding this comment

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

the reason for yes/no enums in LORIS is that MySQL mutilates "boolean" to tinyint(1) and changes true/false to 1/0.

Maybe we should document that and update the SQL standards? (not answered doesn't make sense though)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah i agree, i wanted to talk about the not answered option at tomorrow's meeting

CONSTRAINT `PK_candidate_consent_type_rel` PRIMARY KEY (`CandidateID`,`ConsentTypeID`),
CONSTRAINT `FK_candidate_consent_type_rel_CandidateID` FOREIGN KEY (`CandidateID`) REFERENCES `candidate` (`CandID`),
CONSTRAINT `FK_candidate_consent_type_rel_ConsentTypeID` FOREIGN KEY (`ConsentTypeID`) REFERENCES `consent_type` (`ConsentTypeID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy link
Contributor

Choose a reason for hiding this comment

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

FK should containt "on cascade" and "on delete" clause

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PapillonMcGill did you mean "on update" and "on delete"? i dont think there's an "on cascade"

Copy link
Collaborator

Choose a reason for hiding this comment

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

@zaliqarosli i would assume so, and on that note, you should update the delete_candidate php script to delete the fields from the tables you added

Copy link
Contributor

Choose a reason for hiding this comment

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

@zaliqarosli yes, on delete and on update. Look like I write faster than I think. Must be in need of vacation ;-)

Copy link
Contributor Author

@zaliqarosli zaliqarosli Feb 22, 2018

Choose a reason for hiding this comment

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

@ridz1208 should we maybe fix #3348 first or update it to include consent tables once this PR goes through? i feel like changes to delete_candidate script should be separate from this consent PR. thoughts?

`DateWithdrawn` date DEFAULT NULL,
`EntryStaff` varchar(255) DEFAULT NULL,
`EntryDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `PK_candidate_consent_type_history` PRIMARY KEY (`CandidateConsentHistoryID`)
Copy link
Contributor

@PapillonMcGill PapillonMcGill Feb 22, 2018

Choose a reason for hiding this comment

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

varchar() should be after date and timestamp field.
no ENUM

Copy link
Contributor Author

@zaliqarosli zaliqarosli Feb 22, 2018

Choose a reason for hiding this comment

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

could you explain why the varchar comes after the date and timestamp?

Copy link
Contributor

Choose a reason for hiding this comment

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

@zaliqarosli For efficiency, if DB engine want to read a field placed after, it as to traverse the varchar() until the end. Can't jump directly to the required field start as the offset from row start in not fixed.
I'm preparing a presentation on that for an ADM.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ahh okay i think i know what you mean. looking forward to the adm!

static function getConsentList()
{
$DB =& Database::singleton();

Copy link
Contributor

Choose a reason for hiding this comment

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

should be
$factory = NDB_Factory::singleton();
$db = $factory->database();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PapillonMcGill could you explain this to me please? I've seen both used and not sure what the difference is

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PapillonMcGill i do see that the other functions in Utility follow the way you mentioned

Copy link
Contributor

Choose a reason for hiding this comment

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

@zaliqarosli For more info, should ask @xlecours. I saw that he made a PR #3507 for that. I just wanted to do all uniformly.
Suspect it as to do with uniformity to simplify testing

$fieldsInQuery = "SELECT c.DoB,
c.CandID,
c.CandID,
c.PSCID,
Copy link
Contributor

Choose a reason for hiding this comment

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

extra space!

c.CandID,
c.DoB,
c.CandID,
c.PSCID,
Copy link
Contributor

Choose a reason for hiding this comment

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

extra space at end of line 147
missing space at line 148

Copy link
Contributor Author

@zaliqarosli zaliqarosli Feb 22, 2018

Choose a reason for hiding this comment

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

@PapillonMcGill thanks for your review! will make these changes :)

`EntryDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`EntryStaff` varchar(255) DEFAULT NULL,
CONSTRAINT `PK_candidate_consent_type_history` PRIMARY KEY (`CandidateConsentHistoryID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy link
Contributor

Choose a reason for hiding this comment

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

still some varchar() before date and timestamp,
Nullable should be after not null

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i think i need more coffee


CREATE TABLE `candidate_consent_type_history` (
`CandidateConsentHistoryID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`EntryDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PapillonMcGill did i get it right this time? lol

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup!

$factory = NDB_Factory::singleton();
$db = $factory->database();

$query = "SELECT *
Copy link
Contributor

Choose a reason for hiding this comment

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

Please specify the columns.

  1. They are no all used
  2. If some get changed in the schema, it is not obvious that it was used here?
  3. If this need to join other tables, it will gather all those new table's columns

Copy link
Collaborator

Choose a reason for hiding this comment

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

agree

{
$DB =& Database::singleton();

$query = "SELECT * FROM consent_type";
Copy link
Contributor

Choose a reason for hiding this comment

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

Please specify the columns.

} else {
// Check for zero dates
$psData = $db->pselect(
'SELECT * FROM participant_status WHERE ' . $consentName . ' IS NOT NULL OR ' . $consentName . ' != ""',
Copy link
Contributor

Choose a reason for hiding this comment

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

Please specify the columns.


// Get all data where the consent status has a value
$psData = $db->pselect(
'SELECT * FROM participant_status WHERE '
Copy link
Contributor

Choose a reason for hiding this comment

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

Please specify the columns.


// Select consent history and import into new history table
$consentHistory = $db->pselect(
'SELECT * FROM consent_info_history',
Copy link
Contributor

Choose a reason for hiding this comment

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

Please specify the columns.

Copy link
Collaborator

Choose a reason for hiding this comment

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

i already said I AGREE !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@xlecours thank you for your input!! i made the changes for all the other queries but for this one, the field names in the old history table are dynamically set by the consent types. I'll specify the predicted column names here but it'll be in a concatenated query


array_push($commentHistory, $unformattedComments);
$historyData = $db->pselect(
"SELECT *
Copy link
Contributor

Choose a reason for hiding this comment

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

Please specify the columns.

// Select consent history and import into new history table
$historyFieldsQuery = 'SELECT CandID, entry_staff, data_entry_date';
foreach ($consentType as $consentName => $consentLabel) {
$historyFieldsQuery .= ', ' . $consentName . ', ' . $consentName . '_date, ' . $consentName . '_withdrawal';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@xlecours let me know what you think

Copy link
Collaborator

@driusan driusan left a comment

Choose a reason for hiding this comment

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

some preliminary review, but this is too big to review in one sitting. Can you try and split this up into multiple smaller self-contained PRs?

The schema+import script would make a good start for a first one without touching any other code.

CREATE TABLE `candidate_consent_type_rel` (
`CandidateID` int(6) NOT NULL,
`ConsentTypeID` int(2) NOT NULL,
`Status` enum('yes', 'no', 'not_answered') NOT NULL,
Copy link
Collaborator

Choose a reason for hiding this comment

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

No enums in the core tables is the new rule. yes, no, not_answered NOT NULL also doesn't really make sense. It should just be yes, no, NULLABLE, and (at that point it's just a boolean, not an enum, which makes more sense)

`PSCID` varchar(255) NOT NULL,
`ConsentName` varchar(255) NOT NULL,
`ConsentLabel` varchar(255) NOT NULL,
`Status` enum('yes','no','not_answered') NOT NULL,
Copy link
Collaborator

Choose a reason for hiding this comment

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

same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

to enum or not to enum appears to be a point of contention.. to be determined hopefully tomorrow

`DateGiven` date DEFAULT NULL,
`DateWithdrawn` date DEFAULT NULL,
`EntryStaff` varchar(255) DEFAULT NULL,
`EntryDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
Copy link
Collaborator

Choose a reason for hiding this comment

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

The history should never be updated, why does this have an ON UPDATE clause?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as suggested by @PapillonMcGill and the new SQL modelling standard

INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'allowPrenatalTimepoints', 'Determines whether creation of timepoints prior to Date of Birth is allowed', 1, 0, 'boolean', ID, 'Allow Prenatal Timepoints', 21 FROM ConfigSettings WHERE Name="study";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'ImagingUploaderAutoLaunch', "Allows running the ImagingUpload pre-processing scripts", 1, 0, 'boolean', ID, 'ImagingUploader Auto Launch',22 FROM ConfigSettings WHERE Name="study";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'citation_policy', 'Citation Policy for Acknowledgements module', 1, 0, 'textarea', ID, 'Citation Policy', 23 FROM ConfigSettings WHERE Name="study";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'CSPAdditionalHeaders', 'Extensions to the Content-security policy allow only for self-hosted content', 1, 0, 'text', ID, 'Content-Security Extensions', 24 FROM ConfigSettings WHERE Name="study";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is all this changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ridz1208 wanted to update the order of the statements

$consentDetails = Utility::getConsentList();

// Get list of consents for candidate
$candidateConsent = Candidate::getConsent($candID);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This isn't (and shouldn't be) defined as a static function below

Copy link
Contributor Author

Choose a reason for hiding this comment

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

what do you mean? could you be more explicit please?

* @return array List of consents and their associated values for this candidate.
* The keys of the arrays are the IDs of the consents
*/
function getConsent($candID)
Copy link
Collaborator

Choose a reason for hiding this comment

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

It doesn't make sense for any functions in the Candidate class to take a candID as a parameter. It's the candidate class, it already knows what it's candID is.

This should probably have a consent type parameter instead though, shouldn't it?

$factory = NDB_Factory::singleton();
$db = $factory->database();

$query = "SELECT *
Copy link
Collaborator

Choose a reason for hiding this comment

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

Select * should be avoided in favour of explicit column names, otherwise the code is going to be brittle with schema changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh yes, @xlecours pointed that out for other statements, i missed these ones

{
$DB =& Database::singleton();

$query = "SELECT * FROM consent_type";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment, avoid SELECT *

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@driusan cool, thanks for all your comments. i'll make the changes and work on sending out smaller, more contained PRs

foreach ($result as $row) {
$list[$row["ConsentTypeID"]] = $row;
unset($list[$row["ConsentTypeID"]]["ConsentTypeID"]);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Didn't @ridz1208 recently add a variant of pselect to make this easier?

Copy link
Contributor Author

@zaliqarosli zaliqarosli Feb 26, 2018

Choose a reason for hiding this comment

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

@ridz1208 is he talking about pselectCol? i forgot what the above foreach statement does

Copy link
Collaborator

Choose a reason for hiding this comment

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

i think pselectcolwithindex something

Copy link
Collaborator

Choose a reason for hiding this comment

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

Here's a reference to the PR #3428 - The function is called 'pselectWithIndexKey()'

`CandidateID` int(6) NOT NULL,
`ConsentTypeID` int(2) NOT NULL,
`Status` enum('yes', 'no', 'not_answered') NOT NULL,
`DateGiven` date DEFAULT NULL,
Copy link
Collaborator

Choose a reason for hiding this comment

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

the reason for yes/no enums in LORIS is that MySQL mutilates "boolean" to tinyint(1) and changes true/false to 1/0.

Maybe we should document that and update the SQL standards? (not answered doesn't make sense though)

@zaliqarosli zaliqarosli added State: Needs work PR awaiting additional work by the author to proceed State: Blocked PR or issue awaiting an external event such as the merge or another PR to proceed labels Feb 26, 2018
`ConsentTypeID` int(2) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NOT NULL,
`Label` varchar(255) NOT NULL,
CONSTRAINT `PK_consent_type` PRIMARY KEY (`ConsentTypeID`),
Copy link
Contributor

Choose a reason for hiding this comment

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

Do these 2 varchar() field need 255 chars?
What is the practical / forecast max length for the name and label?
Would 15 or 20 for Name and 20 or 30 for Label be sufficient?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

New PR #3532 if you could redirect reviews there

c.DoB,
c.CandID,
c.DoB,
c.CandID,
Copy link
Contributor

Choose a reason for hiding this comment

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

extra space at end of line

@zaliqarosli zaliqarosli closed this Mar 5, 2018
@zaliqarosli zaliqarosli deleted the normalise_consent branch April 24, 2019 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Category: Cleanup PR or issue introducing/requiring at least one clean-up operation Language: SQL PR or issue that update SQL code State: Blocked PR or issue awaiting an external event such as the merge or another PR to proceed State: Discussion required PR or issue that requires the resolution of a discussion with the relevant parties to proceed State: Needs work PR awaiting additional work by the author to proceed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants