diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..5220547 --- /dev/null +++ b/config.xml @@ -0,0 +1,179 @@ + + + + lorisDB.xml + + + 1 + + + + /opt/minc/ + + + + + + + + + random + + + + + + + + random + + + + + + + + + candidate_comment + ProbandDoB + MRI_Done + + + + + sequence + /^[A-Z0-9]{2}$/i + 2 + V%value% + + V1 + V2 + V3 + + + + + sequence + /^[A-Z0-9]{2}$/i + 2 + V%value% + + V1 + V2 + V3 + + + + + sequence + /^[A-Z0-9]{2}$/i + 2 + V%value% + + V3 + V4 + V5 + V6 + + + + + sequence + /^[A-Z0-9]{2}$/i + 2 + V%value% + + V3 + V4 + V5 + V6 + + + + + + bmi + 0.5 + BMI + + + + + + + 1 + + + 1 + 2 + 3 + + + + BMI + Radiology Review + + + + 0 + + + + true + + study_consent + + + + raisin_consent + + + + bread_consent + + + + + + + + + + 0 + + + + + + false + + + + sampleInstrument + + sampleInstrumentPermissionName + + + sampleInstrument2 + sampleInstrument2PermissionName + + + + + diff --git a/data/training/pdf/bmi.pdf b/data/training/pdf/bmi.pdf new file mode 100644 index 0000000..9d495e6 Binary files /dev/null and b/data/training/pdf/bmi.pdf differ diff --git a/instruments/NDB_BVL_Instrument_TEMPLATE.class.inc b/instruments/NDB_BVL_Instrument_TEMPLATE.class.inc new file mode 100644 index 0000000..9cc9e39 --- /dev/null +++ b/instruments/NDB_BVL_Instrument_TEMPLATE.class.inc @@ -0,0 +1,130 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/loris/ + */ + +/** + * Creates the form elements for the Boston_Diagnostic_Aphasia_Exam instrument + * + * @category Instrument + * @package TEMPLATE + * @author Stella Lee + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/loris/ + */ +class NDB_BVL_Instrument_TEST_NAME extends NDB_BVL_Instrument +{ + + /** + * Sets up basic data such as the HTML_Quickform object, database references + * + * @param string $commentID the CommentID identifying the data to load + * @param string $page if a multipage form, the page to show + * + * @return void + * @access public + */ + function setup($commentID, $page) + { + $this->formType ="XIN"; + $this->form = new HTML_Quickform('test_form'); + $this->page = $page; // page label (number or + // string - used by + // user-defined child classes) + + // set the object properties + // Corresponds to the Test_name column in test_names table + $this->testName = ""; + // name of database table corresponding to instrument + $this->table = ''; + // data keyed by commentID + $this->commentID = $commentID; + + //The array of dates/timestamps to convert to database dates/timestamps + //Any HTML_Quickform date elements must be listed here + $this->dateTimeFields =array("Date_taken"); + + //The array of selects with multiple answers allowed + //Any HTML_Quickform multiple selects must be listed here + $this->_selectMultipleElements = array(); + + // required fields for data entry completion status + $this->_requiredElements = array( + 'Examiner', + '', + ); + + // setup the form + $this->_setupForm(); + + } + + /** + * Builds the HTML_Quickform object into a paged form + * + * @return void + * @access private + */ + function _setupForm() + { + if (preg_match("/(_page[0-9]+)/", $this->page, $matches)) { + $this->_page($matches[1]); + } else { + $this->_main(); + } + // Defines the call back function for HTML Quickform to use in validation + $this->form->addFormRule(array(&$this, 'XINValidate')); + } + + /** + * Generates the main page of the form. + * + * @return void + * @access private + */ + function _main() + { + // display test name + $this->addHeader(""); + + // automatically adds examiner & date of administration + $this->_addMetadataFields(); + + // If the instrument is not paged, + // remove the switch from the _setupForm method + // and add all the form Elements in this function + } + + /** + * Page 1 + * + * @return void + **/ + function _page1() + { + //add form Elements here as needed. + //continue onto further pages, if needed. + } + + /** + * Page 2 + * + * @return void + **/ + function _page2() + { + //add form Elements here as needed. + //continue onto further pages, if needed. + } + +} +?> diff --git a/instruments/NDB_BVL_Instrument_medical_history.class.inc b/instruments/NDB_BVL_Instrument_medical_history.class.inc new file mode 100644 index 0000000..90e8c70 --- /dev/null +++ b/instruments/NDB_BVL_Instrument_medical_history.class.inc @@ -0,0 +1,413 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/loris/ + */ + +/** + * Creates the form elements for the Medical History Instrument + * + * @category Instrument + * @package TEMPLATE + * @author Stella Lee + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/loris/ + */ + +class NDB_BVL_Instrument_medical_history extends NDB_BVL_Instrument +{ + + var $ValidityEnabled = false; + var $ValidityRequired = false; + + /** + * Sets up basic data, such as the HTML_Quickform object, and so on. + * + * @param string $commentID the CommentID identifying the data to load + * @param string $page if a multipage form, the page to show + * + * @return void + * @access public + */ + function setup($commentID, $page) + { + $this->formType ='XIN'; + $this->form = new LorisForm('test_form'); + $this->page = $page; // page label + + // set the object properties + $this->testName = 'medical_history'; // test_names.Test_name + $this->table = 'medical_history'; // database table + $this->commentID = $commentID; // data keyed by commentID + + //The array of dates/timestamps to convert to database dates/timestamps + //Any HTML_Quickform date elements must be listed here + $this->dateTimeFields = array("Date_taken"); + + //The array of selects with multiple answers allowed + //Any HTML_Quickform multiple selects must be listed here + $this->_selectMultipleElements = array('current_concussion_symptoms'); + + // required fields for data entry completion status + $this->_requiredElements = array( + 'Examiner', + 'arthritis', + 'hypertension', + 'concussion_or_head_trauma', + ); + + $this->_doubleDataEntryDiffIgnoreColumns = array( + 'CommentID', + 'UserID', + 'Testdate', + 'Window_Difference', + 'Candidate_Age', + 'Data_entry_completion_status', + 'pulmonary_issues_specific', + 'concussion_1_description', + 'concussion_2_description', + 'concussion_3_description', + ); + + // setup the form + $this->_setupForm(); + } + + /** + * Method to build the HTML_Quickform object into a paged form + * + * @return void + * @access private + */ + function _setupForm() + { + if (preg_match("/medical_history(_page[0-9]+)/", $this->page, $matches)) { + call_user_func(array($this, $matches[1])); + } else { + $this->_main(); + } + // Defines the call back function for HTML Quickform to use in validation + $this->form->addFormRule(array(&$this, 'XINValidate')); + + //Defines the call back function for HTML Quickform to use + $this->form->addFormRule(array(&$this, 'XINValidate')); + } + + /** + * Generates the main page of the form. + * + * @return void + * @access private + */ + function _main() + { + // display test name + $this->addHeader("Medical History"); + + // automatically adds examiner & date of administration + $this->_addMetadataFields(); + } + + /** + * Arthritis/Respiratory + * + * @return void + */ + function _page1() + { + + // Header + $this->addHeader("A. Arthritis"); + + // Questions + + $yesNoOptions = array( + null => "", + "yes" => "Yes", + "no" => "No", + ); + + // Question 1 + + $this->addSelect( + "arthritis", + "1. Do you have any type of arthritis?", + $yesNoOptions + ); + + $this->addNumericElement( + "arthritis_age", + "$this->indent $this->indent Age:" + ); + + $this->XINRegisterRule( + "arthritis_age", + array("arthritis{@}=={@}yes"), + "Required." + ); + + // Header + $this->addHeader("B. Respiratory"); + + // Questions + + $yesNoOptions = array( + null => "", + "yes" => "Yes", + "no" => "No", + ); + + // Question 2 + + $this->addSelect( + "pulmonary_issues", + "2. Do you currently have, or have you ever had any pulmonary issues?", + $yesNoOptions + ); + + $this->addBasicText( + "pulmonary_issues_specific", + "$this->indent $this->indent If YES, please specify:" + ); + + $this->XINRegisterRule( + "pulmonary_issues_specific", + array("pulmonary_issues{@}=={@}yes"), + "Required." + ); + + } + + /** + * Cardiac/Cardiovascular + * + * @return void + */ + function _page2() + { + + // Header + $this->addHeader("C. Cardiac/Cardiovascular"); + + // Questions + + $yesNoOptions = array( + null => "", + "yes" => "Yes", + "no" => "No", + ); + + // Question 7 + + $this->addSelect( + "hypertension", + "7. Do you have hypertension?", + $yesNoOptions + ); + + $this->addSelect( + "hypertension_while_pregnant", + "$this->indent $this->indent" . + "If YES + FEMALE, were you pregnant " . + "when you were diagnosed with hypertension?", + $yesNoOptions + ); + + $this->XINRegisterRule( + "hypertension_while_pregnant", + array("hypertension_while_pregnant{@}=={@}NEVER_REQUIRED"), + "Required." + ); + + $this->form->addFormRule(array(&$this, 'areFemaleQuestionsRequired')); + + $this->addNumericElement( + "hypertension_while_pregnant_age", + "$this->indent $this->indent $this->indent $this->indent Age:" + ); + + $this->XINRegisterRule( + "hypertension_while_pregnant_age", + array("hypertension_while_pregnant{@}=={@}yes"), + "Required." + ); + + } + + /** + * Neurological + * + * @return void + */ + function _page3() + { + + // Header + $this->addHeader("D. Neurological"); + + // Questions + + $yesNoOptions = array( + null => "", + "yes" => "Yes", + "no" => "No", + ); + + // Question 21 + + $this->addSelect( + "concussion_or_head_trauma", + "21. Have you ever had a concussion?", + $yesNoOptions + ); + + $this->addLabel( + "$this->indent" . + "If YES, please fill out the table below, indicating:\n" . + "a) How many concussions have you experienced in your lifetime?\n" . + "b) For each incident, were you hospitalized?\n" . + "c) For each incident, how old were you?" + ); + + // Concussion table headers + + $group[] =& $this->form->createElement( + "static", + null, + null, + "Incident" + ); + + $group[] =& $this->form->createElement( + "static", + null, + null, + "Hospitalized?" + ); + + $group[] =& $this->form->createElement( + "static", + null, + null, + "Age" + ); + + $this->form->addGroup( + $group, + "concussion_titles", + null, + $this->_GUIDelimiter, + false + ); + + unset($group); + + for ($i = 1; $i < 4; $i++) { + + $group[] =& $this->createText( + "concussion_" . $i . "_description", + "Incident" + ); + + $this->XINRegisterRule( + "concussion_" . $i . "_description", + array("concussion_" . $i . "_description{@}=={@}NEVER_REQUIRED"), + "Required." + ); + + $group[] =& $this->createSelect( + "concussion_" . $i . "_hospitalized", + "Hospitalized?", + $yesNoOptions + ); + + $this->XINRegisterRule( + "concussion_" . $i . "_hospitalized", + array("concussion_" . $i . "_hospitalized{@}=={@}NEVER_REQUIRED"), + "Required." + ); + + $group[] =& $this->createText( + "concussion_" . $i . "_age", + "Age" + ); + + $this->XINRegisterRule( + "concussion_" . $i . "_age", + array("concussion_" . $i . "_age{@}=={@}NEVER_REQUIRED"), + "Required." + ); + + $this->form->addGroup( + $group, + "concussion_table_" . $i, + null, + $this->_GUIDelimiter, + false + ); + + unset($group); + } + + $concussionOptions = array( + null => "", + "headaches" => "Headaches", + "dizziness" => "Dizziness", + "memory_problems" => "Memory problems", + ); + + $this->addSelect( + "current_concussion_symptoms", + "$this->indent" . + "d) Are you currently experiencing any problems " . + "that you think might be related to a concussion?", + $concussionOptions, + array('multiple') + ); + + } + + /** + * Determines whether the question specific to female participants are required. + * + * @param array $values Array containing all the form values + * + * @return array + */ + function areFemaleQuestionsRequired($values) + { + $errors = array(); + $candID = isset($_REQUEST['candID']) ? $_REQUEST['candID'] : ''; + if ($values['hypertension'] == 'yes' && $this->isCandidateFemale($candID)) { + $errors['hypertension_while_pregnant'] = 'Required'; + } + return $errors; + } + + /** + * Given the CandID, determines if the candidate is female + * + * @param string $candID CandID + * + * @return boolean + */ + function isCandidateFemale($candID) + { + $DB = Database::singleton(); + $gender = $DB->pselectOne( + "SELECT Gender from candidate WHERE CandID=:candID", + array('candID' => $candID) + ); + if ($gender == 'female') { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/instruments/NDB_BVL_Instrument_mri_parameter_form.class.inc b/instruments/NDB_BVL_Instrument_mri_parameter_form.class.inc new file mode 100644 index 0000000..8e27072 --- /dev/null +++ b/instruments/NDB_BVL_Instrument_mri_parameter_form.class.inc @@ -0,0 +1,524 @@ +formType="XIN"; + $this->form = new LorisForm('test_form'); + $this->page = $page; // page label (number or + // string - used by + // user-defined child classes) + + // set the object properties + $this->testName = "mri_parameter_form"; // test_names.Test_name + $this->table = 'mri_parameter_form'; // name of table containing + + // data keyed by commentID + $this->commentID = $commentID; + + //The array of dates/timestamps to convert to database dates/timestamps + //Any HTML_Quickform date elements must be listed here + $this->dateTimeFields=array("Date_taken", "wait_time", "total_duration"); + + $this->_requiredElements = array('Date_taken', 'Examiner'); //, 'CaregiverReport'); + + $this->_selectMultipleElements = array("gre_fieldmap"); + $config =& NDB_Config::singleton(); + $this->dateOptions = array( + 'language' => 'en', + 'format' => 'YMd', + 'minYear' => $config->getSetting('startYear'), + 'maxYear' => $config->getSetting('endYear'), + 'addEmptyOption' => true, + 'emptyOptionValue' => null + ); + + $db =& Database::singleton(); + + $query = "SELECT * FROM ".$this->table." WHERE CommentID=:cID"; + $record = $db->pselectRow( + $query, + array('cID' => $this->getCommentID()) + ); + + if (!is_array($record)) { + $record = array(); + } + + for($i=0; $i < 25; $i++){ + if(isset($record["vector{$i}_x"])) { + $vector_x = number_format($record["vector{$i}_x"], 5); + } else { + $vector_x = 'unknown'; + } + + if(isset($record["vector{$i}_y"])) { + $vector_y = number_format($record["vector{$i}_y"], 5); + } else { + $vector_y = 'unknown'; + } + + if(isset($record["vector{$i}_z"])) { + $vector_z = number_format($record["vector{$i}_z"], 5); + } else { + $vector_z = 'unknown'; + } + + + $this->localDefaults["vector{$i}"] = "({$vector_x}, {$vector_y}, {$vector_z})"; + } + + /* + * Array of column names to be ignored by the double data entry conflict detector. + */ + $this->_doubleDataEntryDiffIgnoreColumns = array('CommentID', 'UserID', 'Testdate', 'Window_Difference', 'Candidate_Age', + 'dti_Comments', + 'dti_Comments_status', + 'DTI65Dir_Comments', + 'DTI65Dir_Comments_status', + 't1_Comments', + 't1_Comments_status', + 't2_Comments', + 't2_Comments_status', + 'fMRI_Comments', + 'fMRI_Comments_status', + 'Spectroscopy_Comments', + 'Spectroscopy_Comments_status', + 'total_duration_status', + 'wait_time_status', + 'Data_entry_completion_status' + ); + + // setup the form + $this->_setupForm(); + } + + /** + * method to build the HTML_Quickform object into a paged form + * + * @return void + * @access private + */ + function _setupForm(){ + + // display test name + $this->form->addElement('header', 'instrument_title', "MRI Parameter Form"); + + // automatically adds examiner & date of administration + $this->_addMetadataFields(); + + $this->form->addElement("select", "Scanner_Type", "Type of Scanner:", array("" => NULL, "hospital" => "Hospital", "research" => "Research", "other" => "Other")); + $this->form->addElement("text", "Scanner_Type_other", "If other, specify:"); + //$this->XINRegisterRule("Scanner_Type_other", array("Scanner_Type{@}=={@}other"), "Required if scanner type is other"); + + $this->addTextElement("session_attempts", "Number of Session Attempts:"); + $this->addHourMinElement("wait_time", "Wait Time for Scanner (hours:minutes)"); + $this->addHourMinElement("total_duration", "Total Duration of Session (hours:minutes)"); + $this->form->addElement('select','sedation', "Was sedation administered?", array(""=>NULL,"Yes" => "Yes", "No"=>"No")); + $this->form->addElement('static', null, "(please use comment field for details)"); + $this->form->addElement('select','medication', "Was the child on any medication?", array(""=>NULL,"Yes" => "Yes", "No"=>"No", 'not_answered'=>'Not Answered')); + + $this->form->addElement("text", "medication_specify", "If yes, specify:"); + $this->form->addFormRule(array(&$this, 'Medication_Rules')); + $yes_no_option= array(""=>NULL, "No"=>"No", "Complete"=>"Yes, Complete Acquisition", "Partial"=>"Yes, Partial Acquisition", "not_answered"=>"Not Answered"); + + $this->form->addElement('header', null, 'Localizer'); + $this->form->addElement('static', null, 'Scan time: 1-2 min (exact details are not critical)'); + + $this->form->addElement('header', null, 'Warm up scan (Coarse TSE PD/T2W)'); + $this->form->addElement('static', null, 'Optional warm-up sequence of site\'s choosing'); + $this->form->addElement('static', null, 'Scan time: should not exceed 2 minutes'); + + $this->form->addElement('header', null, '3D T1 MP-Rage'); + $this->form->addElement('static', null, 'SIEMENS: tfl'); + $this->form->addElement('select', 't1_Scan_done', 'Was this sequence acquired?', $yes_no_option); + $this->form->addElement('text', "t1_number_attempts", "Number of scan attempts:"); + $this->addDateElement('t1_Scan_done', 'If so, when?'); + $this->addTextAreaElement('t1_Comments', 'Comments'); + + $this->form->addElement('header', null, 'High Resolution 3D T2W'); + $this->form->addElement('static', null, 'SIEMENS: tse_vfl'); + $this->form->addElement('select', 't2_Scan_done', 'Was this sequence acquired?', $yes_no_option); + $this->form->addElement('text',"t2_number_attempts", "Number of scan attempts:"); + $this->addDateElement('t2_Scan_done', 'If so, when?'); + $this->addTextAreaElement('t2_Comments', 'Comments'); + + $this->form->addElement('header', null, '25 Direction DTI'); + $this->form->addElement('static', null, 'SIEMENS: epd2_diff'); + $this->form->addElement('select', 'dti_Scan_done', 'Was this sequence acquired?', $yes_no_option); + $this->form->addElement('text', "dti_number_attempts", "Number of scan attempts:"); + $this->addDateElement('dti_Scan_done', 'If so, when?'); + $this->addTextAreaElement('dti_Comments', 'Comments'); + + $this->form->addElement('header', null, '65 Direction DTI'); + $this->form->addElement('static', null, 'SIEMENS: epd2_diff_12ch_b2000'); + $this->form->addElement('select', 'DTI65Dir_Scan_done', 'Was this sequence acquired?', $yes_no_option); + $this->form->addElement('text', "DTI65Dir_number_attempts", "Number of scan attempts:"); + $this->addDateElement('DTI65Dir_Scan_done', 'If so, when?'); + $this->addTextAreaElement('DTI65Dir_Comments', 'Comments'); + + $this->form->addElement('header', null, 'Resting BOLD'); + $yes_no_option2 = array(""=>NULL, "No"=>"No", "Complete"=>"Yes, Complete Acquisition", "Partial"=>"Yes, Partial Acquisition", "not_answered"=>"Not Answered"); + $this->form->addElement('select', 'fMRI_Scan_done', 'Was this sequence acquired?', $yes_no_option2); + $this->form->addElement('select', 'fMRI_Number_Complete_Runs', 'Number of complete runs:', array('' => '', '1' => '1', '2'=> '2', '3' => '3')); + // Adding XIN rules would be complicated at this point, since everything would need to ne explicitly never_required'd. + // So just add a form rule + $this->form->addFormRule(array(&$this, 'MRI_Bold_Rules')); + $this->XINRegisterRule("fMRI_Number_Complete_Runs", array("Bold_Scan_done{@}=={@}Complete"), "Required if "); + $this->form->addElement('text', "fMRI_number_attempts", "Number of scan attempts:"); + $this->addDateElement('fMRI_Scan_done', 'If so, when?'); + $yes_no_option3 = array(""=>NULL,"No"=>"No","Yes"=>"Yes","Possibly"=>"Possibly"); + $this->form->addElement('select','fMRI_child_awake','Was the child awake during the BOLD scanning?',$yes_no_option3); + $this->addTextAreaElement('fMRI_Comments', 'Comments'); + + $this->form->addElement('header', null, 'Spectroscopy'); + $this->form->addElement('select', 'Spectroscopy_Scan_done', 'Was this sequence acquired?', $yes_no_option); + $this->form->addElement('text', "Spectroscopy_number_attempts", "Number of scan attempts:"); + $this->addDateElement('Spectroscopy_Scan_done', 'If so, when?'); + $this->form->addElement('static', null, 'Which scans were acquired?'); + + $acquired_option= array(""=>NULL, "acquired" => "Acquired", "not_acquired" => "Not Acquired"); + $this->form->addElement('select', 'Spectroscopy_long_echo_acquired', "Long echo", $acquired_option); + $this->form->addElement('select', 'Spectroscopy_short_echo_acquired', "Short echo", $acquired_option); + $this->form->addElement('select', 'Spectroscopy_water_scan_acquired', "Water Scan", $acquired_option); + + $this->addTextAreaElement('Spectroscopy_Comments', 'Comments'); + + $this->form->addElement('header',null,'GRE Field Map'); + $this->form->addElement('select','gre_accquired',"Was this sequence acquired",array(""=>NULL,"yes" => "Yes", "no"=>"No", 'not_answered'=>'Not Answered')); + $gre_options= array(""=>null,"25dir_dti"=>"25 direction DTI","65dir_dti"=>"65 direction DTI","bold"=>"BOLD"); + $this->form->addElement('select','gre_fieldmap',"If yes, which field maps were acquired?
Cmd+Click or Ctrl+Click to select multiple options", $gre_options,"multiple size='4'"); + $this->form->addFormRule(array(&$this, 'GRE_Rules')); + + $this->form->addElement('header', null, 'Direction File'); + + $this->form->addElement('static', 'coordinate_system', 'Coordinate system:'); + $this->form->addElement('static', 'normalization', 'Normalization:'); + + for($i=0; $i < 25; $i++){ + $this->form->addElement('static', "vector{$i}", "Vector {$i}:"); + } + + $this->form->addElement('file', 'directions_file', 'Upload the directions file (optional)'); + } // End setupForm + + function Medication_Rules($values) { + if($values['medication'] == 'Yes' && $values['medication_specify'] == '') { + return array('medication_specify' => 'Required if on medication'); + } + return array(); + } + + function MRI_Bold_Rules($values) { + if(($values['fMRI_Scan_done'] == 'Complete' || $values['fMRI_Scan_done'] == 'Partial')&& $values['fMRI_Number_Complete_Runs'] == '') { + return array('fMRI_Number_Complete_Runs' => 'Required if acquisition complete or partial'); + } + return array(); + } + + function GRE_Rules($values) { + if($values['gre_accquired']=='yes' && ($values['gre_fieldmap']==null || $values['gre_fieldmap']=="") ){ + return array('gre_fieldmap' => 'Required'); + + } + return array(); + } + + + // methods available to all children + /** + * preprocesses the array of values to be saved into the database + * (such as to rearrange date fields) + * + * @param array $values the array of values ready to be passed to + * an Database::update call as the set array + * @return void + * @access private + */ + function _saveValues($values) + { + $timepoint =& TimePoint::singleton($this->getSessionID()); + $candidate =& Candidate::singleton($timepoint->getCandID()); + $this->_nullStatus($values); + //Convert select multiple elements into database storable values + if(!empty($this->_selectMultipleElements)){ + foreach($this->_selectMultipleElements AS $elname){ + if(isset($values[$elname]) && is_array($values[$elname])){ + $values[$elname]=implode("{@}",$values[$elname]); + } + } + } + + if(isset($values['Date_taken'])) { + $date = $values['Date_taken']; + if(!empty($date['Y']) && !empty($date['M']) && !empty($date['d'])) { + $values['Date_taken'] = $this->_getDatabaseDate($date); //sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + $this->_saveCandidateAge($values); + } else { + unset($values['Date_taken']); + } + } + + if(isset($values['t1_Scan_done_date'])) { + $date = $values['t1_Scan_done_date']; + if(!empty($date['Y']) && !empty($date['M']) && !empty($date['d'])) { + $values['t1_Scan_done_date'] = $this->_getDatabaseDate($date); //sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + //$values['t1_Scan_done_date'] = sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + } else { + unset($values['t1_Scan_done_date']); + } + + } + + if(isset($values['t2_Scan_done_date'])) { + $date = $values['t2_Scan_done_date']; + if(!empty($date['Y']) && !empty($date['M']) && !empty($date['d'])) { + $values['t2_Scan_done_date'] = $this->_getDatabaseDate($date); //sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + //$values['t2_Scan_done_date'] = sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + } else { + unset($values['t2_Scan_done_date']); + } + + } + + if(isset($values['dti_Scan_done_date'])) { + $date = $values['dti_Scan_done_date']; + if(!empty($date['Y']) && !empty($date['M']) && !empty($date['d'])) { + $values['dti_Scan_done_date'] = $this->_getDatabaseDate($date); //sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + //$values['dti_Scan_done_date'] = sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + } else { + unset($values['dti_Scan_done_date']); + } + + } + + if(isset($values['DTI65Dir_Scan_done_date'])) { + $date = $values['DTI65Dir_Scan_done_date']; + if(!empty($date['Y']) && !empty($date['M']) && !empty($date['d'])) { + $values['DTI65Dir_Scan_done_date'] = $this->_getDatabaseDate($date); //sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + //$values['DTI65Dir_Scan_done_date'] = sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + } else { + unset($values['DTI65Dir_Scan_done_date']); + } + + } + if(isset($values['fMRI_Scan_done_date'])) { + $date = $values['fMRI_Scan_done_date']; + if(!empty($date['Y']) && !empty($date['M']) && !empty($date['d'])) { + $values['fMRI_Scan_done_date'] = $this->_getDatabaseDate($date); //sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + //$values['fMRI_Scan_done_date'] = sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + } else { + unset($values['fMRI_Scan_done_date']); + } + + } + if(isset($values['Spectroscopy_Scan_done_date'])) { + $date = $values['Spectroscopy_Scan_done_date']; + if(!empty($date['Y']) && !empty($date['M']) && !empty($date['d'])) { + $values['Spectroscopy_Scan_done_date'] = $this->_getDatabaseDate($date); //sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + //$values['Spectroscopy_Scan_done_date'] = sprintf("%04d-%02d-%02d", $date['Y'], $date['M'], $date['d']); + } else { + unset($values['Spectroscopy_Scan_done_date']); + } + + } + + if(isset($values['wait_time'])) { + $time = $values['wait_time']; + $values['wait_time'] = sprintf("%02d:%02d:00", $time['H'], $time['i']); + } + + if(isset($values['total_duration'])) { + $time = $values['total_duration']; + $values['total_duration'] = sprintf("%02d:%02d:00", $time['H'], $time['i']); + } + + //print_r($values['wait_time']); + if(isset($values['directions_file']) && !empty($values['directions_file']['name'])) { + require_once "File_Upload.class.inc"; + $file=new File_Upload; + + //pass the existing form ($form) by reference to File_Upload, and register the file_upload field names being used. + $file->registerForm($this->form); + + //Tell File_Upload what file handlers to use. + $file->setFileHandler("directions_file", $this); + + //Set the target directory that you want files moved into once they are validated and processed. + $config = NDB_Config::singleton(); + $dir = $config->getSetting("UploadDir"); + if(empty($dir)) { + $dir = "."; + } + + $file->setBaseUploadDirectory($dir . "/mri_parameter_directions"); + + //Set the prefix to prepend to the filenames + //$file->setFilenamePrefix($timepoint->getVisitLabel()."-".$this->testName."-"); + + //set the the IDs to the handler functions. + $file->setHandlerArgs(array("CommentID"=>$this->getCommentID(), + "candID"=>$candidate->getCandID(), + "PSCID"=>$candidate->getPSCID(), + "visitLabel"=>$timepoint->getVisitLabel(), + "username"=>$_SESSION['State']->getUsername(), + "values"=>$values) + ); + + //If the form is validated, call File_Upload::processFiles() which loops through the files and + //proccesses them (including verify, move, and import steps) + + $file->processFiles(); + } + + + //echo error messages + if(!empty($file->errorLog)){ + while(list($fileType,$fileErrors)=each($file->errorLog)){ + foreach($fileErrors AS $error){ + echo "Upload Error $fileType: $error
"; + } + } + } + unset($values['candID'], $values['sessionID'], $values['commentID'], $values['test_name'], $values['page'], $values['fire_away'], $values['subtest'], $values['MAX_FILE_SIZE'], $values['directions_file']); + $this->_save($values); + } + + /** + * isValid checks the validity of the file. + * + * @param string $file The full filename including directory. + * + * @return bool $success if operation succeeded + * @access public + */ + function isValid(&$file, $args){ + $fp=fopen($file->fileInfo['tmp_name'], "r"); + $contents=fread($fp,filesize($file->fileInfo['tmp_name'])); + fclose($fp); + + $lines=explode("\n",$contents); + + $i = 0; + $size = count($lines); + while(!preg_match("/\[directions\s*\=\s*25\]/", $lines[$i++])){ + if($i > $size){ + echo("Improper format: Can't find entry with 25 directions.
"); + return false; + } + } + if(!preg_match("/CoordinateSystem\s*=\s*.+/", $lines[$i++])){ + echo("Improper format: Can't find coordinate system.
"); + return false; + } + if(!preg_match("/Normalisation\s*=\s*.+/", $lines[$i++])){ + echo("Improper format: Can't find normalization.
"); + return false; + } + + for($j = 0; $j < 25; $j++){ + if(!preg_match("/Vector\[{$j}\]\s*=\s*\(\s*(\-?[\d\.]+)\s*,\s*(\-?[\d\.]+)\s*,\s*(\-?[\d\.]+)\s*\)/", $lines[$i++])){ + echo("Improper format: Can't find entry for vector {$j}.
"); + return false; + } + } + return true; + } + + /** + * importFile imports the vineland file into the database. + * + * @param object $file A reference to the file object (passed automatically by callFileHandler) + * @param assoc_array $args The arguments passed to the function they must be: + * - + * + * @return bool $success if operation succeeded + * @access public + */ + function importFile(&$file, $args){ + $fp=fopen($file->fileInfo['tmp_name'], "r"); + $contents=fread($fp,filesize($file->fileInfo['tmp_name'])); + fclose($fp); + + $values=array( + 'UserID' =>$args['username'], + 'File_type' =>'directions', + 'File_name' =>$file->getDestinationFilename(), + 'Data_dir' =>$file->getDestinationDirectory() + ); + + + //Cycle through the lines and extract the data + $lines=explode("\n",$contents); + + $i = 0; + $size = count($lines); + while(!preg_match("/\[directions\s*\=\s*25\]/", $lines[$i++]) && $i < $size); + $result = preg_split("/\s*\=\s*/", $lines[$i++]); + $values["coordinate_system"] = trim($result[1]); + $result = preg_split("/\s*\=\s*/", $lines[$i++]); + $values["normalization"] = trim($result[1]); + + for($j = 0; $j < 25; $j++){ + $matches = array(); + if(preg_match("/Vector\[{$j}\]\s*=\s*\(\s*(\-?[\d\.]+)\s*,\s*(\-?[\d\.]+)\s*,\s*(\-?[\d\.]+)\s*\)/", $lines[$i++], $matches)){ + $values["vector{$j}_x"] = $matches[1]; + $values["vector{$j}_y"] = $matches[2]; + $values["vector{$j}_z"] = $matches[3]; + + $vector_x = number_format($values["vector{$j}_x"], 5); + $vector_y = number_format($values["vector{$j}_y"], 5); + $vector_z = number_format($values["vector{$j}_z"], 5); + + $this->localDefaults["vector{$j}"] = "({$vector_x}, {$vector_y}, {$vector_z})"; + }else{ + return false; + } + } + + $db=& Database::singleton(); + + ///Setting trackchanges to false because getting error messages + $db->_trackChanges = false; + //////////////////////////////////////////////////////////////// + + $result = $db->update($this->testName, $values, array('CommentID'=>$args['CommentID'])); + return true; + } + + function formatDate($date){ + if(empty($date)){ + return null; + } + $dateBits = explode('/', $date); + return sprintf("%04d-%02d-%02d", $dateBits[2], $dateBits[0], $dateBits[1]); + } + + /** + * getTargetDirectory retrieves info about where the file should be stored. + * + * The returned directory is ADDED to the baseUploadDirectory proprety. It should have a trailing slash. + * + * @param string $file The full filename including directory. + * + * @return bool $success if operation succeeded + * @access public + */ + function getTargetDirectory(&$file, $args){ + $output=$args['candID']."/"; + return $output; + } +} +?> diff --git a/instruments/NDB_BVL_Instrument_peer_socialcontact.class.inc b/instruments/NDB_BVL_Instrument_peer_socialcontact.class.inc new file mode 100644 index 0000000..f176e4a --- /dev/null +++ b/instruments/NDB_BVL_Instrument_peer_socialcontact.class.inc @@ -0,0 +1,930 @@ +NULL, '1_very_rarely'=>"1. Very Rarely", '2_rarely'=>"2. Rarely", + '3_occasionally'=>"3. Occasionally",'4_somewhat_often'=>'4. Somewhat Often', + '5_often'=>'5. Often','6_very_often'=>'6. Very Often','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $respondent = array(''=>NULL,'mother'=>'Mother','father'=>'Father','other'=>'Other',"not_answered"=>"Not answered"); + var $num_playmates = array(''=>NULL,'0'=>0,'1'=>1,'2'=>2,'3'=>3,'4'=>4,'5'=>5,'not_answered'=>"Not answered"); + var $total_peer = 5; + var $age_yrs = array(''=>NULL,'0'=>'0yrs','1'=>'1 yr','2'=>'2 yrs','3'=>'3 yrs','4'=>'4 yrs','5'=>'5 yrs', + '6'=>'6 yrs','7'=>'7 yrs','8'=>'8 yrs','9'=>'9 yrs','10'=>'10 yrs', + '11'=>'11 yrs','12'=>'12 yrs','13'=>'13 yrs','14'=>'14 yrs','15'=>'15 yrs', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $age_mos = array(''=>NULL,'0'=>'0 mos','1'=>'1 mos','2'=>'2 mos','3'=>'3 mos','4'=>'4 mos','5'=>'5 mos', + '6'=>'6 mos','7'=>'7 mos','8'=>'8 mos','9'=>'9 mos','10'=>'10 mos', + '11'=>'11 mos','12'=>'12 mos','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $gender = array(''=>NULL,'boy'=>'Boy','girl'=>'Girl','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q3_options = array(''=>NULL, 'cousins'=>'Cousins','neighbors'=>'Neighbors', + 'classmates'=>'Classmates','parent_friend_child'=>"Parent's friend's child", + 'other'=>'Other','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q4_options = array(''=>NULL,'mostly_your_home'=>'Mostly your home', + 'mostly_playmate_home'=>'Mostly playmates home','equally_both'=>'Equally both', + 'mostly_other'=>'Mostly other (such as playground)','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q5_options = array(''=>NULL,'walking_distance'=>'Walking distance', + 'short_drive'=>'Short drive(up to 15min)','long_drive'=>'Long drive(> 15min)', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $yesNo = array(''=>NULL,'yes'=>'Yes','no'=>'No','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q8_options = array(''=>NULL,'none'=>'None','dont_know'=>"Don't know",'autism'=>'Autism', + 'other_developmental_delay'=>'Other developmental delay', + 'speech_language_delay'=>'Speech or language delay', + 'other'=>'Other','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q9_options = array(''=>NULL,'really_likes'=>'Really likes','likes'=>'Likes', + 'pretty_neutral'=>'Pretty neutral','just_tolerates'=>'Just tolerates', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q10_options = array(''=>NULL,'really_likes'=>'Really likes','likes'=>'Likes', + 'pretty_neutral'=>'Pretty neutral','just_tolerates'=>'Just tolerates', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + + var $q11_options = array(''=>NULL,'very_well'=>'Very well','okay'=>'Okay', + 'not_very_well'=>'Not very well','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q12_options = array(''=>NULL,'my_child'=>'My child','playmate'=>'Playmate','both'=>'Both', + 'adult'=>'Adult','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q13_options = array(''=>NULL,'positive'=>'Positive','neutral'=>'Neutral','negative'=>'Negative', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q14_options = array(''=>NULL,'very_excited'=>'Very Excited','active'=>'Active','calm'=>'Calm', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q15_options = array(''=>NULL,'frequently'=>'Frequently','occasionally'=>'Occasionally', + 'rarely'=>'Rarely','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q16_options = array(''=>NULL,'ends_play'=>'Ends play','major_disruption'=>'Major disruptions', + 'minor_disruption'=>'Minor disruption','no_disruption'=>'No disruption', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q17_options = array(''=>NULL,'lot_of_interaction'=>'A lot of interaction', + 'stay_close_no_interaction'=>'Stay close but do not interact much', + 'dont_stay_close_or_interact'=>'Do not stay close or interact much', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q19_options = array(''=>NULL,'very_difficult'=>'Very difficult', + 'somewhat_difficult'=>'Somewhat difficult', + 'not_difficult'=>'Not difficult at all', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q20_options = array(''=>NULL,'very_stressful'=>'Very stressful', + 'somewhat_stressful'=>'Somewhat stressful', + 'not_stressful'=>'Not stressful at all', + 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q21_options = array(''=>NULL,'frequently'=>'Frequently','occasionally'=>'Occasionally', + 'little_or_none'=>'Little-or-none','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q23_options = array(''=>NULL,'yes'=>'Yes','no'=>'No (go to next section)','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $q24_options = array(''=>NULL,'yes'=>'Yes','no'=>'No','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $activity_desc = array(''=>NULL,'religious'=>'a. Religious activity','play_group'=>'b. Play group','sports'=>'c. Sports(soccer, dance etc)','recreational'=>'d. Recreational (music, art, reading, etc.)','other'=>'e. Other','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $special_service = array(''=>NULL,'occupational_physical'=>'a. Occupational/Physical therapy','lang_speech'=>'b. Language or speech therapy','behavior'=>'c. Behavior therapy','social_skills'=>'d. Social skills or play group',"not_answered"=>"Not answered"); + var $numdays_week = array(''=>NULL,'0'=>'0','1'=>'1','2'=>'2','3'=>'3','4'=>'4','5'=>'5','6'=>'6','7'=>'7','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $numhrs_day = array(''=>NULL,'0'=>'0','1'=>'1','2'=>'2','3'=>'3','4'=>'4','5'=>'5','6'=>'6','7'=>'7','8'=>'8','9'=>'9', + '10'=>'10','11'=>'11','12'=>'12','13'=>'13','14'=>'14','15'=>'15','16'=>'16','17'=>'17','18'=>'18', + '19'=>'19','20'=>'20','21'=>'21','22'=>'22','23'=>'23','24'=>'24','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + var $promote_play = array(''=>NULL,'almost_always'=>'a. Almost always','sometimes'=>'b. Sometimes','never'=>'c. Never', 'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + //abstract function setup($commentID, $page); + /** + * sets up basic data, such as the HTML_Quickform object, and so on. + * + * @param string $commentID the CommentID identifying the data to load + * @param string $page if a multipage form, the page to show + * @return void + * @access public + */ + function setup($commentID, $page){ + $this->formType="XIN"; + $this->form = new HTML_Quickform('test_form'); + $this->page = $page; // page label (number or + + // set the object properties + $this->testName = "peer_socialcontact"; // test_names.Test_name + $this->table = 'peer_socialcontact'; + // data keyed by commentID + $this->commentID = $commentID; + // $this->dateTimeFields = array("datecompleted"); + $config = & NDB_Config::singleton(); + $this->dateOptions = array( + 'language' => 'en', + 'format' => 'YMd', + 'minYear' => $config->getSetting('startYear'), + 'maxYear' => $config->getSetting('endYear'), + 'addEmptyOption' => true, + 'emptyOptionValue' => null + ); + + $db =& Database::singleton(); + try { + $record = $db->pselectRow("SELECT * FROM ".$this->table." WHERE CommentID=:cid", + array('cid' =>$this->getCommentID())); + } catch (DatabaseException $ex) { + print "Query has failed to select"; + $record = array(); + } + $fields = array('peer_initials_1','peer_initials_2','peer_initials_3','peer_initials_4','peer_initials_5'); + foreach($fields as $field) { + $this->localDefaults[$field] = $record[$field]; + } + if(!empty($record['total_playmates'])) { + $this->localDefaults['total_peer'] = $record['total_playmates']; + } else { + $this->localDefaults['total_peer'] = 5; //by default we want the total playmates as 5 in order for the data dictionary to work properly + } + + // required fields for data entry completion status + $this->_requiredElements = array('total_playmates'); + /* + for($i = 1; $i <= $record['total_playmates']; $i++) { + array_push($this->_requiredElements,"age_yrs_playmate_".$i ); + } + */ + // setup the form + $this->_setupForm(); + + } + /** + * method to build the HTML_Quickform object into a paged form + * + * @return void + * @access private + */ + function _setupForm(){ + + if(preg_match("/peer_socialcontact(_page[0-9]+)/",$this->page,$matches)){ + call_user_method($matches[1], $this); + } else { + $this->_main(); + } + + //Defines the call back function for HTML Quickform to use when validating the form. + $this->form->addFormRule(array(&$this,'XINValidate')); + } + + + /** + * generates the main page of the form. + * + * @return void + * @access private + * + */ + function _main(){ + // display test name + + $this->form->addElement('header', 'instrument_title', "Peer Social Contact Questionnaire"); + + // $this->addBasicDate('datecompleted', "Today's date:", $this->dateOptions); + $this->addSelect('respondent',"Completed by :", $this->respondent); + + $group [] = $this->createText('respondent_other',$this->indent."If Other please specify"); + $this->addGroup($group,"respondent_other_group",$this->indent."If Other please specify",null,false); + unset($group); + $rules_array = array("respondent{@}=={@}other"); + $this->XINRegisterRule('respondent_other', $rules_array, "Please specify relationship", 'respondent_other_group'); + + $this->form->addElement('header', null, "Section 1: Individual Playmates"); + $this->form->addElement('static', 'lorisSubHeader', "We would like to know about the children your child plays with regularly outside of
school, childcare or group activities. Please do not include siblings or other
relatives living in your home. List the children your child plays with at least every
three months and who are at least 2 years old and no more than 10 years old.
There are spaces for you to list up to five children; however, most children do not
have that many regular playmates. It's easy to forget some children when making
such lists. Please think about children your child knows in various groups (such as
cousins, neighbors, classmates, children of your friends, etc) or settings (such as
church, childcare, recreational activities, the gym, etc.). If there are more than five
children, please list the ones your child plays with most frequently."); + + $this->addSelect('total_playmates',"Please choose the number of playmates your child has :", $this->num_playmates); + $this->form->addElement('static', 'lorisSubHeader', "Please list the initals of the children your child plays with regularly
the lines below:"); + for($i = 1; $i <=$this->total_peer; $i++) { + $group [] = $this->createText("peer_initials_".$i,"Initials of child $i:"); + $this->addGroup($group,"peer_initials_".$i."_group","Initials of child $i:",null, false); + unset($group); + $this->XINRegisterRule('peer_initials_'.$i, array("peer_initials_.$i{@}=={@}NEVER_REQUIRED"), null, "peer_initials_".$i."_group"); + } + $this->form->addElement("static", null, "

"); + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + + } + + function _page2() { + + // $this->form->addElement("static",null,"In the table below indicate which children know each other.
You must select `Yes` or `No` fields to save this form.
"); + $db =& Database::singleton(); + $numplaymates = $db->pselectOne("SELECT total_playmates FROM ".$this->table." WHERE CommentID=:cid", + array('cid' =>$this->getCommentID())); + + if(isset($_GET["key"])) { + $key = $_GET["key"]; + } + $colspan = 2; + + if(is_numeric($numplaymates)) { + $colspan = $numplaymates -1; + } else { + $numplaymates = 5; + } + + if($numplaymates > 1) { + $group[] = &$this->form->createElement("static", null, null,""); + $this->form->addGroup($group,"page2_header","In the table below indicate which of your children's playmates know
each other. You must select 'Yes' or 'No' in all fields to save this form.", null,false); + unset($group); + + } else if($numplaymates == 1) { + $group[] = &$this->form->createElement("static", null, null,""); + + $this->form->addGroup($group,"page2_header","In the previous page you have indicated $numplaymates playmate(s).
Please click 'Save and Continue' to go to the next section. ", null,false); + unset($group); + + } else if($numplaymates == 0) { + $group[] = &$this->form->createElement("static", null, null,""); + + $this->form->addGroup($group,"page2_header","In the previous page you have indicated $numplaymates playmate(s).
IF so, please click here go to the next section: Section 2 ", null,false); + unset($group); + + } + + $this->localDefaults['total_peer'] = $numplaymates; + if($this->localDefaults['total_peer'] > 1) { + $group[] = &$this->form->createElement("static", null, null, "Child 1"); + } + if($this->localDefaults['total_peer'] >= 2) { + $group[] = &$this->form->createElement("static", null, null, "Child 2"); + } + if($this->localDefaults['total_peer'] >= 3) { + $group[] = &$this->form->createElement("static", null, null, "Child 3"); + } + if($this->localDefaults['total_peer'] >= 4) { + $group[] = &$this->form->createElement("static", null, null, "Child 4"); + } + if($this->localDefaults['total_peer'] == 5) { + $group[] = &$this->form->createElement("static", null, null, "Child 5"); + } + + //$this->form->addGroup($group, "table_headers","" , $this->_GUIDelimiter, false); + if($this->localDefaults['total_peer'] > 1 ) { + $this->form->addGroup($group, "table_headers",null , $this->_GUIDelimiter, false); + unset($group); + } + + $know_eachother = array(""=>null,"yes"=>"Yes","no"=>"No","refused_dontknow"=>"Refused/I Don't Know"); + for($i = 1; $i < $this->localDefaults['total_peer']; $i++){ + $groupname = $i."_group"; + for($j = 1; $j <= $this->localDefaults['total_peer']; $j++ ) { + $field = "know_" . $i . "_" . $j; + if ($j <= $i) { + $group[] =& $this->form->createElement("static", null, null, "-"); + + } else { + $group[] =& $this->form->createElement("select", $field, null, $know_eachother); + } + + $this->XINRegisterRule($field, array("$field{@}=={@}NEVER_REQUIRED"), $groupname); + } + $this->form->addGroup($group, $groupname, "Child ".$i, $this->_GUIDelimiter, false); + unset($group); + } + + /* the following "if" statement was created to submit a hidden value of null to know_1_2 since + * it is hardcoded that each iteration must update the code somehow + *find a better fix !!! + */ + + + if ($this->localDefaults['total_peer'] == 1 || $this->localDefaults['total_peer'] == 0){ + $field = "know_1_2"; + $group[] =& $this->form->createElement("hidden", $field, null); + $group[] = &$this->form->createElement("static", null, null,""); + $group[] = &$this->form->createElement("static", null, null,""); + $this->form->addGroup($group, $groupname, " ", $this->_GUIDelimiter, false); + unset($group); + } + + + if($this->localDefaults['total_peer'] > 0) { + $group[] = &$this->form->createElement("static", null, null,""); + $this->form->addGroup($group,"page2_header","Initials of the playmates are :
", null,false); + unset($group); + } + for ($i = 1; $i<= $this->localDefaults['total_peer']; $i++) { + if(!empty($this->localDefaults["peer_initials_".$i])) { + $group[] =& $this->form->createElement("static", "peer_initials_".$i,null,null); + $this->form->addGroup($group,$i."_header","Initials of child $i:", null,false); + unset($group); + + } + } + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + + } + + function _page3(){ + $this->_generate_Questions(1); // playmate #1 + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + } + function _page4() { + $this->_generate_Questions(2); // playmate #2 + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + } + function _page5() { + $this->_generate_Questions(3); // playmate #3 + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + } + function _page6() { + $this->_generate_Questions(4); // playmate #4 + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + } + function _page7() { + $this->_generate_Questions(5); // playmate #5 + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + } + function _page8() + { + $this->form->addElement('header', 'section2_header', "Section 2: Educational or Child Care Activities"); + if(isset($_GET["key"])) { + $key = $_GET["key"]; + } + + $this->form->addElement('static', 'lorisSubHeader',"Aside from individual play activities, children participate in group activities in educational and
childcare placements with peers. We'd like to know a little about your child's current placements.

If your child is not in school or childcare, please go to next section: Section 3 "); + $this->form->addElement('header','section2_a',"School"); + + $this->addSelect('q23_childin_school',"23. Is your child in school or preschool?",$this->q23_options); + //$this->XINRegisterRule('q23_childin_school', array("q23_childin_school{@}=={@}NEVER_REQUIRED"), "Please specify"); + + $this->addSelect('q24_childin_spneeds_school',"24. Is your child in a classroom which specializes in educating children
$this->indent with special educational needs?",$this->q23_options); + $this->XINRegisterRule('q24_childin_spneeds_school', array("q23_childin_school{@}=={@}yes"), "Please specify"); + + $this->addSelect('q25_school_numdays_perweek','25. How many days per week is your child in school?', $this->numdays_week); + $this->XINRegisterRule('q25_school_numdays_perweek', array("q23_childin_school{@}=={@}yes"), "Please specify"); + + $this->addSelect('q26_school_numhrs_perday','26. How many hours per day is your child in school?', $this->numhrs_day); + $this->XINRegisterRule('q26_school_numhrs_perday', array("q23_childin_school{@}=={@}yes"), "Please specify"); + + $questions = array('q27_numchildren_perclass'=>'27. How many children are in the (primary) classroom at school?', + 'q28_numchildren_specialneeds'=>'28. How many of the children have special needs in the (primary)
classroom at school?'); + foreach($questions as $field=>$label) { + $this->addBasicText($field, $label); + $this->XINRegisterRule($field, array("$field{@}=={@}NEVER_REQUIRED")); + } + + $this->form->addElement('header','section2_b',"Childcare"); + + $this->addSelect('q29_childin_childcare',"29. Is your child in a childcare setting?",$this->q23_options); + //$this->XINRegisterRule('q29_childin_childcare', array("q29_childin_childcare{@}=={@}NEVER_REQUIRED"), "Please specify"); + + $this->addSelect('q30_childin_spneeds_childcare',"30. Is the childcare setting a center which specializes in educating
$this->indent children with special educational needs?",$this->q23_options); + $this->XINRegisterRule('q30_childin_spneeds_childcare', array("q29_childin_childcare{@}=={@}yes"), "Please specify"); + + $this->addSelect('q31_childcare_numdays_perweek','31. How many days per week is your child in a childcare setting?', $this->numdays_week); + $this->XINRegisterRule('q31_childcare_numdays_perweek', array("q29_childin_childcare{@}=={@}yes"),"Please specify"); + + $this->addSelect('q32_childcare_numhrs_perday','32. How many hours per day is your child in a childcare setting?',$this->numhrs_day); + $this->XINRegisterRule('q32_childcare_numhrs_perday',array("q29_childin_childcare{@}=={@}yes"), "Please specify"); + + $questions = array('q33_numchildren_perclass'=>'33. How many children are in the (primary) classroom at childcare?', + 'q34_numchildren_specialneeds'=>"34. How many of the children have special needs in the (primary)
$this->indent classroom at childcare?"); + foreach($questions as $field=>$label) { + $this->addBasicText($field, $label); + $this->XINRegisterRule($field, array("$field{@}=={@}NEVER_REQUIRED")); + } + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + } + function _page9() + { + $this->form->addElement('header', 'section3_header', "Section 3: Community Group Activities"); + if(isset($_GET["key"])) { + $key = $_GET["key"]; + } + + $this->form->addElement('static', 'lorisSubHeader',"In addition to school or childcare, some children regularly participate in other group
activities with their peers, such as swimming lessons, soccer, or classes outside of school.
If your child participates in such activities, please list up to three groups below.

If your child does not participate in any such groups, please go to the next section: Section 4

**Please note that groups designed to provide special services for children with
developmental problems are to be included in the next section**"); + for($i = 1; $i <= 3; $i++) { + + $this->form->addElement('header', 'group_header'.$i, " Group $i"); + + $group [] = $this->createText("activity_".$i,"Type or name of group activity :"); + $this->addGroup($group,"activity_".$i."_group","Type or name of group activity :",null, false); + $this->XINRegisterRule('activity_'.$i, array("activity_$i{@}=={@}NEVER_REQUIRED"), null, "activity_".$i."_group"); + unset($group); + $this->addSelect('activity_desc_'.$i,"Which one of the following best describes the group",$this->activity_desc); + $this->XINRegisterRule('activity_desc_'.$i, array("activity_desc_$i{@}=={@}NEVER_REQUIRED") ); + + $group [] = $this->createText('activity_other'.$i,$this->indent."If Other please specify"); + $this->addGroup($group,'activity_other'.$i."_group",$this->indent."If Other please specify",null,false); + unset($group); + $rules_array = array("activity_$i{@}=={@}other"); + $this->XINRegisterRule('activity_other'.$i, $rules_array, "Please specify activity", 'activity_other'.$i.'_group'); + + $questions = array('activity'.$i."_meet_permonth"=>'How many times each month does the group meet?', + 'activity'.$i."_numchildren_participate"=>"How many children participate in the group?", + 'activity'.$i."_numchildren_class"=>"How many of these children are in your child's classroom at school or daycare?", + 'activity'.$i."_numchildren_spneeds"=>"How many of these children have special needs(if known)?"); + foreach($questions as $field=>$label) { + $this->addBasicText($field, $label); + $this->XINRegisterRule($field, array("$field{@}=={@}NEVER_REQUIRED")); + } + + } + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + + } + function _page10() + { + $this->form->addElement('header', 'section4_header', "Section 4: Groups for Special Services"); + if(isset($_GET["key"])) { + $key = $_GET["key"]; + } + + $this->form->addElement('static', 'lorisSubHeader',"Some children who need special services outside of school, such as speech
or physical therapy, receive them in a group setting. If your child regularly participates
with other children in such therapeutic groups, please list up to three of them below.

If your child does not participate in such groups, please go to the next section: Section 5 "); + for($i = 1; $i <= 3; $i++) { + + $this->form->addElement('header', 'group_header'.$i, " Group $i"); + + $group [] = $this->createText("special_service_".$i,"Type or name of group activity :"); + $this->addGroup($group,"special_service_".$i."_group","Type or name of group activity :",null, false); + unset($group); + $this->XINRegisterRule('special_service_'.$i, array("special_service_.$i{@}=={@}NEVER_REQUIRED"), null, "special_service_".$i."_group"); + $this->addSelect('special_service_desc_'.$i,"Which one of the following best describes the group",$this->special_service); + $this->XINRegisterRule('special_service_desc_'.$i, array("special_service_$i{@}=={@}NEVER_REQUIRED") ); + + $questions = array('special_service'.$i."_meet_permonth"=>'How many times each month does the group meet?', + 'special_service'.$i."_numchildren_participate"=>"How many children participate in the group?", + 'special_service'.$i."_numchildren_class"=>"How many of these children are in your child's classroom at school or daycare?"); + foreach($questions as $field=>$label) { + $this->addBasicText($field, $label); + $this->XINRegisterRule($field, array("$field{@}=={@}NEVER_REQUIRED") ); + } + + } + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + + } + function _page11() + { + $this->form->addElement('header', 'section4_header', "Section 5: Effects of Autism on Children's play"); + $this->form->addElement('static','lorisSubHeader', "Please complete the following section if your child has ever been diagnosed with autism
(fill out for either a current diagnosis or a history of autism in the past)"); + $this->addSelect('q1_autism_effect_discuss', "1. When trying to help your child establish a friendship with a peer, do you discuss
issues related to autism with peer's parents in order to promote better play?", $this->promote_play); + //$this->XINRegisterRule('q1_autism_effect_discuss',array("q1_autism_effect_discuss{@}=={@}NEVER_REQUIRED")); + $strategy = array(''=>NULL, 'detail_about_child_strength_weakness'=>"a. Go into a lot of detail about your child's strengths and weaknesses", + 'no_details_not_minimizing_issues_either'=>'b. Not many details, but not minimizing issues either', + 'minimize_issues_comfortable'=>'c. Minimize the issues to try to help them feel comfortable', + 'never_discuss'=>'d. Never discuss it','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + $this->addSelect('q2_autism_effect_discuss_parents', "2. If you do discuss these issues with parents to promote better play with peers,
which strategy best describes your approach? ", $strategy); + // $this->XINRegisterRule('q2_autism_effect_discuss_parents',array("q2_autism_effect_discuss_parents{@}=={@}NEVER_REQUIRED")); + + $avoid_arranging = array(''=>NULL,'frequently'=>'a. Frequently','occasionally'=>'b. Occasionally','rarely_never'=>'c. Rarely or never','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + $this->addSelect('q3_autism_effect_difficulty_arrangingplay', "3. How often have you experienced difficulty in arranging play opportunities for your
child which you believe is due to your child having autism? ", $avoid_arranging); + // $this->XINRegisterRule('q3_autism_effect_difficulty_arrangingplay',array("q3_autism_effect_difficulty_arrangingplay{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q4_autism_effect_avoid_arranding_play', "4. How often have you avoided arranging play opportunities for your child due to fear
of rejection due to your child having autism?", $avoid_arranging); + // $this->XINRegisterRule('q4_autism_effect_avoid_arranding_play',array("q4_autism_effect_avoid_arranding_play{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q5_autism_effect_concernc_childskill',"5. How often have you avoided arranging play opportunities for your child due to concern
that your child's social skills are not good enough?", $avoid_arranging); + // $this->XINRegisterRule('q5_autism_effect_concernc_childskill',array("q5_autism_effect_concernc_childskill{@}=={@}NEVER_REQUIRED")); + + $restrict = array(''=>NULL, 'very_much'=>'a. Very much','somewhat'=>'b. Somewhat','not_at_all'=>'c. Not at all','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + $this->addSelect('q6_autism_effect_restrict_socialtime',"6. To what extent do the various medical and educational services your child requires
restrict the amount of time your child has for social opportunities with peers?", $restrict); + // $this->XINRegisterRule('q6_autism_effect_restrict_socialtime',array("q6_autism_effect_restrict_socialtime{@}=={@}NEVER_REQUIRED")); + + $school = array(''=>NULL, 'very_satisfied'=>'a. Very satisfied','somewhat_satisfied'=>'b. Somewhat satisfied','not_satisfied'=>'c. Not satisfied','extremely_dissatisfied'=>'d. Extremely dissatisfied','not_in_school'=>'e. Not in school' ,'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + $this->addSelect('q7_autism_effect_satisfaction_schoolstaff', "7. How satisfied are you with how the school staff help your child get along with peers?", $school); + // $this->XINRegisterRule('q7_autism_effect_satisfaction_schoolstaff',array("q7_autism_effect_satisfaction_schoolstaff{@}=={@}NEVER_REQUIRED")); + + $daycare = array(''=>NULL, 'very_satisfied'=>'a. Very satisfied','somewhat_satisfied'=>'b. Somewhat satisfied','not_satisfied'=>'c. Not satisfied','extremely_dissatisfied'=>'d. Extremely dissatisfied','not_in_daycare'=>'e. Not in daycare' ,'refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + $this->addSelect('q8_autism_effect_satisfaction_daycarestaff', "8. How satisfied are you with how the daycare staff help your child get along with peers?", $daycare); + // $this->XINRegisterRule('q8_autism_effect_satisfaction_daycarestaff',array("q8_autism_effect_satisfaction_daycarestaff{@}=={@}NEVER_REQUIRED")); + + $q9_options = array(''=>NULL, 'mostly'=>'Mostly','some'=>'Some','rarely'=>'Rarely','refused_dontknow'=>"Refused/I Don't Know","not_answered"=>"Not answered"); + $this->form->addElement('static',null,"9. To what extent does your child tend to play with each of the groups listed below?"); + $questions = array('q9a_autism_effect_other_children_autism'=>'a. Other children with autism', + 'q9b_autism_effect_other_children_devdelay'=>'b. Other children with developmental delays', + 'q9c_autism_effect_sameage'=>'c. Typically developing children -same age', + 'q9d_autism_effect_younger'=>'d. Typically developing children -younger', + 'q9e_autism_effect_older'=>'e. Typically developing children -older'); + + foreach($questions as $qstn=>$desc) { + $this->addSelect($qstn,$this->indent.$desc, $q9_options); + // $this->XINRegisterRule($qstn, array("$qstn{@}=={@}NEVER_REQUIRED")); + + } + + $this->form->addElement('static',null,"10. Which group of peers does your child play with the best? Please rank order 1 through 5."); + $questions = array('q10a_autism_effect_other_children_autism'=>'a. Other children with autism', + 'q10b_autism_effect_other_children_devdelay'=>'b. Other children with developmental delays', + 'q10c_autism_effect_sameage'=>'c. Typically developing children -same age', + 'q10d_autism_effect_younger'=>'d. Typically developing children -younger', + 'q10e_autism_effect_older'=>'e. Typically developing children -older'); + + foreach($questions as $field=>$label) { + $this->addBasicText($field, $label); + $this->XINRegisterRule($field , array($field."{@}=={@}NEVER_REQUIRED")); + } + + + $this->addBasicTextArea("q11_comments", "11. Feel free to describe any other ways in which you have noticed that having autism has
affected your child's social contacts with peers:"); + $this->XINRegisterRule('q11_comments', array('q11_comments{@}=={@}NEVER_REQUIRED')); + $this->form->addFormRule(array(&$this, 'Peer_Rules')); + + } + function _generate_Questions($playmate) { + $i = $playmate; + $this->form->addElement('header', 'instrument_title', "Peer Social Contact Questionnaire"); + + $this->form->addElement('static', 'lorisSubHeader',"Now, we would like to find out more about your child's relationship with each individual playmate.
Answer the questions below in relationship to their contact in the last three months."); + if(isset($_GET["key"])) { + $key = $_GET["key"]; + } + $db =& Database::singleton(); + + $numplaymates = $db->pselectOne("SELECT total_playmates FROM ".$this->table." WHERE CommentID=:cid", + array('cid' =>$this->getCommentID())); + + + if($playmate > $numplaymates) { + $this->form->addElement('static', 'lorisSubHeader',"NOTE: You only need to fill this form if the playmate initals are specified below.
If no initials are specified click here go to the next section: Section 2 "); + + } else { + $this->form->addElement('static', 'lorisSubHeader',"NOTE: You only need to fill this form if the playmate initals are specified below."); + } + $this->form->addElement('static', 'peer_initials_'.$i, "Playmate #".$i." : "); + + $group[] = &$this->form->createElement("select",'age_yrs_playmate_'.$i,null,$this->age_yrs); + $group[] = &$this->form->createElement("select",'age_mos_playmate_'.$i,null,$this->age_mos); + $this->form->addGroup($group, "age_playmate".$i."_group", "Age ", null, false); + $this->XINRegisterRule('age_yrs_playmate_'.$i, array("age_yrs_playmate_$i{@}=={@}NEVER_REQUIRED"), "age_playmate".$i."_group"); + $this->XINRegisterRule('age_mos_playmate_'.$i, array("age_mos_playmate_$i{@}=={@}NEVER_REQUIRED"), "age_playmate".$i."_group"); + unset($group); + + $this->addSelect('gender_playmate_'.$i,'Gender ', $this->gender); + $g_playmate = 'gender_playmate_'.$i; + $this->XINRegisterRule($g_playmate, array($g_playmate."{@}=={@}NEVER_REQUIRED")); + + $questions = array("q1_number_playtimes_playmate".$i=> "1. How many times on average did they play together in the last
$this->indent three months in your home?","q2_avg_playtime_playmate".$i=>"2. How long are they together in an average play time? (in Minutes)"); + foreach($questions as $field=>$label) { + $group [] = $this->createText($field, $label); + $this->addGroup($group, $field . "_group", $label, null, false); + $this->XINRegisterRule($field,array("$field{@}=={@}NEVER_REQUIRED"),$field . "_group"); + unset($group); + $this->addGroupRule($field . "_group", array(array(array("Value must be numeric.", 'numeric')))); + } + $this->addSelect('q3_related_playmate'.$i,"3. How do they know each other?", $this->q3_options); + $this->XINRegisterRule('q3_related_playmate'.$i, array("q3_related_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $group [] = $this->createText('q3_related_other_playmate'.$i,$this->indent."If Other please specify"); + $this->addGroup($group,'q3_related_other_playmate'.$i."_group",$this->indent."If Other please specify",null,false); + unset($group); + $rules_array = array("q3_related_playmate$i{@}=={@}other"); + $this->XINRegisterRule('q3_related_other_playmate'.$i, $rules_array, "Please specify relationship", 'q3_related_other_playmate'.$i.'_group'); + + $this->addSelect('q4_playarea_playmate'.$i,"4. Where do they usually play?", $this->q4_options); + $this->XINRegisterRule('q4_playarea_playmate'.$i, array("q4_playarea_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $group [] = $this->createText('q4_playarea_other_playmate'.$i,$this->indent."If Other please specify"); + $this->addGroup($group,'q4_playarea_other_playmate'.$i."_group",$this->indent."If Other please specify",null,false); + unset($group); + $rules_array = array("q4_playarea_playmate$i{@}=={@}mostly_other"); + $this->XINRegisterRule('q4_playarea_other_playmate'.$i, $rules_array, "Please specify play area", 'q4_playarea_other_playmate'.$i.'_group'); + + $this->addSelect('q5_proximity_playmate'.$i,"5. How close does this playmate live to your house?", + $this->q5_options); + + $this->XINRegisterRule('q5_proximity_playmate'.$i, array("q5_proximity_playmate.$i{@}=={@}NEVER_REQUIRED")); + // $this->XINRegisterRule(''.$i, array(".$i{@}=={@}NEVER_REQUIRED")); + $this->addSelect('q6_sameschool_playmate'.$i,"6. Is this playmate in your child's school now?", + $this->yesNo); + $this->XINRegisterRule('q6_sameschool_playmate'.$i, array("q6_sameschool_playmate.$i{@}=={@}NEVER_REQUIRED")); + $this->addSelect('q7_samechildcare_playmate'.$i,"7. Is this playmate in your child's childcare now?", + $this->yesNo); + $this->XINRegisterRule('q7_samechildcare_playmate'.$i, array("q7_samechildcare_playmate.$i{@}=={@}NEVER_REQUIRED")); + $this->addSelect('q8_specialneeds_playmate'.$i,"8. What special needs does this playmate have?
Press Ctrl+click or Cmd+click to select mutiple options", + $this->q8_options, "multiple size='8'"); + $this->_selectMultipleElements[] = 'q8_specialneeds_playmate'.$i; + $this->XINRegisterRule('q8_specialneeds_playmate'.$i, array("q8_specialneeds_playmate.$i{@}=={@}NEVER_REQUIRED")); + $group [] = $this->createText('q8_specialneeds_other_playmate'.$i,$this->indent."If Other please specify"); + $this->addGroup($group,'q8_specialneeds_other_playmate'.$i."_group",$this->indent."If Other please specify",null,false); + $this->XINRegisterRule('q8_specialneeds_other_playmate'.$i, array("q8_specialneeds_other_playmate.$i{@}=={@}NEVER_REQUIRED"),'q8_specialneeds_other_playmate'.$i."_group"); + unset($group); + $rules_array = array("q8_specialneeds_playmate$i{@}=={@}other"); + $this->XINRegisterRule('q8_specialneeds_other_playmate'.$i, $rules_array, "Please specify", 'q8_specialneeds_other_playmate'.$i.'_group'); + + $this->addSelect('q9_yourchild_like_playmate'.$i,"9. How much does your child like this playmate?", + $this->q9_options); + $this->XINRegisterRule('q9_yourchild_like_playmate'.$i, array("q9_yourchild_like_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q10_like_yourchild_playmate'.$i,"10. How much does this playmate like your child?", + $this->q9_options); + $this->XINRegisterRule('q10_like_yourchild_playmate'.$i, array("q10_like_yourchild_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q11_getalong_playmate'.$i,"11. How well would you say they get along?", + $this->q11_options); + $this->XINRegisterRule('q11_getalong_playmate'.$i, array("q11_getalong_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q12_leading_play_playmate'.$i,"12. Who usually is leading or in control of the play?", + $this->q12_options); + $this->XINRegisterRule('q12_leading_play_playmate'.$i, array("q12_leading_play_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q13_typical_playtone_playmate'.$i,"13. What is the typical tone of the play?", + $this->q13_options); + $this->XINRegisterRule('q13_typical_playtone_playmate'.$i, array("q13_typical_playtone_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q14_typical_excitement_playmate'.$i,"14. What is the typical level of excitement?", + $this->q14_options); + $this->XINRegisterRule('q14_typical_excitement_playmate'.$i, array("q14_typical_excitement_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q15_num_conflicts_playmate'.$i,"15. How often do they have conflicts during play?", + $this->q15_options); + $this->XINRegisterRule('q15_num_conflicts_playmate'.$i, array("q15_num_conflicts_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q16_disruptive_playmate'.$i,"16. How disruptive are their conflicts?", + $this->q16_options); + $this->XINRegisterRule('q16_disruptive_playmate'.$i, array("q16_disruptive_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q17_how_involved_playmate'.$i,"17. In a typical play session, how closely involved are they with
$this->indent each other during play?", $this->q17_options); + $this->XINRegisterRule('q17_how_involved_playmate'.$i, array("q17_how_involved_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->form->addElement('static',null,"18. What percentage of time were the play dates arranged by:"); + + $q18 = array("q18_arrangeplay_you_playmate".$i=>$this->indent."You (%) ", + "q18_arrangeplay_yourchild_playmate".$i=>$this->indent."Your child (%)", + "q18_arrangeplay_other_playmate".$i=>$this->indent."Other family or friends (%) "); + foreach($q18 as $field=>$label) { + $this->addBasicText($field, $label); + $this->XINRegisterRule($field, array("$field{@}=={@}NEVER_REQUIRED")); + } + + $this->addSelect('q19_arrange_contacts_playmate'.$i,"19. How difficult was it to arrange those contacts?", $this->q19_options); + $this->XINRegisterRule('q19_arrange_contacts_playmate'.$i, array("q19_arrange_contacts_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->addSelect('q20_stress_monitorplay_playmate'.$i,"20. How stressful is it usually for you to monitor play sessions?", $this->q20_options); + $this->XINRegisterRule('q20_stress_monitorplay_playmate'.$i, array("q20_stress_monitorplay_playmate.$i{@}=={@}NEVER_REQUIRED")); + + $this->form->addElement('static',null,"21. How do you usually monitor the play at home?"); + $questions = array('q21_facililate_getalong_playmate'.$i=>'a. Directly facilitate by helping them get along', + 'q21_participate_activity_playmate'.$i=>'b. Participate in the activity with children', + 'q21_maintain_presence_playmate'.$i=>'c. Maintain presence (in childs view)', + 'q21_outofview_listen_playmate'.$i=>'d. Out of view but listen to their ongoing activity', + 'q21_outofview_notlisten_playmate'.$i=>'e. Out of view and can only hear outbursts or cries'); + + foreach($questions as $qstn=>$desc) { + $this->addSelect($qstn,$this->indent.$desc, $this->q21_options); + $this->XINRegisterRule($qstn, array("$qstn{@}=={@}NEVER_REQUIRED")); + + } + + $this->form->addElement('static',null,"22. How much does your child need your help during play with
$this->indent this playmate in the following areas?"); + $questions = array('q22_manage_emotions_playmate'.$i=>'a. Managing Emotions', + 'q22_understand_rules_playmate'.$i=>'b. Understanding social rules (such as taking turns and sharing)', + 'q22_understand_howtoplay_playmate'.$i=>'c. Understanding how to play activities', + 'q22_startplay_playmate'.$i=>'d. Getting the play session started', + 'q22_remain_inplay_playmate'.$i=>'e. Remaining involved in the play', + 'q22_manage_conflicts_playmate'.$i=>'f. Managing conflicts'); + + foreach($questions as $qstn=>$desc) { + $this->addSelect($qstn,$this->indent.$desc, $this->q21_options); + $this->XINRegisterRule($qstn, array("$qstn{@}=={@}NEVER_REQUIRED")); + } + } + + function Peer_Rules ($values) + { + $errors = array(); + $db =& Database::singleton(); + reset($values); + $first =key($values); + try { + $playmates_listed = $db->pselectOne("SELECT total_playmates FROM ".$this->table." WHERE CommentID=:cid", + array('cid' =>$this->getCommentID())); + } catch (DatabaseException $ex) { + print "Query has failed to select"; + $playmates_listed = array(); + } + + for ($i =1; $i < 5; $i++) { + $groupname = $i."_group"; + for($temp = $i+1; $temp<5; $temp++) { + $field = "know_".$i."_".$temp; + if(array_key_exists($field, $values) && empty($values[$field])) { + $errors[$groupname] = "Please specify"; + } + } + } + for($i = 1; $i <= 5; $i++) + { + if((empty($values["peer_initials_".$i]) && $i <= $values["total_playmates"]) && array_key_exists('peer_initials_'.$i, $values)) { + $errors["peer_initials_".$i."_group"] = "Please specify playmate initials"; + } + if(array_key_exists('age_yrs_playmate_'.$i, $values)) { + $questions = array('age_yrs_playmate_','age_mos_playmate_','gender_playmate_','q1_number_playtimes_playmate','q2_avg_playtime_playmate','q3_related_playmate','q4_playarea_playmate','q5_proximity_playmate','q6_sameschool_playmate','q7_samechildcare_playmate','q8_specialneeds_playmate','q9_yourchild_like_playmate','q10_like_yourchild_playmate','q11_getalong_playmate','q12_leading_play_playmate','q13_typical_playtone_playmate','q14_typical_excitement_playmate','q15_num_conflicts_playmate','q16_disruptive_playmate','q17_how_involved_playmate','q18_arrangeplay_you_playmate','q18_arrangeplay_yourchild_playmate','q18_arrangeplay_other_playmate','q19_arrange_contacts_playmate','q20_stress_monitorplay_playmate','q21_facililate_getalong_playmate','q21_participate_activity_playmate','q21_maintain_presence_playmate','q21_outofview_listen_playmate','q21_outofview_notlisten_playmate','q22_manage_emotions_playmate','q22_understand_rules_playmate','q22_understand_howtoplay_playmate','q22_startplay_playmate','q22_remain_inplay_playmate','q22_manage_conflicts_playmate'); + $group_qstn = array('age_yrs_playmate_','age_mos_playmate_','q1_number_playtimes_playmate','q2_avg_playtime_playmate'); + foreach($questions as $qstn) { + $q = $qstn.$i; //print "QQQQQ8";// print_r( $values['q8_specialneeds_playmate'.$i]); + if(empty($values[$q]) && $i <= $playmates_listed && ($i == substr($first, -1))) { + + foreach($values['q8_specialneeds_playmate'.$i ] as $key=>$val) { + if(empty($val)) { + $errors['q8_specialneeds_playmate'.$i] = "Please specify"; + } + } + if($values["q3_related_playmate".$i] == 'other'&& empty($values["q3_related_other_playmate".$i]) ) { + $errors["q3_related_other_playmate".$i."_group"] = "Please specify relationship"; + } + + if($values["q4_playarea_playmate".$i] == 'mostly_other' && empty($values["q4_playarea_other_playmate".$i])) { + $errors["q4_playarea_other_playmate".$i."_group"] = "Please specify play area"; + } + foreach($values["q8_specialneeds_playmate".$i] as $key=>$value) { + if($value == 'other' && empty($values["q8_specialneeds_other_playmate".$i])) { + $errors["q8_specialneeds_other_playmate".$i."_group"] = "Please specify"; + } + } + $q18 = array("q18_arrangeplay_you_playmate".$i, + "q18_arrangeplay_yourchild_playmate".$i, + "q18_arrangeplay_other_playmate".$i); + $total = $values["q18_arrangeplay_you_playmate".$i] + $values["q18_arrangeplay_yourchild_playmate".$i] + + $values["q18_arrangeplay_other_playmate".$i]; + if(($total > 100 || $total < 100) && (!empty($values["q18_arrangeplay_you_playmate".$i]) || !empty($values["q18_arrangeplay_yourchild_playmate".$i]) || !empty($values["q18_arrangeplay_other_playmate".$i]))) { + $errors["q18_arrangeplay_you_playmate".$i] = "The total percent should add up to 100"; + } + } + } + } + } + if(array_key_exists('q23_childin_school', $values)) { + + if($values['q27_numchildren_perclass'] == null && $values['q23_childin_school'] == 'yes') { + $errors["q27_numchildren_perclass"] = "Please specify"; + } + if(!is_numeric($values['q27_numchildren_perclass']) && $values['q23_childin_school'] == 'yes' && strtolower($values['q27_numchildren_perclass']) != 'dont know') { + $errors["q27_numchildren_perclass"] = "Please specify numerical values only or use 'dont know'"; + } + if(!is_numeric($values['q28_numchildren_specialneeds']) && $values['q23_childin_school'] == 'yes' && $values['q28_numchildren_specialneeds'] != null && strtolower($values['q28_numchildren_specialneeds']) != 'dont know') { + $errors["q28_numchildren_specialneeds"] = "Please specify numerical values only or use 'dont know'"; + } + if( $values['q33_numchildren_perclass'] == null && $values['q29_childin_childcare'] == 'yes') { + $errors["q33_numchildren_perclass"] = "Please specify"; + } + if( !is_numeric ($values['q33_numchildren_perclass']) && $values['q29_childin_childcare'] == 'yes' && strtolower($values['q33_numchildren_perclass']) != 'dont know') { + $errors["q33_numchildren_perclass"] = "Please specify numerical values only or use 'dont know'"; + } + if(!is_numeric( $values['q34_numchildren_specialneeds']) && $values['q29_childin_childcare'] == 'yes' && $values['q34_numchildren_specialneeds'] != null && strtolower($values['q34_numchildren_specialneeds']) != 'dont know') { + $errors["q34_numchildren_specialneeds"] = "Please specify numerical values only or use 'dont know'"; + } + + } + if(array_key_exists('q10a_autism_effect_other_children_autism', $values)) { + $q_list = array ('q10a_autism_effect_other_children_autism','q10b_autism_effect_other_children_devdelay', + 'q10c_autism_effect_sameage','q10d_autism_effect_younger','q10e_autism_effect_older'); + foreach($q_list as $q) { + if(($values[$q] < 1 || $values[$q] > 5 ) && ($values[$q] != null)) { + $errors[$q] = "Please rank order 1 through 5 or leave box empty"; + } + } + } + if(array_key_exists('special_service_1', $values)) { + for($i = 1; $i<=3; $i++) { + if(!empty($values['special_service_'.$i]) && empty($values['special_service_desc_'.$i])) { + $errors['special_service_desc_'.$i] = "Please specify"; + } + $questions = array('special_service'.$i."_meet_permonth", 'special_service'.$i."_numchildren_participate", + 'special_service'.$i."_numchildren_class"); + + foreach($questions as $q) { + if(!empty($values['special_service_'.$i]) && $values[$q] == null) { + $errors[$q] = "Please specify"; + } + if(! is_numeric($values[$q]) && !empty($values['activity_'.$i])) { + $errors[$q] = "Please specify numerical values only"; + } + + } + } + } + if(array_key_exists('activity_1', $values)) { + for($i = 1; $i<=3; $i++) { + if( !empty($values['activity_'.$i]) && empty($values['activity_desc_'.$i]) ) { + $errors['activity_desc_'.$i] = "Please specify"; + } + if($values['activity_desc_'.$i] == "other" && empty($values['activity_other'.$i])) { + $errors['activity_other'.$i."_group"] = "Please specify other"; + } + $questions = array('activity'.$i."_meet_permonth", + 'activity'.$i."_numchildren_participate", + 'activity'.$i."_numchildren_class"); + foreach($questions as $q) { + if(! is_numeric($values[$q]) && !empty($values['activity_'.$i])) { + $errors[$q] = "Please specify numerical values only"; + } + + if(!empty($values['activity_'.$i]) && $values[$q] == null) { + $errors[$q] = "Please specify"; + } + } + if(!is_numeric( $values['activity'.$i."_numchildren_spneeds"]) && !empty($values['activity_'.$i]) && $values['activity'.$i."_numchildren_spneeds"] != null) { + $errors['activity'.$i."_numchildren_spneeds"] = "Please specify numerical values only"; + } + } + + } + // print_r($errors); + return $errors; + } + function getReview() { + $DB = Database::singleton(); + $smarty = new Smarty_neurodb(); + $tpl_data = array(); + + $tpl_data['questions'] = $DB->pselect( + "SELECT Description as question, + SourceField FROM parameter_type + WHERE SourceFrom=:TN AND + SourceField NOT IN ('Validity', 'Administration')", + array( + 'TN' => $this->testName + ) + ); + + $Responses = $DB->pselectRow( + "SELECT * FROM " . $this->testName . " WHERE CommentID=:CID", + array('CID' => $this->getCommentID()) + ); + $total_playmates = $Responses['total_playmates']; + $to_unset = array(); + if($total_playmates == 1 || $total_playmates == 0) { + $to_unset = array ('know_1_2','know_1_3','know_1_4','know_1_5','know_2_3','know_2_4','know_2_5','know_3_4','know_3_5','know_4_5'); + } + if($total_playmates == 2) { + $to_unset = array ('know_1_3','know_1_4','know_1_5','know_2_3','know_2_4','know_2_5','know_3_4','know_3_5','know_4_5'); + } + if($total_playmates == 3) { + $to_unset = array ('know_1_4','know_1_5','know_2_4','know_2_5','know_3_4','know_3_5','know_4_5'); + } + if($total_playmates == 4) { + $to_unset = array ('know_1_5','know_2_5','know_3_5','know_4_5'); + } + + foreach($to_unset as $temp) { + foreach($tpl_data['questions'] as $key=>$val) { + if($val['SourceField'] == $temp) { + unset($tpl_data['questions'][$key]); + } + } + } + $questions_toremove = array('peer_initials_' ,'age_yrs_playmate_','age_mos_playmate_','gender_playmate_','q1_number_playtimes_playmate','q2_avg_playtime_playmate','q3_related_playmate','q3_related_other_playmate','q4_playarea_playmate','q4_playarea_other_playmate','q5_proximity_playmate','q6_sameschool_playmate','q7_samechildcare_playmate','q8_specialneeds_playmate','q8_specialneeds_other_playmate','q9_yourchild_like_playmate','q10_like_yourchild_playmate','q11_getalong_playmate','q12_leading_play_playmate','q13_typical_playtone_playmate','q14_typical_excitement_playmate','q15_num_conflicts_playmate','q16_disruptive_playmate','q17_how_involved_playmate','q18_arrangeplay_you_playmate','q18_arrangeplay_yourchild_playmate','q18_arrangeplay_other_playmate','q19_arrange_contacts_playmate','q20_stress_monitorplay_playmate','q21_facililate_getalong_playmate','q21_participate_activity_playmate','q21_maintain_presence_playmate','q21_outofview_listen_playmate','q21_outofview_notlisten_playmate','q22_manage_emotions_playmate','q22_understand_rules_playmate','q22_understand_howtoplay_playmate','q22_startplay_playmate','q22_remain_inplay_playmate','q22_manage_conflicts_playmate'); + for($i= $total_playmates+1; $i<=5; $i++) { + foreach($questions_toremove as $qstn) { + $field = $qstn.$i; + foreach($tpl_data['questions'] as $key=>$val) { + if($val['SourceField'] == $field) { + unset($tpl_data['questions'][$key]); + } + } + } + } + $questions_other = array('q3_related_playmate'=>'q3_related_other_playmate','q4_playarea_playmate'=>'q4_playarea_other_playmate', + 'q8_specialneeds_playmate'=>'q8_specialneeds_other_playmate'); + for($i=1; $i<=5; $i++) { + foreach($questions_other as $other=>$other_val) { + $field = $other.$i; + foreach($tpl_data['questions'] as $key=>$val) { + if($val['SourceField'] == $other_val.$i && $Responses[$field] != 'other') { + unset($tpl_data['questions'][$key]); //unset all if Other specify question if answer to previous question is not set to other + } + } + + } + } + $activity_other = array('activity_desc_'=>'activity_other'); + for($i=1; $i<=3; $i++) { + foreach($activity_other as $other=>$other_val) { + $field = $other.$i; + foreach($tpl_data['questions'] as $key=>$val) { + if($val['SourceField'] == $other_val.$i && $Responses[$field] != 'other') { + unset($tpl_data['questions'][$key]); //unset all if Other specify question if answer to previous question is not set to other + } + + } + + } + $all_activities = array ('activity_desc_'.$i,'activity'.$i."_meet_permonth",'activity'.$i."_numchildren_participate",'activity'.$i."_numchildren_class",'activity'.$i."_numchildren_spneeds"); + + } + foreach ($tpl_data['questions'] as $key=>&$row) { + if (isset($Responses[$row['SourceField']])) { + $section3a = array('q24_childin_spneeds_school','q25_school_numdays_perweek','q26_school_numhrs_perday', + 'q27_numchildren_perclass','q28_numchildren_specialneeds' ); + foreach($section3a as $sec) { + if($row['SourceField'] == $sec && $Responses['q23_childin_school'] == 'no') { + unset($tpl_data['questions'][$key]); + } + } + $section3b = array('q30_childin_spneeds_childcare','q31_childcare_numdays_perweek','q32_childcare_numhrs_perday', + 'q33_numchildren_perclass','q34_numchildren_specialneeds'); + foreach($section3b as $sec) { + if($row['SourceField'] == $sec && $Responses['q29_childin_childcare'] == 'no') { + unset($tpl_data['questions'][$key]); + } + } + if($row['SourceField'] == 'respondent_other' && $Responses['respondent'] != 'other') { + unset($tpl_data['questions'][$key]); + } elseif (empty ($row['question'] ) || empty($row['SourceField']) ) { + unset($tpl_data['questions'][$key]); //removes static fields from showing up + } else { + $row['response'] = $Responses[$row['SourceField']]; + } + } + } + $smarty->assign($tpl_data); + $html = $smarty->fetch("directentry_review.tpl"); + return $html; + } + +} diff --git a/instruments/NDB_BVL_Instrument_radiology_review.class.inc b/instruments/NDB_BVL_Instrument_radiology_review.class.inc new file mode 100644 index 0000000..3a4ec62 --- /dev/null +++ b/instruments/NDB_BVL_Instrument_radiology_review.class.inc @@ -0,0 +1,115 @@ +formType="XIN"; + $this->form = new LorisForm('test_form'); + $this->page = $page; // page label (number or + // string - used by + // user-defined child classes) + + // set the object properties + $this->testName = "radiology_review"; // test_names.Test_name + $this->table = 'radiology_review'; // name of table containing + + + // data keyed by commentID + $this->commentID = $commentID; + + //$this->_requiredElements = array('Date_taken', 'Examiner'); + + $this->dateTimeFields=array("Date_taken"); + + // setup the form + $this->_setupForm(); + } + + /** + * method to build the HTML_Quickform object into a paged form + * + * @return void + * @access private + */ + function _setupForm(){ + + //Defines the call back function for HTML Quickform to use when validating the form. + $this->form->addFormRule(array(&$this,'XINValidate')); + + // display test name + $this->form->addElement('header', 'instrument_title', "Radiology Review Form"); + + // automatically adds examiner & date of administration + $this->_addMetadataFields(); + + $yes_no_option= array(NULL=>"", "no"=>"No", "yes"=>"Yes", "not_answered"=>"Not Answered"); + $normal_option= array(NULL=>"", "normal"=>"Normal","abnormal"=>"Abnormal", "atypical"=>"Atypical", "not_answered"=>"Not Answered"); + $exclusionaryOrNot = array(NULL=>"", "exclusionary"=>"Exclusionary", "non_exclusionary"=>"Non-Exclusionary", "not_answered"=>"Not Answered"); + + $this->form->addElement('header', null, '3D T1 MP-Rage'); + $this->form->addElement('select', 'Scan_done', 'Was an MRI performed?', $yes_no_option); + $this->form->addElement('static', null, 'If Yes?'); + + //These date fields were first not added to $this->dateTimeFields so they weren't saving. +// $this->form->addElement('date', 'MRI_date', 'Date of MRI acquisition'); +// $this->form->addElement('date', 'Review_date', 'Date of local radiology review'); + + $this->addDateElement('MRI', 'Date of MRI acquisition'); + $this->XINRegisterRule("MRI_date", array("Scan_done{@}=={@}yes"), "Please enter the scan information", "MRI_date_group"); + $this->addDateElement('Review', 'Date of local radiology review'); + $this->XINRegisterRule("Review_date", array("Scan_done{@}=={@}yes"), "Please enter the scan information", "Review_date_group"); + + $this->form->addElement('select', 'Review_results', 'Results of local radiology review?', $normal_option); + $this->XINRegisterRule("Review_results", array("Scan_done{@}=={@}yes"), "Please enter the scan information"); + + $this->form->addElement('select', 'abnormal_atypical_exclusionary', $this->indent . "If the results are ABNORMAL or ATYPICAL:", $exclusionaryOrNot); + $this->XINRegisterRule('abnormal_atypical_exclusionary', array('Review_results{@}=={@}abnormal|atypical'), "Please indicate if the Abnormal or Atypical review is Exclusionary or Not."); + + $this->form->addElement('static', null, $this->indent . $this->indent . 'If Abnormal or Atypical, describe incidental finding(s)'); +// $this->form->addElement('textarea', 'Incidental_findings', 'Description of incidental findings:'); + $this->addTextAreaElement('Incidental_findings', $this->indent . $this->indent . 'Description of incidental findings:', array("Review_results{@}=={@}abnormal"), "Please describe the abnormality"); + } // End Setup form + +/** + * adds metadata fields (such as Examiner and Date_taken) to the + * current form + * + * @return void + * @access private + */ + function _addMetadataFields() + { + $config =& NDB_Config::singleton(); + $this->dateOptions = array( + 'language' => 'en', + 'format' => 'YMd', + 'minYear' => $config->getSetting('startYear'), + 'maxYear' => $config->getSetting('endYear'), + 'addEmptyOption' => true, + 'emptyOptionValue' => null + ); + + $this->form->addElement('date', 'Date_taken', 'Date of Administration', $this->dateOptions); + + $examiners = $this->_getExaminerNames(); + $this->form->addElement('select', 'Examiner', 'Radiologist', $examiners); + + $this->form->addGroupRule('Date_taken', 'Date of Administration is required', 'required'); + + $this->form->registerRule('checkdate', 'callback', '_checkDate'); + $this->form->addRule('Date_taken', 'Date of Administration is invalid', 'checkdate'); + + $this->form->addRule('Examiner', 'Examiner is required', 'required'); + } +} +?> diff --git a/instruments/NDB_BVL_Instrument_tsi.class.inc b/instruments/NDB_BVL_Instrument_tsi.class.inc new file mode 100644 index 0000000..88a4208 --- /dev/null +++ b/instruments/NDB_BVL_Instrument_tsi.class.inc @@ -0,0 +1,528 @@ +"", "yes"=>"Yes", "no"=>"No", "not_answered"=>"Not Answered"); + + var $education_choices = array(null => "", "not_answered" => "Not Answered", "j_high" => "Junior High", "some_hs" => "Some Highschool", "some_college_12yr_degree" => "Some College 12yr Degree", "college_degree" => "College Degree", "some_grad_level" => "Some Graduate Level", "grad_degree" => "Graduate Degree"); + + /* + INSERT INTO test_names VALUES ('', 'tsi', 'Telephone Screening Interview', '0', '1'); + + INSERT INTO instrument_subtests VALUES('', 'tsi', 'tsi_page1', 'Exclusionary Factors', 1); + INSERT INTO instrument_subtests VALUES('', 'tsi', 'tsi_page2', 'Medical History or MRI/Sedation', 2); + INSERT INTO instrument_subtests VALUES('', 'tsi', 'tsi_page3', 'Medical Records / Research Staff', 3); + + INSERT INTO test_battery VALUES ('', 'tsi', '150', '210', 'Y', 'Screening', '1'); + INSERT INTO test_battery VALUES ('', 'tsi', '300', '420', 'Y', 'Screening', '2'); + INSERT INTO test_battery VALUES ('', 'tsi', '150', '420', 'Y', 'Screening', '3'); + + ALTER TABLE tsi ADD contact_date date; + ALTER TABLE tsi ADD contact_date_status enum('not_answered'); + + ALTER TABLE tsi ADD contacted_by varchar(255); + + ALTER TABLE tsi ADD complications_birth enum('yes','no','not_answered'); + + ALTER TABLE tsi ADD exc_inc_subject_meets_criteria enum('yes','no','not_answered'); + + */ + + /** + * Array of column names to be ignored by the double data entry conflict detector. + */ + /** + * sets up basic data, such as the HTML_Quickform object, and so on. + * + * @param string $commentID the CommentID identifying the data to load + * @param string $page if a multipage form, the page to show + * @return void + * @access public + */ + function setup($commentID, $page){ + $this->formType="XIN"; + $this->form = new LorisForm('test_form'); + $this->page = $page; // page label (number or + // string - used by + // user-defined child classes) + + // set the object properties + $this->testName = "tsi"; // test_names.Test_name + $this->table = 'tsi'; // name of database table corresponding to instrument + // data keyed by commentID + $this->commentID = $commentID; + + //The array of dates/timestamps to convert to database dates/timestamps + //Any HTML_Quickform date elements must be listed here + $this->dateTimeFields=array("Date_taken"); + + //The array of selects with multiple answers allowed + //Any HTML_Quickform multiple selects must be listed here + $this->_selectMultipleElements = array('candidate_race', 'mother_race', 'father_race'); + + // required fields for data entry completion status + $this->_requiredElements = array('Examiner', "ses_income", "neurological_problems_proband", "med_his_q_1_med_surgical_problems", "exc_inc_meets_criteria"); + $this->_doubleDataEntryDiffIgnoreColumns = array('CommentID', 'UserID', 'Testdate', 'Window_Difference', 'Candidate_Age', + 'Data_entry_completion_status', + 'in_utero_substance_proband', + 'in_utero_substance_amount_proband', + 'in_utero_time_exposure_proband', + 'asd_further_information', + 'med_his_q_3_surgery_describe', + 'med_his_q_5_medications_describe', + 'med_his_q_6_allergies_describe', + 'med_his_q_8_brain_MRI_results', + 'med_his_q_9_anethesia_problems', + 'med_his_q_10_nap', + 'med_his_q_11_nap_how_long', + 'further_information' + ); + + + // setup the form + $this->_setupForm(); + + } + + /** + * method to build the HTML_Quickform object into a paged form + * + * @return void + * @access private + */ + function _setupForm(){ + if(preg_match("/tsi(_page[0-9]+)/",$this->page,$matches)){ + call_user_method($matches[1], $this); + } else { + $this->_main(); + } + + //Defines the call back function for HTML Quickform to use when validating the form. + $this->form->addFormRule(array(&$this,'XINValidate')); + } + + /** + * generates the main page of the form. + * + * @return void + * @access private + * + */ + function _main(){ + // display test name + $this->form->addElement('header', 'instrument_title', +"Telephone Screening Interview"); + + // automatically adds examiner & date of administration + $this->_addMetadataFields(); + + $this->form->addElement('header', null, "Identifying information"); + $this->form->addElement('header', null, "This type of identifying information should not be entered in the database"); + + $config =& NDB_Config::singleton(); + + $this->dateOptions = array( + 'language' => 'en', + 'format' => 'YMd', + 'minYear' => $config->getSetting('startYear') - 20, //allows for siblings up to 20 years older than candidate + 'maxYear' => $config->getSetting('endYear'), + 'addEmptyOption' => true, + 'emptyOptionValue' => null + ); + + $nonAskedQuestions = array("Subject's Name", + "Subject ID:", + "Date of Contact:", + "Date of Birth:", + "Estimated Due Date: $this->indent Data entered during candidate registration", + "Contacted By:", + "Subject's Gender: $this->indent Subject's Age (in months):"); + foreach ($nonAskedQuestions as $field) { + if($field == "Date of Contact:"){ + $this->addDateElement("contact", $field); + } + elseif($field == "Contacted By:"){ + $this->form->addElement("text", "contacted_by", $field); + $this->XINRegisterRule ( "contacted_by", array ("contact_date_status{@}!={@}not_answered"), "Please enter person who contacted" ); + + } + else{ + $this->form->addElement('static', null, $field); + } + } + $this->form->addElement("select","is_infant_adopted","Is infant adopted?", $this->yesNo); + $this->form->addElement("select","is_infant_twin","Is infant a twin?", $this->yesNo); + //Ethnicity + $this->form->addElement("select", "child_ethnicity", "Candidate Ethnicity:", array(null=>"", "hispanic"=>"Hispanic", "non_hispanic"=>"Non-Hispanic", "not_answered"=>"Not Answered")); + + $this->form->addElement("select", "mother_ethnicity", "Mother Ethnicity:", array(null=>"", "hispanic"=>"Hispanic", "non_hispanic"=>"Non-Hispanic", "not_answered"=>"Not Answered")); + + $this->form->addElement("select", "father_ethnicity", "Father Ethnicity:", array(null=>"", "hispanic"=>"Hispanic", "non_hispanic"=>"Non-Hispanic", "not_answered"=>"Not Answered")); + + + //RACES + $this->form->addElement("select", "candidate_race", "Candidate Race:", array("white"=>"White", "black_african_american"=>"Black or African American", "asian"=>"Asian", "unknown_not_reported"=>"Unknown or not Reported", "american_indian_alaska_native"=>"American Indian / Alaska Native", "native_hawaiian_pacific_islander"=>"Native Hawaiian / Other Pacific Islander", "more_than_one_race"=>"More than one race (select all applicable)"), "multiple"); + $this->form->addRule("candidate_race", "Must answer this race question", 'required'); + + $this->form->addElement("select", "mother_race", "Mother Race:", array("white"=>"White", "black_african_american"=>"Black or African American", "asian"=>"Asian", "unknown_not_reported"=>"Unknown or not Reported", "american_indian_alaska_native"=>"American Indian / Alaska Native", "native_hawaiian_pacific_islander"=>"Native Hawaiian / Other Pacific Islander", "more_than_one_race"=>"More than one race (select all applicable)"), "multiple"); + $this->form->addRule("mother_race", "Must answer this race question", 'required'); + + $this->form->addElement("select", "father_race", "Father Race:", array("white"=>"White", "black_african_american"=>"Black or African American", "asian"=>"Asian", "unknown_not_reported"=>"Unknown or not Reported", "american_indian_alaska_native"=>"American Indian / Alaska Native", "native_hawaiian_pacific_islander"=>"Native Hawaiian / Other Pacific Islander", "more_than_one_race"=>"More than one race (select all applicable)"), "multiple"); + + $this->form->addRule("father_race", "Must answer this race question", 'required'); + $this->addTextElement("primary_language", "Primary language spoken in the home:"); + $this->form->addElement("static", null, "Parent's Information:

"); + + //Mother's Information + $this->form->addElement("static", null, "Mother's Information
"); + + /* + $this->addTextElement("mother_first_name", "First Name:"); + $this->form->addElement("text", "mother_middle_name", "Middle Name:"); + $this->addTextElement ("mother_last_name", "Last Name" ); + */ + + $this->form->addElement("static", null, "First Name:
"); + $this->form->addElement("static", null, "Middle Name:
"); + $this->form->addElement("static", null, "Last Name:
"); + + $this->addTextElement("mother_occupation", "Occupation:"); + + $this->form->addElement("select", "mother_education", "Highest Level of Education", array(null => "", "not_answered" => "Not Answered", "j_high" => "Junior High", "some_hs" => "Some Highschool", "high_school"=> "High School", "some_college" => "Some College/2yr Degree", "college_degree" => "College Degree", "some_grad_level" => "Some Graduate Level", "grad_degree" => "Graduate Degree")); + + $this->XINRegisterRule ( "mother_education_rule", array ("mother_education{@}!={@}"), "Please enter the mother's education" ); + + + $config =& NDB_Config::singleton(); + + $this->dateOptions = array( + 'language' => 'en', + 'format' => 'YMd', + 'minYear' => $config->getSetting('startYear') -80, //allows for siblings up to 20 years older than candidate + 'maxYear' => $config->getSetting('endYear'), + 'addEmptyOption' => true, + 'emptyOptionValue' => null + ); + + $this->addDateElement('mother_dob', "Date of Birth:"); + $this->addScoreColumn('mother_age_yrs', "Mother's age (in years) at time of candidate's
birth (Calculated)"); + //Father's Information + + $this->form->addElement("static", null, "

Father's Information
"); + + $this->form->addElement("static", null, "First Name:
"); + $this->form->addElement("static", null, "Middle Name:
"); + $this->form->addElement("static", null, "Last Name:
"); + + /* + $this->addTextElement("father_first_name", "First Name:"); + $this->form->addElement("text", "father_middle_name", "Middle Name:"); + $this->addTextElement ("father_last_name", "Last Name" ); + */ + + $this->addTextElement("father_occupation", "Occupation:"); + + $this->form->addElement("select", "father_education", "Highest Level of Education", array(null => "", "not_answered" => "Not Answered", "j_high" => "Junior High", "some_hs" => "Some Highschool", "high_school"=> "High School", "some_college" => "Some College/2yr Degree", "college_degree" => "College Degree", "some_grad_level" => "Some Graduate Level", "grad_degree" => "Graduate Degree")); + + $this->XINRegisterRule ( "father_education_rule", array ("father_education{@}!={@}"), "Please enter the father's education" ); + + $config =& NDB_Config::singleton(); + + $this->addDateElement('father_dob', "Date of Birth:"); + $this->addScoreColumn('father_age_yrs', "Father's age (in years) at time of candidate's
birth (Calculated)"); + + $this->form->addElement("static", null, "

"); + + $this->form->addElement("select", "ses_income", "SES:", array(null=>"", "less_than_25K"=>"less than $24,999", "25K-35K"=>"$25,000 - 34,999", "35K-50K"=>"$35,000 - 49,999", "50K-75K"=>"$50,000 - 74,999", "75K-100K"=>"$75,000 - 99,999", "100K-150K"=>"$100,000 - 149,999", "150K-200K"=>"$150,000 - 199,000", "over_200K"=>"over $200,000", "not_answered"=>"Not Answered")); + + $nonAskedQuestions = array("
Parent's Telephone:", + "Parent's Address:", + "$this->indent a) Older sibling", + "$this->indent b) Other siblings"); + foreach ($nonAskedQuestions as $field) { + $this->form->addElement('static', null, $field); + } + $this->form->addElement("static", null, "

Other siblings
"); + for($i = 1; $i<=10; $i++) { + $this->addScoreColumn(null, "Sibling $i"); + $field = 'age_sibling'.$i; $label = $this->indent. 'Age (Please indicate age in years):'; + $group [] = $this->createText($field, $label); + $this->addGroup($group, $field . "_group", $label, null, false); + $this->XINRegisterRule($field,array("$field{@}=={@}NEVER_REQUIRED"), $field . "_group"); + unset($group); + $this->addGroupRule($field . "_group", array(array(array("Value must be numeric.", 'numeric')))); + $this->addTextElement("dx_sibling".$i, $this->indent . "Dx:"); + $this->XINRegisterRule("dx_sibling".$i,array("$field{@}!={@}"), "Specify diagnoses for sibling $i", "dx_sibling".$i."_group"); + $this->addSelect('relation_sibling'.$i, $this->indent. "Specify relationship:", + array(''=>NULL,'half_sibling'=>'Half sibling', 'full_sibling'=>'Full sibling', + 'step_sibling'=>'Step sibling','not_answered'=>'Not Answered')); + $this->XINRegisterRule("relation_sibling".$i, array("$field{@}!={@}"), "Specify relationship for sibling $i"); + $this->addSelect('live_at_home_sibling'.$i, $this->indent. "Live in home:", array(''=>NULL,'yes'=>'Yes','no'=>'No','part_time'=>'Part time','not_answered'=>'Not Answered')); + $this->XINRegisterRule("live_at_home_sibling".$i, array("$field{@}!={@}"), "Specify if sibling $i lives at home or not"); + } + $this->form->addElement("static", null, "

"); + $this->form->addElement('static', null, "Nature of Referral:"); + } + + function _page1(){ + + $this->form->addElement('header', null, "Exclusionary Factors - ASD PROBANDs only"); + + $questionArray = array("neurological_problems_proband"=>"Any neurological problems such as cerebral palsy or tuberous sclerosis in PROBAND?", + "genetic_conditions_proband"=>"Diagnosed/suspected genetic conditions or syndromes?", + // "relative_with_disorder"=>"1st degree relative w/ MR, schizophrenia, bipolar or psychosis?", + "relative_mr" =>$this->indent."MR?", + "relative_schizophrenia"=>$this->indent."Schizophrenia?", + "relative_bipolar"=>$this->indent."Bipolar?", + "relative_psychosis"=>$this->indent."Psychosis?", + "relative_down_syndrome"=>$this->indent."Down Syndrome?", + "cns"=>"CNS problem/injury such as a head injury or meningitis?", + "tested_for_fragile_x"=>"Tested for Fragile X syndrome?", + "premature_birth_proband"=>"Premature birth (< 37 weeks)?"); + foreach($questionArray as $field=>$label) { + $this->form->addElement("select", $field, $label, $this->yesNo); + } + $this->addNumericElement("gestation_proband","Number of weeks of gestation"); + $this->form->addFormRule(array(&$this,'tsi_Rules')); + + $lb_options = array(null=>"",0=>"0 lbs",1=>"1 lbs",2=>"2 lbs",3=>"3 lbs",4=>"4 lbs",5=>"5 lbs",6=>"6 lbs", + 7=>"7 lbs",8=>"8 lbs",9=>"9 lbs",10=>"10 lbs",11=>"11 lbs",12=>"12 lbs",13=>"13 lbs",14=>"14 lbs",15=>"15 lbs"); + $oz_options = array(null=>"",0=>"0 oz",1=>"1 oz",2=>"2 oz",3=>"3 oz",4=>"4 oz",5=>"5 oz",6=>"6 oz", + 7=>"7 oz",8=>"8 oz",9=>"9 oz",10=>"10 oz",11=>"11 oz",12=>"12 oz",13=>"13 oz",14=>"14 oz",15=>"15 oz"); + + $group[] =& $this->form->createElement("select", "birthweight_proband_lb", null, $lb_options); + $group[] =& $this->form->createElement("select", "birthweight_proband_oz", null, $oz_options); + $group[] =& $this->form->createElement("select", "birthweight_proband_lb_status", null, array(null=>"","not_answered"=>"Not Answered")); + $this->form->addGroup($group,"weight_proband_group","Birth Weight? in lbs., oz.", $this->_GUIDelimiter, false); + $this->XINRegisterRule("birthweight_proband_lb", array("birthweight_proband_lb_status{@}=={@}"),"Required lbs and oz","weight_proband_group"); + $this->XINRegisterRule("birthweight_proband_oz", array("birthweight_proband_lb_status{@}=={@}"),"Required lbs and oz.","weight_proband_group"); + unset($group); + + $this->form->addElement('select', "complications_birth_proband", "Complications at birth?(ex. vaccum extraction)", $this->yesNo); + $this->form->addElement('select', "jaundice_proband", "Diagnosed with jaundice (hyperbilirubinemia)", $this->yesNo); + $this->form->addElement('select', "lighttherapy_proband",$this->indent. "If yes, light therapy required?", $this->yesNo); + $this->XINRegisterRule("lighttherapy_proband", array("lighttherapy_proband{@}=={@}NEVER_REQUIRED")); + + $this->addNumericElement('lighttherapy_days_proband', $this->indent."#days light therapy".$this->indent."
(exclusionary if >3 days of lights; requiring transfusion;or due to maternal/fetal RH incompatibility?)"); + $this->XINRegisterRule("lighttherapy_days_proband", array("lighttherapy_days_proband{@}=={@}NEVER_REQUIRED")); + + $this->form->addElement('select', "in_utero_exposure_proband", "Exposure during pregnancy to meds,tobacco, alcohol, or drugs
(Rx or not including herbal and prenatal vitamins)?", $this->yesNo); + + $questionArray = array("substance_proband"=>"Name of substance (get spelling):", + "substance_amount_proband"=>"Amount of substance at one time:", + "time_exposure_proband"=>"Time period of exposure (weeks of pregnancy):"); + foreach($questionArray as $field=>$label) { + $this->addTextElement("in_utero_" . $field, $this->indent . $label, array("in_utero_exposure_proband{@}=={@}yes"), "This field is required if there was in-utero exposure"); + } + $this->form->addElement('select', "breastfed_proband", "Was the proband breastfed?", $this->yesNo); + $this->addNumericElement("breastfed_proband_months",$this->indent."If yes, how long? Months:"); + $this->XINRegisterRule("breastfed_proband_months", array("breastfed_proband_months{@}=={@}NEVER_REQUIRED")); + + $this->addNumericElement("breastfed_proband_weeks",$this->indent."Weeks:"); + $this->XINRegisterRule("breastfed_proband_weeks", array("breastfed_proband_weeks{@}=={@}NEVER_REQUIRED")); + + $this->form->addElement('select', "medication_in_breastfeed_proband", "Medications while breastfeeding(Rx or not including vitamins)?", $this->yesNo); + + $questionArray = array("substance_breastfed_proband"=>"Name of substance (get spelling):", + "substance_breastfed_amount_proband"=>"Amount of substance at one time:", + "time_exposure_breastfed_proband"=>"Time period of exposure (weeks of pregnancy):"); + foreach($questionArray as $field=>$label) { + $this->addTextElement( $field, $this->indent . $label, array("medication_in_breastfeed_proband{@}=={@}yes"), "This field is required if there was taken during breast feeding"); + } + + + $this->addTextAreaElement("asd_further_information", "If yes to any of the above, obtain further information.", array("typical_or_asd_subject{@}=={@}asd")); + + $this->form->addElement("header", null, "Exclusionary Factors - Infant subjects"); + $questionArray = array( + "neurological_problems_subject"=>"Any neurological problems such as cerebral palsy or tuberous sclerosis in SUBJECT?", + "genetic_conditions_subject"=>"Diagnosed/suspected genetic conditions or syndromes?", + "seizures_or_neuro_disorder"=>"Do they have or have had they had seizures?", + "cns_problems"=>"CNS problems/injury such as head injury or meningitis?", + "congenital_heart_problems"=>"Congenital heart problems?", + "hearing_vision_impairments"=>"Significant hearing/vision problems?", + "diabetes"=>"Diabetes or gestational diabetes?", + "pregnancy_complication_subject"=>"Any complication during pregnancy?
(toxemia, high blood pressure, thyroid disorder)", + "complications_birth" => "Any complications at birth?(Ex: vaccum extraction?)", + "premature_birth"=>"Premature birth (<37 weeks )?", + "jaundice_subject"=>"Diagnosed with jaundice(hyperbilirubinemia)?", + "low_birth_weight"=>"Birth weight <4lbs. 6oz. (2000 grams)?", + "delivery_problems"=>"Problems during delivery?", + "tested_for_fragile_x_subject"=>"Tested for Fragile X syndrome?"); + foreach($questionArray as $field=>$label) { + $this->form->addElement("select", $field, $label, $this->yesNo); + if ($field == 'premature_birth') { + $this->addTextElement('weeks_gestation', $this->indent . "Number of weeks gestation", array("premature_birth{@}=={@}yes"), "If premature, how many gestational weeks?"); + } + if ($field == 'jaundice_subject') { + $this->form->addElement("select",'lighttherapy_subject',$this->indent."If yes, light therapy required?", $this->yesNo); + $this->XINRegisterRule("lighttherapy_subject", array("lighttherapy_subject{@}=={@}NEVER_REQUIRED")); + + $this->addNumericElement('lighttherapy_days_subject', $this->indent . "# of days
(exclusionary if >3days of lights;requiring transfusion;or due to maternal/fetal RH incompatibility)"); + $this->XINRegisterRule("lighttherapy_days_subject", array("lighttherapy_days_subject{@}=={@}NEVER_REQUIRED")); + + } + + } + $this->form->addElement('select', "pregnancy_exposure_subject", "Exposure during pregnancy to meds,tobacco, alcohol, or drugs
(Rx or not including herbal and prenatal vitamins)?", $this->yesNo); + + $questionArray = array("substance_subject"=>"Name of substance (get spelling):", + "substance_amount_subject"=>"Amount of substance at one time:", + "time_exposure_subject"=>"Time period of exposure (weeks of pregnancy):"); + foreach($questionArray as $field=>$label) { + $this->addTextElement("pregnancy_" . $field, $this->indent . $label, array("pregnancy_exposure_subject{@}=={@}yes"), "This field is required if there was exposure during pregnancy"); + } + + $this->form->addElement('select', "breastfed_subject", "Did you or are you breastfeeding your infant?", $this->yesNo); + $this->addNumericElement("breastfed_subject_months",$this->indent."If yes, how long? Months:"); + $this->XINRegisterRule("breastfed_subject_months", array("breastfed_subject_months{@}=={@}NEVER_REQUIRED")); + $this->addNumericElement("breastfed_subject_weeks",$this->indent."Weeks:"); + $this->XINRegisterRule("breastfed_subject_weeks", array("breastfed_subject_weeks{@}=={@}NEVER_REQUIRED")); + + $this->form->addElement('select', "medication_in_breastfeed_subject", "Medications while breastfeeding(Rx or not including vitamins)?", $this->yesNo); + + $questionArray = array("substance_breastfed_subject"=>"Name of substance (get spelling):", + "substance_breastfed_amount_subject"=>"Amount of substance at one time:", + "time_exposure_breastfed_subject"=>"Time period of exposure (weeks of pregnancy):"); + foreach($questionArray as $field=>$label) { + $this->addTextElement($field, $this->indent . $label, array("medication_in_breastfeed_subject{@}=={@}yes"), "This field is required if medication was taken during breast feeding"); + } + + $this->addTextAreaElement("further_information", "If yes to any of the above, obtain further information."); + $this->form->addFormRule(array(&$this, 'tsi_Rules')); + + } + + function _page2() { + $this->form->addElement('header', null, "Medical History Pertaining to MRI/Sedation"); + $this->form->addElement("select", "med_his_q_1_med_surgical_problems", "1. Has X ever had any serious medical or surgical problems?", $this->yesNo); + $this->form->addElement("select", "med_his_q_2_hospitalized", "2. Has X ever been hospitalized?", $this->yesNo); + $this->form->addElement("select", "med_his_q_3_surgery", "3. Surgery of any type?", $this->yesNo); + $this->addTextElement("med_his_q_3_surgery_describe", $this->indent . "Describe:", array("med_his_q_3_surgery{@}=={@}yes"), "This field is required if there was surgery"); + $this->form->addElement("select", "med_his_q_4_metal", "4. Does X have any metal plates, clips, etc. from surgery?", $this->yesNo); + $this->form->addElement("select", "med_his_q_5_medications", "5. Has X taken any medications over the last 12 months?", $this->yesNo); + $this->addTextElement("med_his_q_5_medications_describe", $this->indent . "If yes, list medications:", array("med_his_q_5_medications{@}=={@}yes"), "This field is required if there were medications"); + $this->form->addElement("select", "med_his_q_6_allergies", "6. Does X have any allergies to medicines?", $this->yesNo); + $this->addTextElement("med_his_q_6_allergies_describe", $this->indent . "If yes, describe:", array("med_his_q_6_allergies{@}=={@}yes"), "This field is required if there are allergies"); + $this->form->addElement("select", "med_his_q_7_doctor", "7. Has X seen a doctor over the last 12 months?", $this->yesNo); + $this->form->addElement("select", "med_his_q_8_brain_MRI", "8. Has X ever had an MRI of the brain?", $this->yesNo); + $this->form->addElement("select","med_his_q_8_brain_MRI_results", $this->indent . "If yes, what were the results?", array(null=>"","typical"=>"Typical","atypical"=>"Atypical","not_answered"=>"Not Answered")); + $this->XINRegisterRule ( "med_his_q_8_brain_MRI_results", array("med_his_q_8_brain_MRI{@}=={@}yes"),"Please specify results"); + $this->addTextElement("med_his_q_8_brain_MRI_notes", $this->indent . "Notes", array("med_his_q_8_brain_MRI_results{@}=={@}atypical")); + $this->form->addElement("select", "med_his_q_9_anethesia", "9. Has X ever had anesthesia or other forms of sedation?", $this->yesNo); + $this->form->addElement("select","med_his_q_9_anethesia_problems", $this->indent . "Problems with sedation?",$this->yesNo); + $this->XINRegisterRule ( "med_his_q_9_anethesia_problems", array("med_his_q_9_anethesia{@}=={@}yes"),"Please specify results"); + $this->addTextElement("med_his_q_9_anethesia_notes", $this->indent . "Notes", array("med_his_q_9_anethesia_problems{@}=={@}yes")); + + $this->addTextElement("med_his_q_10_nap", "10. Does X nap during the day?"); + $this->addTextElement("med_his_q_11_nap_how_long", "11. When & how long?"); + $this->form->addElement("select", "med_his_q_12_likelihood_sleep", "12. On a scale of 1-10, please rate the likelihood of your child sleeping through a scan.", array(null=>"", 1=>"1", 2=>"2", 3=>"3", 4=>"4", 5=>"5", 6=>"6", 7=>"7", 8=>"8", 9=>"9", 10=>"10", "not_answered"=>"Not Answered")); + + } + + function _page3() { + $this->form->addElement("header", null, "Medical Records"); + $this->addTextElement("city_of_birth", "City of Birth:"); + + $this->form->addElement('header', null, "This type of identifying information should not be entered in the database"); + $this->form->addElement("static", null, "Any diagnoses, dates and locations:"); + $this->form->addElement("static", null, "Has X had any other cognitive/developmental testing?"); + $this->form->addElement("static", null, $this->indent . "If yes, where was evaluation done/who conducted the evaluation?"); + $this->form->addElement("static", null, "Is any member of your family participating in a research study at this time?"); + + $this->form->addElement("header", null, "FOR RESEARCH STAFF TO COMPLETE"); + $this->form->addElement("static", null, "Exclusion/Inclusion"); + $this->form->addElement("select", "exc_inc_meets_criteria", "1. This proband meets any of the medical or neurological exclusion criteria for this study. (see Section A)", $this->yesNo); + $this->form->addElement("select", "exc_inc_subject_meets_criteria", "2. This subject meets any of the medical or neurological exclusion criteria for this study. (see Section B)", $this->yesNo); + $this->form->addElement("select", "exc_inc_contraindication", "3. This subject has evidence for contraindication to MRI. (see Section B)", $this->yesNo); + $this->form->addElement("static", null, "Disposition"); + $this->form->addElement("select", "disposition", "Proceed to next level beyond screening?", $this->yesNo); + } + function tsi_Rules($values) { + $errors = array(); + $gestation = intval ($values["gestation_proband"]); + if($gestation < 0 || $gestation > 45){ + $errors['gestation_proband'] = 'Please enter value between 0-45'; + + } + if( ($values["lighttherapy_days_proband"] == null || $values["lighttherapy_days_proband"] == '') + && ($values["lighttherapy_days_proband_status"] == null || $values["lighttherapy_days_proband_status"] == '') + && $values["lighttherapy_proband"] == "yes") { + $errors['lighttherapy_days_proband_group'] = 'Required' ; + } + if(($values["lighttherapy_proband"] == null || $values["lighttherapy_proband"] == '') + && $values["jaundice_proband"] === "yes") { + $errors['lighttherapy_proband'] = 'Required' ; + } + + if($values["breastfed_proband"] == "yes" && + ($values["breastfed_proband_months"] == null || $values["breastfed_proband_months"] == '') && + ($values["breastfed_proband_months_status"] == null || $values["breastfed_proband_months_status"] == '')) { + $errors['breastfed_proband_months_group'] = 'Required'; + } + if($values["breastfed_proband"] == "yes" && + ($values["breastfed_proband_weeks"] == null || $values["breastfed_proband_weeks"] == '') && + ($values["breastfed_proband_weeks_status"] == null || $values["breastfed_proband_weeks_status"] == "")) { + $errors['breastfed_proband_weeks_group'] = 'Required'; + } + if($values["breastfed_subject"] == "yes" && + ($values["breastfed_subject_months"] == null || $values["breastfed_subject_months"] == "" )&& + ($values["breastfed_subject_months_status"] == null || $values["breastfed_subject_months_status"] == "")) { + $errors['breastfed_subject_months_group'] = 'Required'; + } + if($values["breastfed_subject"] == "yes" && + ($values["breastfed_subject_weeks"] == null || $values["breastfed_subject_weeks"] == "") && + ($values["breastfed_subject_weeks_status"] == null || $values["breastfed_subject_weeks_status"] == "")) { + $errors['breastfed_subject_weeks_group'] = 'Required'; + } + if( ($values["lighttherapy_days_subject"] == null || $values["lighttherapy_days_subject"] == '') + && ($values["lighttherapy_days_subject_status"] == null || $values["lighttherapy_days_subject_status"] == '') + && $values["lighttherapy_subject"] == "yes") { + $errors['lighttherapy_days_subject_group'] = 'Required' ; + } + if(($values["lighttherapy_subject"] == null || $values["lighttherapy_subject"] == '') + && $values["jaundice_subject"] === "yes") { + $errors['lighttherapy_subject'] = 'Required' ; + } + + + return $errors; + } + function score() { + $db =& Database::singleton(); + + // Get the item scores + $record= $db->pselectRow("SELECT * FROM $this->table WHERE CommentID=:cid", array('cid'=>$this->getCommentID())); + $score = array(); + + if($record['relative_mr'] == 'yes' || $record['relative_schizophrenia']== 'yes' + || $record['relative_bipolar']=='yes' || $record['relative_psychosis']== 'yes') { + $score['relative_with_disorder'] = 'yes'; + } else { + $score['relative_with_disorder'] = 'no'; + } + //Find the candidate ID + //set for med_records_recruit - so watch out needs to be fixed + $candidate_dob = $db->pselectOne("SELECT c.DoB from session as s, flag as f, candidate c where f.sessionId = s.ID and + s.CandID=c.CandID and f.CommentID=:cid", array('cid'=>$this->getCommentID())); + + $score['mother_age_yrs'] = 'Unknown';$score['father_age_yrs'] = 'Unknown'; + $parents = array('mother','father'); + foreach ($parents as $parent) { + if (!empty($record[$parent.'_dob_date']) & !empty($candidate_dob) & + $record[$parent.'_dob_date']!='0000-00-00' + ) { + $age = Utility::calculateAge($record[$parent.'_dob_date'], + $candidate_dob); + $score[$parent.'_age_yrs'] = $age['year'] + round($age['mon']/12,2) + + round($age['day']/365, 2); + } + } + $result = $db->update($this->table, $score, array('CommentID'=>$this->getCommentID())); + } + +} +?> diff --git a/instruments/bmi.linst b/instruments/bmi.linst new file mode 100644 index 0000000..9085804 --- /dev/null +++ b/instruments/bmi.linst @@ -0,0 +1,22 @@ +table{@}bmi +title{@}BMI Calculator +date{@}Date_taken{@}Date of Administration{@}2006{@}2012 +static{@}Candidate_Age{@}Candidate Age (Months) +static{@}Window_Difference{@}Window Difference (+/- Days) +select{@}Examiner{@}Examiner{@}NULL=>'' +static{@}{@}Note: Please enter measurements in Standard OR Metric units +select{@}unit_classification{@}Choose unit of measurement{@}NULL=>''{-}'metric'=>'Metric'{-}'standard'=>'Standard' +static{@}{@}Standard units +numeric{@}height_feet{@}Your height (feet) : +select{@}height_feet_status{@}{@}NULL=>''{-}'not_answered'=>'Not Answered' +numeric{@}height_inches{@}Your height (inches) : +select{@}height_inches_status{@}{@}NULL=>''{-}'not_answered'=>'Not Answered' +numeric{@}weight_lbs{@}Your weight (lbs) : +select{@}weight_lbs_status{@}{@}NULL=>''{-}'not_answered'=>'Not Answered' +static{@}{@}Metric units +numeric{@}height_cms{@}Your height (cms) : +select{@}height_cms_status{@}{@}NULL=>''{-}'not_answered'=>'Not Answered' +numeric{@}weight_kgs{@}Your weight (kgs) : +select{@}weight_kgs_status{@}{@}NULL=>''{-}'not_answered'=>'Not Answered' +static{@}bmi{@}Your BMI +static{@}bmi_category{@}Your BMI Classification diff --git a/libraries/NDB_BVL_Instrument_Additional_Options.class.inc b/libraries/NDB_BVL_Instrument_Additional_Options.class.inc new file mode 100644 index 0000000..16dc7f3 --- /dev/null +++ b/libraries/NDB_BVL_Instrument_Additional_Options.class.inc @@ -0,0 +1,361 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris-Trunk/ + */ + + +/** + * Base class for instruments with additional options in Loris + * + * Throws PEAR errors. Also requires PEAR HTML_Quickform. + * + * @category Main + * @package Behavioural + * @author Tara Campbell + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris-Trunk/ + */ +class NDB_BVL_Instrument_Additional_Options extends NDB_BVL_Instrument +{ + + + /** + * options + */ + var $options = array( + 'dont_know' => "Don't know", + 'not_answered' => "No Answer", + 'refused_to_answer' => "Refused to Answer" + ); + + /** + * Wrapper to create a select drop-down list + * + * @param string $name The field name of this select dropdown + * @param string $label The label to attach to this dropdown + * @param array $options Options to pass to QuickForm for this + * select, adding 'not_answered' option + * @param array $optional Optional extra HTML attributes to add + * to the select + * + * @return none + */ + function addSelect($name, $label, $options, $optional=array()) + { + $options = array_merge( + $options,$this->options + ); + $optional = array_merge( + array('class' => 'form-control input-sm'), + $optional + ); + $this->form->addElement('select', $name, $label, $options, $optional); + } + + /** + * Creates a QuickForm Select dropdown but does not add it to the page + * + * @param string $field The field name for this select + * @param string $label The label to attach to the element + * @param array $options Extra options to pass to QuickForm + * @param array $attr Extra HTML attributes to add to the element + * + * @return HTML_QuickForm select element + */ + function createSelect( + $field, + $label, + $options=null, + $attr=array('class' => 'form-control input-sm') + ) { + $options = array_merge($options,$this->options); + return $this->form->createElement("select", $field, $label, $options, $attr); + } + + /** + * Wrapper to create a simple Yes, No, Null, Not Answered type + * of question + * + * @param string $field The database field in which the response + * will be stored + * @param string $label The question text to display + * @param array $rules {@} delimited rules + * @param string $rule_message rule message to display + * + * @return none + */ + function createYesNoElement($field, $label, $rules=array(), $rule_message='This field is required.'){ + $select = $this->createSelect($field, $label, array( + null => '', + 'yes' => 'Yes', + 'no' => 'No', + 'not_answered' => 'Not Answered', + ) + ); + + if (!empty($rules)) { + $this->XINRegisterRule($field, $rules, $rule_message); + } + return $select; + } + + /** + * Wrapper to create a static label to the current page + * + * @param string $label The text to add to the label. + * + * @return none + */ + function createLabel($label) + { + return $this->form->createElement('static', null, null, $label); + } + + /** + * Wrapper to create an Hour/Minute field, with an accompanying status field. + * + * @param string $field Name given to the HTML QuickForm Element + * being created + * @param string $label The question text to display + * @param array $rules Additional rules to apply to the element + * being added,{@} delimited + * @param string $rule_message Message to display upon rule violation. + * + * @return none + */ + function addHourMinElement( + $field, + $label, + $rules = array(), + $rule_message = null + ) { + if ($rule_message === null) { + $rule_message = "You are required to select a status " + . "if you want to leave this time blank."; + } + $group[] =& $this->createDate( + $field, + null, + array( + 'language' => 'en', + 'format' => 'H:i', + 'addEmptyOption' => true, + ) + ); + $group[] =& $this->createSelect( + $field . "_status", + "", + array_merge( + array(null=> ''), $this->options) + ); + $this->addGroup($group, $field . "_group", $label, null, false); + $this->XINRegisterRule( + $field, + array_merge($rules, array($field . '_status{@}=={@}')), + $rule_message, + $field . '_group' + ); + unset($group); + } + + /** + * Adds a date group with a status box and appropriate rule + * Note: $this->dateOptions must be defined by the subclass calling this + * wrapper function + * + * @param string $name Name prepended to the HTMLQuickform element + * @param string $label Element label + * @param array $options optional override of class's dateOptions + * + * @return none + */ + function addDateElement($name, $label, $options = null) + { + if ($options === null) { + $options = $this->dateOptions; + } + $group[] = $this->createDate( + $name . "_date", + $label, + $options, + array( + 'class' => 'form-control input-sm '.$name."_date", + 'style' => 'max-width:33%; display:inline-block;', + ) + ); + $this->WrapperDateWithStatusElements[$name . "_date"] = $group[0]; + if (!in_array($name . "_date", $this->dateTimeFields)) { + $this->dateTimeFields[] = $name . "_date"; + } + $group[] = $this->createSelect( + $name . "_date_status", + null, + array_merge( + array(null=> ''), $this->options), + array('class' => 'form-control input-sm not-answered') + ); + $this->addGroup( + $group, + $name . "_date_group", + $label, + $this->_GUIDelimiter, + false + ); + unset($group); + $this->XINRegisterRule( + $name . "_date", + array($name . "_date_status{@}=={@}"), + "A Date, or Not Answered is required.", + $name . "_date_group" + ); + } + + /** + * Adds metadata fields (such as Examiner and Date_taken) to the + * current form + * + * @return void + * @access private + */ + function _addMetadataFields() + { + $config =& NDB_Config::singleton(); + $dateOptions = array( + 'language' => 'en', + 'format' => 'dMY', + 'minYear' => $config->getSetting('startYear'), + 'maxYear' => $config->getSetting('endYear'), + 'addEmptyOption' => true, + 'emptyOptionValue' => null, + ); + + $this->dateOptions = $dateOptions; + + $this->addBasicDate('Date_taken', 'Date of Administration', $dateOptions); + + if (strrpos($this->testName, '_proband') === false) { + $this->addScoreColumn( + 'Candidate_Age', + 'Candidate Age (Months)', + 'Please enter date of administration and press Save' + ); + $this->addScoreColumn( + 'Window_Difference', + 'Window Difference (+/- Days)', + 'Please enter date of administration and press Save' + ); + } + $examiners = $this->_getExaminerNames(); + parent::addSelect('Examiner', 'Examiner', $examiners); + + $this->addGroupRule( + 'Date_taken', + 'Date of Administration is required', + 'required' + ); + + $this->form->registerRule('checkdate', 'callback', '_checkDateTaken'); + $this->addRule( + 'Date_taken', + 'Date of Administration is invalid', + 'checkdate' + ); + + $this->addRule('Examiner', 'Examiner is required', 'required'); + } + + /** + * Wrapper to create a text field with an accompanying status field. + * + * @param string $field The database field in which the response + * will be stored + * @param string $label The question text to display + * @param array $rules rules with values and comparisson operators + * {@} seperated. + * Example: array( + * 'q_40{@}=={@}1_yes', + * 'q_40_b{@}=={@}1_yes' + * ) + * @param string $rule_message rule message to display + * @param array $refusals options in the refusal select (ie. Refusal, + * Unknown, Not Answered). + * + * @return none + */ + function addTextElement( + $field, + $label, + $rules=array(), + $rule_message='This field is required.' + + ) { + $group[] = $this->createText($field, $label); + $this->WrapperTextElements[$field] = $group[0]; + $group[] = $this->createSelect( + $field."_status", + "", + array_merge( + array(null=> ''), $this->options), + array('class' => 'form-control input-sm not-answered') + ); + + $this->addGroup($group, $field.'_group', $label, null, false); + unset($group); + $rules_array = array_merge(array($field.'_status{@}=={@}'), $rules); + $this->XINRegisterRule($field, $rules_array, $rule_message, $field.'_group'); + } + + /** + * Wrapper to create a field that only accepts a number, with an + * accompanying status field. + * + * @param string $field The database field in which the response + * will be stored + * @param string $label The question text to display + * @param unknown $options Does not appear to be used? + * + * @return none + */ + function addNumericElement($field, $label, $options = null) + { + $group[] = $this->createText($field, $label); + $this->WrapperNumericElements[$field] = $group[0]; + $group[] = $this->createSelect( + $field . "_status", + null, + array_merge(array(null => ''), $this->options), + array('class' => 'form-control input-sm not-answered') + ); + $this->addGroup($group, $field . "_group", $label, null, false); + unset($group); + $this->addGroupRule( + $field . "_group", + array(array(array("Value must be numeric.", 'numeric'))) + ); + $this->XINRegisterRule( + $field, + array($field . '_status{@}=={@}'), + 'This field is required', + $field . '_group' + ); + } + + function italics($str){ + return "".$str.""; + } + function underline($str){ + return "".$str.""; + } + var $linebreak = "
"; + +} + +?> \ No newline at end of file diff --git a/libraries/NDB_BVL_Instrument_No_Additional_Options.class.inc b/libraries/NDB_BVL_Instrument_No_Additional_Options.class.inc new file mode 100644 index 0000000..7a2b7ea --- /dev/null +++ b/libraries/NDB_BVL_Instrument_No_Additional_Options.class.inc @@ -0,0 +1,155 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris-Trunk/ + */ +/** + * Base class for instruments with no additional options in Loris + * + * Throws PEAR errors. Also requires PEAR HTML_Quickform. + * + * @category Main + * @package Behavioural + * @author Tara Campbell + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris-Trunk/ + */ +class NDB_BVL_Instrument_No_Additional_Options extends NDB_BVL_Instrument +{ + /** + * Adds metadata fields (such as Examiner and Date_taken) to the + * current form + * + * @return void + * @access private + */ + function _addMetadataFields() + { + $config =& NDB_Config::singleton(); + $dateOptions = array( + 'language' => 'en', + 'format' => 'dMY', + 'minYear' => $config->getSetting('startYear'), + 'maxYear' => $config->getSetting('endYear'), + 'addEmptyOption' => true, + 'emptyOptionValue' => null, + ); + + $this->dateOptions = $dateOptions; + + $this->addBasicDate('Date_taken', 'Date of Administration', $dateOptions); + + if (strrpos($this->testName, '_proband') === false) { + $this->addScoreColumn( + 'Candidate_Age', + 'Candidate Age (Months)', + 'Please enter date of administration and press Save' + ); + $this->addScoreColumn( + 'Window_Difference', + 'Window Difference (+/- Days)', + 'Please enter date of administration and press Save' + ); + } + $examiners = $this->_getExaminerNames(); + parent::addSelect('Examiner', 'Examiner', $examiners); + + $this->addGroupRule( + 'Date_taken', + 'Date of Administration is required', + 'required' + ); + + $this->form->registerRule('checkdate', 'callback', '_checkDateTaken'); + $this->addRule( + 'Date_taken', + 'Date of Administration is invalid', + 'checkdate' + ); + + $this->addRule('Examiner', 'Examiner is required', 'required'); + } + + /** + * Wrapper to create a month year date field + * + * This is only used in instruments and not in NDB_Page because + * special logic is needed in the _saveValues to deal with the + * monthYear fields. + * + * @param string $field Name of the field + * @param string $label Element label + * @param array $options optional override of class's dateOptions + * + * @return none + */ + function addMonthYear($field, $label, $options=array()) + { + $config =& NDB_Config::singleton(); + $dateOptions = array( + 'language' => 'en', + 'format' => 'MY', + 'minYear' => 1900, + 'maxYear' => $config->getSetting('endYear'), + 'addEmptyOption' => true, + 'emptyOptionValue' => null, + ); + + $this->addBasicDate($field, $label, $dateOptions); + $this->monthYearFields[] = $field; + } + + /** + * Wrapper to create a year month date field + * + * This is only used in instruments and not in NDB_Page because + * special logic is needed in the _saveValues to deal with the + * monthYear fields. + * + * @param string $field Name of the field + * @param string $label Element label + * @param array $options optional override of class's dateOptions + * + * @return none + */ + function addYearMonth($field, $label, $options=array()) + { + $config =& NDB_Config::singleton(); + $dateOptions = array( + 'language' => 'en', + 'format' => 'YM', + 'minYear' => 1900, + 'maxYear' => $config->getSetting('endYear'), + 'addEmptyOption' => true, + 'emptyOptionValue' => null, + ); + + $this->addBasicDate($field, $label, $dateOptions); + $this->monthYearFields[] = $field; + } + + /** + * Wrapper to create a field that only accepts a number, with an + * accompanying status field. + * + * @param string $field The database field in which the response + * will be stored + * @param string $label The question text to display + * @param unknown $options Does not appear to be used? + * + * @return none + */ + function addNumericElement($field, $label, $options = null) + { + $this->addBasicText($field, $label); + $this->addRule($field, "Value must be numeric.", 'numeric'); + } +} +?> \ No newline at end of file diff --git a/modules/document_repository/READ_ME b/modules/document_repository/READ_ME new file mode 100644 index 0000000..52481f0 --- /dev/null +++ b/modules/document_repository/READ_ME @@ -0,0 +1,2 @@ +This module is overwritten to allow for the demenstration of uploading a file +to the doc repo without saving the file to the database diff --git a/modules/document_repository/ajax/GetFile.php b/modules/document_repository/ajax/GetFile.php new file mode 100644 index 0000000..8d71994 --- /dev/null +++ b/modules/document_repository/ajax/GetFile.php @@ -0,0 +1,61 @@ + + * @license Loris license + * @link https://github.com/aces/Loris-Trunk + * + */ + +$user =& User::singleton(); +if (!$user->hasPermission('document_repository_view') && !$user->hasPermission('document_repository_delete')) { + header("HTTP/1.1 403 Forbidden"); + exit; +} + +// Load config file and ensure paths are correct +set_include_path( + get_include_path() . ":" . + __DIR__ . "/../project/libraries:" . + __DIR__ . "/../php/libraries" +); + +// Ensures the user is logged in, and parses the config file. +require_once "NDB_Client.class.inc"; +$client = new NDB_Client(); +$client->initialize("../project/config.xml"); + +// Checks that config settings are set +$config =& NDB_Config::singleton(); + +$File = $_GET['File']; + +// Make sure that the user isn't trying to break out of the $path by +// using a relative filename. +// No need to check for '/' since all downloads are relative to $basePath +if (strpos("..", $File) !== false) { + error_log("ERROR: Invalid filename"); + header("HTTP/1.1 400 Bad Request"); + exit(4); +} + + +$FullPath = __DIR__ . "/../user_uploads/$File"; + +if (!file_exists($FullPath)) { + error_log("ERROR: File $FullPath does not exist"); + header("HTTP/1.1 404 Not Found"); + exit(5); +} + +$fp = fopen($FullPath, 'r'); +fpassthru($fp); +fclose($fp); +?> diff --git a/modules/document_repository/ajax/addCategory.php b/modules/document_repository/ajax/addCategory.php new file mode 100644 index 0000000..a2bd742 --- /dev/null +++ b/modules/document_repository/ajax/addCategory.php @@ -0,0 +1,77 @@ + + * @license Loris license + * @link https://www.github.com/Jkat/Loris-Trunk/ + */ + +$user =& User::singleton(); +if (!$user->hasPermission('document_repository_view') && !$user->hasPermission('document_repository_delete')) { + header("HTTP/1.1 403 Forbidden"); + exit; +} + +set_include_path(get_include_path().":../../project/libraries:../../php/libraries:"); +require_once "NDB_Client.class.inc"; +require_once "NDB_Config.class.inc"; +require_once "Email.class.inc"; + +$factory = NDB_Factory::singleton(); +$baseURL = $factory->settings()->getBaseURL(); +$client = new NDB_Client(); +$client->initialize("../../project/config.xml"); + +$config = NDB_Config::singleton(); + +// create Database object +$DB =& Database::singleton(); + +if (empty($_POST['category_name']) && $_POST['category_name'] !== '0') { + header("HTTP/1.1 400 Bad Request"); + exit; +} else { + $category_name = $_POST['category_name']; +} +if ($_POST['parent_id'] !== '') { + if (isset($_POST['parent_id']) || is_numeric($_POST['parent_id'])) { + $parent_id = $_POST['parent_id']; + } else { + error_log("Invalid parent id!"); + die(); + } +} +if ($_POST['comments'] !== '') { + $comments = $_POST['comments']; +} + +$user =& User::singleton(); +//if user has document repository permission +if ($user->hasPermission('document_repository_view') || $user->hasPermission('document_repository_delete')) { + $DB->insert( + "document_repository_categories", + array("category_name" => $category_name, + "parent_id" => $parent_id, + "comments" => $comments) + ); + + + $msg_data['newCategory'] = $baseURL . "/document_repository/"; + $msg_data['category'] = $category_name; + $msg_data['study'] = $config->getSetting('title'); + + $Doc_Repo_Notification_Emails = $DB->pselect( + "SELECT Email from users where Active='Y' and Doc_Repo_Notifications='Y' and UserID<>:uid", + array("uid"=>$user->getUsername()) + ); + foreach ($Doc_Repo_Notification_Emails as $email) { + Email::send($email['Email'], 'document_repository.tpl', $msg_data); + } +} + +?> diff --git a/modules/document_repository/ajax/categoryEdit.php b/modules/document_repository/ajax/categoryEdit.php new file mode 100644 index 0000000..3c08e99 --- /dev/null +++ b/modules/document_repository/ajax/categoryEdit.php @@ -0,0 +1,48 @@ + + * @license Loris license + * @link https://www.github.com/Jkat/Loris-Trunk/ + */ + +$user =& User::singleton(); +if (!$user->hasPermission('document_repository_view') && !$user->hasPermission('document_repository_delete')) { + header("HTTP/1.1 403 Forbidden"); + exit; +} + +set_include_path(get_include_path().":../../project/libraries:../../php/libraries:"); +require_once "NDB_Client.class.inc"; +$client = new NDB_Client(); +$client->initialize("../../project/config.xml"); + +// create Database object +$DB =& Database::singleton(); + +if (get_magic_quotes_gpc()) { + // Magic quotes adds \ to description, get rid of it. + $comments = stripslashes($_REQUEST['comments']); +} else { + // Magic quotes is off, so we can just directly use the description + // since insert() will use a prepared statement. + $comments = $_REQUEST['comments']; +} + +$user =& User::singleton(); + +//if user has document repository permission +if ($user->hasPermission('document_repository_view') || $user->hasPermission('document_repository_delete')) { + $DB->update( + 'document_repository_categories', + array('comments'=>$comments), + array('id'=>$_REQUEST['id']) + ); +} + +?> diff --git a/modules/document_repository/ajax/documentDelete.php b/modules/document_repository/ajax/documentDelete.php new file mode 100644 index 0000000..731b64a --- /dev/null +++ b/modules/document_repository/ajax/documentDelete.php @@ -0,0 +1,52 @@ +hasPermission('document_repository_delete')) { + header("HTTP/1.1 403 Forbidden"); + exit; +} + +set_include_path(get_include_path().":../../project/libraries:../../php/libraries:"); +require_once "NDB_Client.class.inc"; +require_once "NDB_Config.class.inc"; +require_once "Email.class.inc"; +$client = new NDB_Client(); +$client->initialize("../../project/config.xml"); +$factory = NDB_Factory::singleton(); +$baseURL = $factory->settings()->getBaseURL(); + +$config = NDB_Config::singleton(); + +// create Database object +$DB =& Database::singleton(); + +$rid = $_POST['id']; + +$fileName = $DB->pselectOne("Select File_name from document_repository where record_id =:identifier", + array(':identifier' => $rid)); +$userName = $DB->pselectOne("Select uploaded_by from document_repository where record_id =:identifier", + array(':identifier'=> $rid)); +$dataDir = $DB->pselectOne("Select Data_dir from document_repository where record_id =:identifier", + array(':identifier'=> $rid)); + +$user =& User::singleton(); + +//if user has document repository delete permission +if ($user->hasPermission('document_repository_delete')) { + $DB->delete("document_repository", array("record_id" => $rid)); + $msg_data['deleteDocument'] = $baseURL. "/document_repository/"; + $msg_data['document'] = $fileName; + $msg_data['study'] = $config->getSetting('title'); + $query_Doc_Repo_Notification_Emails = "SELECT Email from users where Active='Y' and Doc_Repo_Notifications='Y' and UserID<>:uid"; + $Doc_Repo_Notification_Emails = $DB->pselect($query_Doc_Repo_Notification_Emails, array("uid"=>$user->getUsername())); + foreach ($Doc_Repo_Notification_Emails as $email) { + Email::send($email['Email'], 'document_repository.tpl', $msg_data); + } +} + +$path = __DIR__ . "/../user_uploads/$dataDir"; + +if (file_exists($path)) + unlink($path); + +?> diff --git a/modules/document_repository/ajax/documentEditUpload.php b/modules/document_repository/ajax/documentEditUpload.php new file mode 100755 index 0000000..2b80e7e --- /dev/null +++ b/modules/document_repository/ajax/documentEditUpload.php @@ -0,0 +1,106 @@ +hasPermission('document_repository_view') && !$userSingleton->hasPermission('document_repository_delete')) { + header("HTTP/1.1 403 Forbidden"); + exit; +} + +set_include_path(get_include_path().":../../project/libraries:../../php/libraries:"); +require_once "NDB_Client.class.inc"; +require_once "NDB_Config.class.inc"; +require_once "Email.class.inc"; +$client = new NDB_Client(); +$client->initialize("../../project/config.xml"); +$factory = NDB_Factory::singleton(); +$baseURL = $factory->settings()->getBaseURL(); + +$config = NDB_Config::singleton(); + +// create Database object +$DB =& Database::singleton(); + +$action = $_POST['action']; + +//if user has document repository permission +if ($userSingleton->hasPermission('document_repository_view') || $userSingleton->hasPermission('document_repository_delete')) { + if ($action == 'upload') { + $puser = $_POST['user']; + $category = $_POST['category']; + $site = $_POST['site']; + $instrument = $_POST['instrument']; + $pscid = $_POST['pscid']; + $visit = $_POST['visit']; + $comments = $_POST['comments']; + $version = $_POST['version']; + + $fileSize = $_FILES["file"]["size"]; + $fileName = $_FILES["file"]["name"]; + $fileType = end((explode(".", $fileName))); + + // __DIR__ is the document_repository ajax directory + // when this script is executing. Go up a level to the + // document_repository module directory, and use a + // user_uploads directory as a base for user uploads + $base_path = __DIR__ . "/../user_uploads/"; + $fileBase = $puser . "/" . $fileName; + + if (!file_exists($base_path . $puser)) { + mkdir($base_path . $puser, 0777); + } + + + $target_path = $base_path . $fileBase; + + if (1) { + exec("touch " + escapeshellarg($target_path)); + $success = $DB->insert('document_repository', + array('File_category'=>$category, 'For_site'=>$site, + 'comments'=>$comments, 'version'=>$version, 'File_name'=>$fileName, + 'File_size'=>$fileSize, 'Data_dir'=>$fileBase, 'uploaded_by'=>$puser, + 'Instrument'=>$instrument, 'PSCID'=>$pscid, 'visitLabel'=>$visit, + 'File_type'=>$fileType)); + $msg_data['newDocument'] = $baseURL . "/document_repository/"; + $msg_data['document'] = $fileName; + $msg_data['study'] = $config->getSetting('title'); + $query_Doc_Repo_Notification_Emails = "SELECT Email from users where Active='Y' and Doc_Repo_Notifications='Y' and UserID<>:uid"; + $Doc_Repo_Notification_Emails = $DB->pselect($query_Doc_Repo_Notification_Emails, array("uid"=>$userSingleton->getUsername())); + foreach ($Doc_Repo_Notification_Emails as $email) { + Email::send($email['Email'], 'document_repository.tpl', $msg_data); + } + header("Location: $baseURL/document_repository/?uploadSuccess=true"); + } else { + echo "There was an error uploading the file"; + } + } elseif ($action == 'edit') { + $id = $_POST['idEdit']; + $category = $_POST['categoryEdit']; + $instrument = $_POST['instrumentEdit']; + $site = $_POST['siteEdit']; + $pscid = $_POST['pscidEdit']; + $visit = $_POST['visitEdit']; + $comments = $_POST['commentsEdit']; + $version = $_POST['versionEdit']; + + if(empty($category) && $category !== '0'){ + header("HTTP/1.1 400 Bad Request"); + exit; + } + + $values = array('File_category' => $category, 'Instrument' => $instrument, 'For_site' => $site, + 'PSCID' => $pscid, 'visitLabel' => $visit, 'comments' => $comments, 'version' => $version); + $DB->update('document_repository', $values, array('record_id'=>$id)); + + $fileName = $DB->pselectOne("select File_name from document_repository where record_id=:record_id", + array('record_id'=>$id)); + $msg_data['updatedDocument'] = $baseURL . "/document_repository/"; + $msg_data['document'] = $fileName; + $query_Doc_Repo_Notification_Emails = "SELECT Email from users where Active='Y' and Doc_Repo_Notifications='Y' and UserID<>:uid"; + $Doc_Repo_Notification_Emails = $DB->pselect($query_Doc_Repo_Notification_Emails, array("uid"=>$userSingleton->getUsername())); + foreach ($Doc_Repo_Notification_Emails as $email) { + Email::send($email['Email'], 'document_repository.tpl', $msg_data); + } + } +} + +?> diff --git a/modules/document_repository/ajax/documentEditUpload.php.bak b/modules/document_repository/ajax/documentEditUpload.php.bak new file mode 100755 index 0000000..4918119 --- /dev/null +++ b/modules/document_repository/ajax/documentEditUpload.php.bak @@ -0,0 +1,109 @@ +hasPermission('document_repository_view') && !$userSingleton->hasPermission('document_repository_delete')) { + header("HTTP/1.1 403 Forbidden"); + exit; +} + +set_include_path(get_include_path().":../../project/libraries:../../php/libraries:"); +require_once "NDB_Client.class.inc"; +require_once "NDB_Config.class.inc"; +require_once "Email.class.inc"; +$client = new NDB_Client(); +$client->initialize("../../project/config.xml"); + +$config = NDB_Config::singleton(); + +// create Database object +$DB =& Database::singleton(); +if (Utility::isErrorX($DB)) { + print "Could not connect to database: ".$DB->getMessage()."
\n"; die(); +} + +$action = $_POST['action']; + +if (Utility::isErrorX($userSingleton)) { + return PEAR::raiseError("User Error: ".$userSingleton->getMessage()); +} + +//if user has document repository permission +if ($userSingleton->hasPermission('document_repository_view') || $userSingleton->hasPermission('document_repository_delete')) { + if ($action == 'upload') { + $user = $_POST['user']; + $category = $_POST['category']; + $site = $_POST['site']; + $instrument = $_POST['instrument']; + $pscid = $_POST['pscid']; + $visit = $_POST['visit']; + $comments = $_POST['comments']; + $version = $_POST['version']; + + $fileSize = $_FILES["file"]["size"]; + $fileName = $_FILES["file"]["name"]; + + // __DIR__ is the document_repository ajax directory + // when this script is executing. Go up a level to the + // document_repository module directory, and use a + // user_uploads directory as a base for user uploads + $base_path = __DIR__ . "/../user_uploads/"; + $fileBase = $user . "/" . $fileName; + + if (!file_exists($base_path . $user)) { + mkdir($base_path . $user, 0777); + } + + + $target_path = $base_path . $fileBase; + + if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_path)) { + $success = $DB->insert('document_repository', + array('File_category'=>$category, 'For_site'=>$site, + 'comments'=>$comments, 'version'=>$version, 'File_name'=>$fileName, + 'File_size'=>$fileSize, 'Data_dir'=>$fileBase, 'uploaded_by'=>$user, + 'Instrument'=>$instrument, 'PSCID'=>$pscid, 'visitLabel'=>$visit)); + $www = $config->getSetting('www'); + $msg_data['newDocument'] = $www['url'] . "/main.php?test_name=document_repository"; + $msg_data['document'] = $fileName; + $query_Doc_Repo_Notification_Emails = "SELECT Email from users where Active='Y' and Doc_Repo_Notifications='Y' and UserID<>:uid"; + $Doc_Repo_Notification_Emails = $DB->pselect($query_Doc_Repo_Notification_Emails, array("uid"=>$userSingleton->getUsername())); + foreach ($Doc_Repo_Notification_Emails as $email) { + Email::send($email['Email'], 'document_repository.tpl', $msg_data); + } + header("Location: ../main.php?test_name=document_repository&uploadSuccess=true"); + } else { + echo "There was an error uploading the file"; + } + } elseif ($action == 'edit') { + $id = $_POST['idEdit']; + $category = $_POST['categoryEdit']; + $instrument = $_POST['instrumentEdit']; + $site = $_POST['siteEdit']; + $pscid = $_POST['pscidEdit']; + $visit = $_POST['visitEdit']; + $comments = $_POST['commentsEdit']; + $version = $_POST['versionEdit']; + + if(empty($category) && $category !== '0'){ + header("HTTP/1.1 400 Bad Request"); + exit; + } + + $values = array('File_category' => $category, 'Instrument' => $instrument, 'For_site' => $site, + 'PSCID' => $pscid, 'visitLabel' => $visit, 'comments' => $comments, 'version' => $version); + $DB->update('document_repository', $values, array('record_id'=>$id)); + + $fileName = $DB->pselectOne("select File_name from document_repository where record_id=:record_id", + array('record_id'=>$id)); + $www = $config->getSetting('www'); + $msg_data['updatedDocument'] = $www['url'] . "/main.php?test_name=document_repository"; + $msg_data['document'] = $fileName; + $query_Doc_Repo_Notification_Emails = "SELECT Email from users where Active='Y' and Doc_Repo_Notifications='Y' and UserID<>:uid"; + $Doc_Repo_Notification_Emails = $DB->pselect($query_Doc_Repo_Notification_Emails, array("uid"=>$userSingleton->getUsername())); + foreach ($Doc_Repo_Notification_Emails as $email) { + Email::send($email['Email'], 'document_repository.tpl', $msg_data); + } + } +} + +?> diff --git a/modules/document_repository/ajax/getFileData.php b/modules/document_repository/ajax/getFileData.php new file mode 100644 index 0000000..547a200 --- /dev/null +++ b/modules/document_repository/ajax/getFileData.php @@ -0,0 +1,21 @@ +hasPermission('document_repository_view') && !$user->hasPermission('document_repository_delete')) { + header("HTTP/1.1 403 Forbidden"); + exit; +} + +set_include_path(get_include_path().":../../project/libraries:../../php/libraries:"); +require_once "NDB_Client.class.inc"; +$client = new NDB_Client(); +$client->initialize("../../project/config.xml"); + +// create Database object +$DB =& Database::singleton(); + +$result = $DB->pselectRow("SELECT * FROM document_repository where record_id =:identifier", array(':identifier'=> $_GET['id'])); + +echo json_encode($result); + +?> diff --git a/modules/document_repository/css/document_repository.css b/modules/document_repository/css/document_repository.css new file mode 100644 index 0000000..e36abb5 --- /dev/null +++ b/modules/document_repository/css/document_repository.css @@ -0,0 +1,338 @@ +.tree { + min-height:20px; + padding:19px; + margin-bottom:20px; + background-color:#fbfbfb; + border:1px solid #999; + -webkit-border-radius:4px; + -moz-border-radius:4px; + border-radius:4px; + -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05) +} +.tree li { + list-style-type:none; + margin:0; + padding:10px 5px 0 5px; + position:relative +} +.tree li::before, .tree li::after { + content:''; + left:-20px; + position:absolute; + right:auto +} +.tree li::before { + border-left:1px solid #999; + bottom:50px; + height:100%; + top:0; + width:1px +} +.tree li::after { + border-top:1px solid #999; + height:20px; + top:25px; + width:25px +} +.tree li span { + -moz-border-radius:5px; + -webkit-border-radius:5px; + border:1px solid #999; + border-radius:5px; + display:inline-block; + padding:3px 8px; + text-decoration:none +} +.tree li.parent_li>span { + cursor:pointer +} +.tree>ul>li::before, .tree>ul>li::after { + border:0 +} +.tree li:last-child::before { + height:30px +} +.tree li.parent_li>span:hover, .tree li.parent_li>span:hover+ul li span { + background:#eee; + border:1px solid #94a0b4; + color:#000 +} + +button#upload { +background: rgb(255,140,25); /* Old browsers */ +background: -moz-linear-gradient(-45deg, rgba(255,140,25,1) 0%, rgba(207,4,4,1) 100%); /* FF3.6+ */ +background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,rgba(255,140,25,1)), color-stop(100%,rgba(207,4,4,1))); /* Chrome,Safari4+ */ +background: -webkit-linear-gradient(-45deg, rgba(255,140,25,1) 0%,rgba(207,4,4,1) 100%); /* Chrome10+,Safari5.1+ */ +background: -o-linear-gradient(-45deg, rgba(255,140,25,1) 0%,rgba(207,4,4,1) 100%); /* Opera 11.10+ */ +background: -ms-linear-gradient(-45deg, rgba(255,140,25,1) 0%,rgba(207,4,4,1) 100%); /* IE10+ */ +background: linear-gradient(135deg, rgba(255,140,25,1) 0%,rgba(207,4,4,1) 100%); /* W3C */ +filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ff8c19', endColorstr='#cf0404',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ +border:1px solid; +border-color:#000; +} + +button#addCategory { +background: rgb(149,149,149); /* Old browsers */ +background: -moz-linear-gradient(-45deg, rgba(149,149,149,1) 0%, rgba(13,13,13,1) 46%, rgba(1,1,1,1) 50%, rgba(10,10,10,1) 53%, rgba(78,78,78,1) 76%, rgba(56,56,56,1) 87%, rgba(27,27,27,1) 100%); /* FF3.6+ */ +background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,rgba(149,149,149,1)), color-stop(46%,rgba(13,13,13,1)), color-stop(50%,rgba(1,1,1,1)), color-stop(53%,rgba(10,10,10,1)), color-stop(76%,rgba(78,78,78,1)), color-stop(87%,rgba(56,56,56,1)), color-stop(100%,rgba(27,27,27,1))); /* Chrome,Safari4+ */ +background: -webkit-linear-gradient(-45deg, rgba(149,149,149,1) 0%,rgba(13,13,13,1) 46%,rgba(1,1,1,1) 50%,rgba(10,10,10,1) 53%,rgba(78,78,78,1) 76%,rgba(56,56,56,1) 87%,rgba(27,27,27,1) 100%); /* Chrome10+,Safari5.1+ */ +background: -o-linear-gradient(-45deg, rgba(149,149,149,1) 0%,rgba(13,13,13,1) 46%,rgba(1,1,1,1) 50%,rgba(10,10,10,1) 53%,rgba(78,78,78,1) 76%,rgba(56,56,56,1) 87%,rgba(27,27,27,1) 100%); /* Opera 11.10+ */ +background: -ms-linear-gradient(-45deg, rgba(149,149,149,1) 0%,rgba(13,13,13,1) 46%,rgba(1,1,1,1) 50%,rgba(10,10,10,1) 53%,rgba(78,78,78,1) 76%,rgba(56,56,56,1) 87%,rgba(27,27,27,1) 100%); /* IE10+ */ +background: linear-gradient(135deg, rgba(149,149,149,1) 0%,rgba(13,13,13,1) 46%,rgba(1,1,1,1) 50%,rgba(10,10,10,1) 53%,rgba(78,78,78,1) 76%,rgba(56,56,56,1) 87%,rgba(27,27,27,1) 100%); /* W3C */ +filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#959595', endColorstr='#1b1b1b',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ +border:1px solid; +border-color:#000; +} + +SPAN.tip { + TEXT-DECORATION: none; +} +SPAN.tip:hover { + POSITION: relative +} +SPAN.tip SPAN { + DISPLAY: none +} +SPAN.tip:hover SPAN , SPAN.tip.SPAN:hover { + POSITION: absolute; + top: -4px; + border-radius: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + padding: 4px; + DISPLAY: block; + Z-INDEX: 100; + BACKGROUND-color: white; + border: 1px solid black !important; + TEXT-DECORATION: none + font-size: 10pt; + filter: progid:DXImageTransform.Microsoft.Shadow(color=gray,direction=135); +} + + +TD.tip { + TEXT-DECORATION: none +} +TD.tip:hover { + POSITION: relative +} +TD.tip SPAN { + DISPLAY: none +} +TD.tip:hover SPAN { + POSITION: absolute; + top: -4px; + border-radius: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + padding: 4px; + DISPLAY: block; + Z-INDEX: 100; + BACKGROUND-color: #fff; + border: 1px solid black !important; + TEXT-DECORATION: none + font-size: 10pt; + filter: progid:DXImageTransform.Microsoft.Shadow(color=gray,direction=135); +} + +.form-fields { + float:right; + size:40; +} +.fieldset { + outline-style: none !important; + +} + +.selected { + color: #fff; +} + +.missing { + background: #f63; +} + +label em { + position: absolute; + left: 35em; +} + +.file-wrapper { + cursor: pointer; + display: inline-block; + overflow: hidden; + position: relative; +} + +.file-wrapper .button-file { + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 4px; + cursor: pointer; + display: inline-block; + padding: .5em 1em; +} + +.file-wrapper input { + cursor: pointer; + height: 100%; + position: absolute; + right: 0; + top: 0; + filter: alpha(opacity=1); + -moz-opacity: 0.05; + opacity: 0.01; + font-size: 100px; +} + +.upload-success { + font-weight: bold; + size: 1.1em; +} + +.edit-success { + font-weight: bold; + size: 1.1em; +} + +.delete-success { + font-weight: bold; + size: 1.1em; +} + +.no-files { + font-weight: bold; + size: 1.1em; +} +.categorycomments{ + color: black; +} + +.glyphicon-refresh-animate { + -animation: spin .7s infinite linear; + -webkit-animation: spin2 .7s infinite linear; +} +@-webkit-keyframes spin2 { + from { -webkit-transform: rotate(0deg);} + to { -webkit-transform: rotate(360deg);} +} +@keyframes spin { + from { transform: scale(1) rotate(0deg);} + to { transform: scale(1) rotate(360deg);} +} +.tree{ + overflow-x: auto +} +#home-dir{ + padding-left: 0px; +} +.file_name{ + float: left; + border: 1px solid #999; +} +.version{ + width: 30px; + float: left; + border: 1px solid #999; +} +.file_type{ + width: 50px; + float: left; + border: 1px solid #999; +} +.instrument{ + width: 100px; + float: left; + border: 1px solid #999; +} +.updated_by{ + width: 100px; + float: left; + border: 1px solid #999; +} +.for_site{ + width: 100px; + float: left; + border: 1px solid #999; +} +.comments{ + width: 300px; + float: left; + border: 1px solid #999; +} +.date_uploaded{ + width: 150px; + float: left; + border: 1px solid #999; +} +.editLink{ + width: 50px; + float: left; + border: 1px solid #999; +} +.deleteLink{ + width: 50px; + float: left; + border: 1px solid #999; +} +.fileDDD::before{ + position: absolute; + margin-top: 20px; + margin-left: -30px; + overflow: hidden; + width: 30px; + height: 1px; + content: '\a0'; + background-color: #999; +} +#dir-tree>tr>td:first-child { + padding: 0px; + /*height: 100%;*/ +} +#dir-tree>tr>td:first-child>div{ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + border-left:1px solid #999; + padding-left: 30px; + height:100%; + overflow: auto; + word-break: break-word; +} +.fileDDD>div{ + overflow: inherit; +} +#dir-tree>tr>td:first-child>span{ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + border-left:1px solid #999; + padding-left: 30px; + overflow: auto; + word-break: break-word; +} +.spacer{ + float: left; + width: 60px; +} +.fileDDD>span, td>span{ + border: 1px solid #999; + border-radius: 5px; + margin: 5px; +} +.fileColumn{ + min-width: 350px; +} +.directoryRow{ + height: 50px; +} +.fileRow{ + height: 60px; +} +@-moz-document url-prefix() { + .directoryRow>td{ + height: 100%; +} +} \ No newline at end of file diff --git a/modules/document_repository/js/document_repository.js b/modules/document_repository/js/document_repository.js new file mode 100644 index 0000000..398ce93 --- /dev/null +++ b/modules/document_repository/js/document_repository.js @@ -0,0 +1,363 @@ +/*global $, document, window, location */ + +function editCategory() { + "use strict"; + + var id, + value; + $('.categorycomments').bind('blur', function (event) { + event.stopImmediatePropagation(); + id = event.target.id; + value = $("#" + id).text(); + id = id.replace("categorycomment", ""); + $.get(loris.BaseURL + "/document_repository/ajax/categoryEdit.php?id=" + id + "&comments=" + value); + }).keypress(function (e) { + if (e.which === 13) { // Determine if the user pressed the enter button + $(this).blur(); + } + }); +} + + +//Checks that all fields required for file upload are entered +function isEmpty(element) { + "use strict"; + + if (element.val() === " ") { + element.focus(); + element.parent().addClass('has-error'); + $(".upload-error").show(); + return true; + } + return false; +} + +//Checks that file has been selected for upload +function isFileEmpty(element) { + "use strict"; + + if (element.length === 0) { + $(".file-error").show(); + return true; + } + return false; +} + +//Pre-populates the Edit form with fields already associated with that file +function selectElement(element, valueToSelect) { + "use strict"; + + $("#" + element).val(valueToSelect); +} + +function postDelete(id) { + "use strict"; + + $.ajax({ + url: loris.BaseURL + "/document_repository/ajax/documentDelete.php", + type: "POST", + data: {id: id}, + success: function () { + $("#" + id).parent().parent().remove(); + $('.delete-success').show(); + setTimeout(function () { + $('.delete-success').hide(); + }, 3000); + }, + error: function (jqXHR, textStatus, errorThrown) { + if (jqXHR) { + console.log("Error: " + textStatus + " " + errorThrown); + } + } + }); +} + +function deleteModal() { + "use strict"; + + var id = this.id; + $('#deleteModal').modal(); + $("#postDelete").click(function () { + postDelete(id); + }); + + return false; +} + +function postCategory() { + "use strict"; + + $.ajax({ + url: loris.BaseURL + "/document_repository/ajax/addCategory.php", + type: "POST", + data: $("#addCategoryForm").serialize(), + success: function () { + $("#addCategoryModal").modal('hide'); + $("#addCategoryCategory").removeClass("has-error"); + $("#categoryAddError").hide(); + $('.add-success').show(); + setTimeout(function () { + $('.add-success').hide(); + }, 3000); + setTimeout(function () { + location.reload(); + }, 3000); + }, + error: function (jqXHR, textStatus, errorThrown) { + if (jqXHR.status === 400) { + $("#addCategoryCategory").addClass("has-error"); + $("#categoryAddError").show() + } + } + }); +} + +function postEdit(id) { + var data = { + idEdit: id, + categoryEdit: $(categoryEdit).val(), + siteEdit: $(siteEdit).val(), + instrumentEdit: $(instrumentEdit).val(), + pscidEdit: $(pscidEdit).val(), + visitEdit: $(visitEdit).val(), + commentsEdit: $(commentsEdit).val(), + versionEdit: $(versionEdit).val(), + action: $(actionEdit).val(), + submit: 'yeah!!!!' + }; + + $.ajax({ + type: "POST", + url: loris.BaseURL + "/document_repository/ajax/documentEditUpload.php", + data: data, + success: function() { + $('.edit-success').show(); + $("#editModal").modal('hide'); + $("#editFileCategory").removeClass("has-error"); + $("#categoryEditError").hide(); + setTimeout(function() { location.reload() }, 3000); + }, + error: function() { + $("#editFileCategory").addClass("has-error"); + $("#categoryEditError").show(); + } + }); +} + +function editModal() { + "use strict"; + + var id = this.id; + $("#editModal").modal(); + + $.ajax({ + type: "GET", + url: loris.BaseURL + "/document_repository/ajax/getFileData.php", + data: {id: id}, + async: false, + dataType: "json", + success: function (data) { + + //Pre-populate the form with the existing values + selectElement("categoryEdit", data.File_category); + selectElement("siteEdit", data.For_site); + selectElement("instrumentEdit", data.Instrument); + selectElement("pscidEdit", data.PSCID); + selectElement("visitEdit", data.visitLabel); + selectElement("commentsEdit", data.comments); + selectElement("versionEdit", data.version); + + } + }); + + $("#postEdit").click(function(e){ + e.preventDefault(); + postEdit(id); + }); + $("#cancelEditButton").click(function() { + $(".dialog-form-edit").dialog("close"); + }); + + return false; +} + +function uploadForm() { + var elements = $(this).get(0).elements, + category_isEmpty = isEmpty($(elements).filter("[name=category]")), + site_isEmpty = isEmpty($(elements).filter("[name=site]")), + file_isEmpty = isFileEmpty($("[name=file]").get(0).files); + if(category_isEmpty || site_isEmpty || file_isEmpty) { + return false; + } +} + +function renderTree(){ + var fileDir = JSON.parse($("#json_data").html()), + filtered = JSON.parse($("#isFiltered").html()).filtered; + + for(var i in fileDir){ + if(fileDir[i]){ + var dir = fileDir[i]; + var path = dir.CategoryName.split(">"); + var depth = path.length; + + //new table layout + var directory = $('#dir').html(); + Mustache.parse(directory); + if(!filtered){ + var dirData = { + name: path[depth - 1], + id: path[depth - 1].replace(/[^\w]/gi,"_"), + Comment: dir.Comment, + parentID: function(){ + if(depth >= 2) + return path[depth - 2].replace(/[^\w]/gi,"_"); + return null; + }, + indent: function(){ + return (depth - 1)*60; + }, + depth: function(){ + var depthArray = []; + for (var i = 0; i < depth - 1; i++) { + if(i === 0){ + var firstSpacer = {first: "dfg "} + depthArray.push(firstSpacer); + } else { + depthArray.push(" "); + } + }; + return depthArray; + } + } + var renderDir = Mustache.render(directory, dirData); + + if(depth == 1) { + //new table layout + $("#dir-tree").append(renderDir); + } else { + //new table layout + $("#" + path[depth - 2].replace(/[^\w]/gi,"_") + "a").after(renderDir); + } + } + var files = fileDir[i].Files; + for(var ii in files) { + + //new table layout + var file = $('#file').html(); + Mustache.parse(file); // optional, speeds up future uses + if(!filtered){ + files[ii].indent = (depth)*60; + files[ii].parentID = path[depth - 1].replace(/[^\w]/gi,"_"); + files[ii].depth = function(){ + var depthArray = []; + for (var i = 0; i < depth; i++) { + if(i === 0){ + var firstSpacer = {first: "dfg "} + depthArray.push(firstSpacer); + } else { + depthArray.push(" "); + } + }; + return depthArray; + } + var renderedFile = Mustache.render(file, files[ii]); + $("#" + path[depth - 1].replace(/[^\w]/gi,"_") + "a").after(renderedFile); + } else { + files[ii].filtered = true; + var renderedFile = Mustache.render(file, files[ii]); + $("#dir-tree").append(renderedFile); + } + } + } + } +} + +function toggleDirectory(){ + var elmID; + if($(this).hasClass("glyphicon-chevron-down")){ + $(this).removeClass("glyphicon-chevron-down"); + $(this).addClass("glyphicon-chevron-right"); + elmID = $(this).closest('tr').attr("id"); + $("." + elmID).each(collapseDir); + } else { + $(this).removeClass("glyphicon-chevron-right"); + $(this).addClass("glyphicon-chevron-down"); + elmID = $(this).closest('tr').attr("id"); + $("." + elmID).each(expandDir); + } +} + +function collapseDir(key, value){ + var elmID; + if($(value).hasClass("directoryRow")){ + elmID = $(value).attr("id"); + $("." + elmID).each(collapseDir); + } + $(value).hide(); +} +function expandDir(key, value){ + var elmID; + if($(value).hasClass("directoryRow") && $(value).find('span.glyphicon-chevron-down').length != 0){ + elmID = $(value).attr("id"); + $("." + elmID).each(expandDir); + } + $(value).show(); +} + + +$(document).ready(function () { + "use strict"; + + //Hide error and success messages on load + $('.upload-success').hide(); + $('.delete-success').hide(); + $('.add-success').hide(); + $('.edit-success').hide(); + $('.upload-error').hide(); + $('.file-error').hide(); + $('.no-files').hide(); + + renderTree(); + editCategory(); + + //Open confirmation dialog on Delete click + var id; + $('.thedeletelink').click(deleteModal); + + //Add category modal + $('#addCategory').click(function(){ + $('.addCategory').dialog('open'); + }); + + //Upload dialog + //File input wrapper + //$('.file-wrapper input[type=file]').bind('change', fileInputs); + + $(".dialog-form").dialog({ + autoOpen: false, + height: 500, + width: 666, + modal: true + }); + + $("#cancelButton").click(function() { + $(".dialog-form").dialog("close"); + }); + + $("#upload").click(deleteModal); + + $("#postCategory").click(postCategory); + + //The Edit file function + $(".theeditlink").click(editModal); + + + //Check form inputs before upload + $("#uploadForm").submit(uploadForm); + + $(".loading").hide(); + $(".directory").click(toggleDirectory); + + $('.directory').popover({ trigger: "hover" }); +}); diff --git a/modules/document_repository/php/NDB_Menu_Filter_document_repository.class.inc b/modules/document_repository/php/NDB_Menu_Filter_document_repository.class.inc new file mode 100644 index 0000000..85415ce --- /dev/null +++ b/modules/document_repository/php/NDB_Menu_Filter_document_repository.class.inc @@ -0,0 +1,340 @@ +, Mia Petkova + * @license GPLv3 + * @link https://www.github.com/Jkat/Loris-Trunk/ + */ + +/** + * Document Repository Class + * + * This class is for the Document Repository + * + * @category Documentation + * @package Main + * @author Justin Kat , Mia Petkova + * @license GPLv3 + * @link https://www.github.com/Jkat/Loris-Trunk/ +*/ + +class NDB_Menu_Filter_Document_Repository extends NDB_Menu_Filter +{ + + var $centerIDMap; + var $treeToArray = array(); + + function _hasAccess() + { + + //create user object + $user =& User::singleton(); + + return $user->hasPermission('document_repository_view') || $user->hasPermission('document_repository_delete'); + + } + + function _setupVariables() + { + $user =& User::singleton(); + + // create the centerID map + $db =& Database::singleton(); + $pscRows = $db->pselect("SELECT CenterID, Name FROM psc", array()); + foreach ($pscRows AS $row) { + $this->centerIDMap[$row['CenterID']] = $row['Name']; + } + + $query = " FROM document_repository v"; + $query .= " WHERE COALESCE(v.hide_video, false)=false"; + + // set the class variables + $this->columns = array('v.record_id', 'v.File_name', 'v.version', 'v.File_size', 'v.File_category', + 'v.File_type', 'v.Instrument', 'v.uploaded_by', 'v.For_site', 'v.comments', + 'v.Data_dir', 'v.Date_uploaded'); + + $openAccordion = $_GET['openAccordion']; + $this->tpl_data['openAccordion'] = $openAccordion; + $filtered = $_GET['filtered']; + $this->tpl_data['filtered'] = $filtered; + $this->query = $query; + $this->group_by = ''; + $this->order_by = 'v.File_name'; + $this->headers = array('File_name', 'version', 'File_type', 'Instrument', 'uploaded_by', 'For_site', 'comments', 'Date_uploaded', 'Edit', 'Delete'); + $this->validFilters = array('v.For_site', 'v.File_name', 'v.File_category', 'v.File_type', 'v.version', 'v.uploaded_by'); + + $this->formToFilter = array( + 'File_name' => 'v.File_name', + 'File_type' => 'v.File_type', + 'File_category' => 'v.File_category', + 'version' => 'v.version', + 'Data_dir' => 'v.Data_dir', + 'For_site' => 'v.For_site', + 'uploaded_by' => 'v.uploaded_by', + 'Date_uploaded' => 'v.Date_uploaded' + ); + return true; + } + + + function _setFilterForm() + { + // create user object + $user =& User::singleton(); + + $list_of_sites = Utility::getSiteList(true, false); + // allow to view all sites data through filter + if ($user->hasPermission('access_all_profiles')) { + // get the list of study sites - to be replaced by the Site object + if (is_array($list_of_sites)) $list_of_sites = array(null => 'Any') + $list_of_sites; + } else { + // allow only to view own site data + $site =& Site::singleton($user->getData('CenterID')); + if ($site->isStudySite()) { + $list_of_sites = array($user->getData('CenterID') => $user->getData('Site')); + } + } + + $list_of_instruments = Utility::getAllInstruments(); + + $list_of_visit_labels = array_merge(array(null=>"Any") + Utility::getVisitList()); + $subproject_options = Utility::getSubprojectList(); + $subproject_options = array(null=>'Any') + $subproject_options; + + $DB = Database::singleton(); + + //There's no dropdown filter for file names? + //$fileNames = $DB->pselect("Select File_name from document_repository", array()); + $fileTypes = array(); + $fileT = $DB->pselect("SELECT DISTINCT File_type FROM document_repository ORDER BY File_type", array()); + foreach ($fileT as $fileType) { + $fileTypes[$fileType['File_type']] = $fileType['File_type']; + } + $fileTypes = array_merge(array(null=>'Any') + $fileTypes); + + // Form Elements + $this->addBasicText('File_name', 'File name:'); + $this->addBasicText('version', 'Version:'); + + $this->addSelect('File_type', 'File type:', $fileTypes); + $this->addSelect('File_category', 'File category:', $fileCategories); + $this->tpl_data['Sites'] = $list_of_sites; + $this->tpl_data['Instruments'] = $list_of_instruments; + $user =& User::singleton(); + $userID = $user->getData('UserID'); + $this->tpl_data['User'] = $userID; + $this->addSelect('For_site', 'For Site:', $list_of_sites); + $this->addBasicText('uploaded_by', 'Uploaded By:'); + + return true; + } + + function getBetween($string,$delim) + { + $string = explode($delim, $string); + return isset($string[0]) ? $string[0] : ''; + } + + function walkTree($item,$key) + { + if ($key == 'id') { + $this->treeToArray[]= $item; + } + } + + function parseTree($treeToParse) + { + foreach ($treeToParse as $theTree) { + array_walk_recursive($theTree, array($this,'walkTree')); + } + return $this->treeToArray; + } + + function parseCategory($value) + { + $depth = 0; + $DB = Database::singleton(); + $categoryName = $value['category_name']; + do { + if ($value['parent_id'] != 0) { + $depth += 1; + $parent_id = $value['parent_id']; + $value = $DB->pselectRow("SELECT * FROM document_repository_categories where id=$parent_id", array()); + $categoryName = $value['category_name'] . ">" . $categoryName; + } + } while ($value['parent_id'] != 0); + return $categoryName; + } + + /** + * Works the same as NDB_Menu_Filter but doesn't include limit. + * The document_repository is not paginated + */ + function _getPreparedQuery() + { + $DB = Database::singleton(); + + $qparams = array(); + // add the base query + $query = ''; + $query .= $this->_getBaseQuery(); + + $filterdetails = $this->_getBaseFilter(); + $query .= $filterdetails['clause']; + $qparams = $filterdetails['params']; + // apply ORDER BY filters + $query .= " ORDER BY "; + if (!empty($this->filter['order'])) { + $query .= $this->filter['order']['field'] + ." ".$this->filter['order']['fieldOrder'].", "; + } + $query .= $this->order_by; + + return array('query' => $query, 'params' => $qparams); + } + /** + * Overwritten because the document repository isn't really a menu filter table, it doesn't + * use pagination + */ + function _getList() { + return; + } + + /** + * Sets the template data for the list of candidates, users, etc. + * + * @return void + * @access private + */ + function _setDataTable() + { + $DB = Database::singleton(); + $results = array(); + $dbresult= $DB->pselect("SELECT * FROM document_repository_categories ORDER BY parent_id ASC", array()); + foreach ($dbresult as $row) { + $results[]=$row; + $tree = null; + foreach ($results as $result) { + $thisref = &$refs->{$result['id']}; + if(empty($thisref)) { + $thisref = new stdClass(); + $refs->{$result['id']} =& $thisref; + } + foreach ($result as $k => $v) { + $thisref->{$k} = utf8_encode($v); + } + if ($result['parent_id'] == 0) { + $tree->{$result['id']} = &$thisref; + } else { + $refs->{$result['parent_id']}->children->{$result['id']} = &$thisref; + } + } + $tree; // contains the newly sorted tree. + } + + $IDCategories = $DB->pselect("SELECT id,category_name,parent_id,comments FROM document_repository_categories ORDER BY parent_id", array()); + $tmpIDCategories = $IDCategories; + + //$tree is an object with references, so here we are encoding it as json and then decoding it into a php associative array + $tmpIDCategories = json_decode(json_encode($tree), true); + $categoryOrdering = $this->parseTree($tmpIDCategories); + + foreach ($categoryOrdering as $order) { + foreach ($IDCategories as $key=>$value) { + $CategoryID = $value['id']; + if ($CategoryID == $order) { + $fileCategories[$CategoryID] = array( + 'CategoryName' => $this->parseCategory($value), + 'Comment' => $value['comments'], + 'Files' => array() + ); + } + } + } + + $this->tpl_data['File_categories'] = $fileCategories; + + // create instance of config object + $config =& NDB_Config::singleton(); + + // overriding pagination + $DB = Database::singleton(); + + + // print out column headings + $i = 0; + foreach ($this->headers as $header) { + $this->tpl_data['headers'][$i]['name'] = $header; + // format header + $this->tpl_data['headers'][$i]['displayName'] = ucwords(str_replace('_', ' ', $header)); + // set field ordering + if (isset($this->filter['order'])) { + $this->tpl_data['headers'][$i]['fieldOrder'] = ($this->filter['order']['fieldOrder'] == 'ASC') ? 'DESC' : 'ASC'; + } + $i++; + } + + // get the template data for the table rows + + $this->_setDataTableRows(-1); + } + + + + function _setDataTableRows($count) + { + $DB = Database::singleton(); + $query = $this->_getPreparedQuery(); + $prepared = $DB->prepare($query['query']); + $prepared->execute($query['params']); + + $site_names = Utility::getSiteList(true); + // print out + $x = 0; + while ($item = $prepared->fetch(PDO::FETCH_ASSOC)) { + $FileCategory =& $this->tpl_data['File_categories'][$item['File_category']]; + $DisplayItem = $item; + $DisplayItem['File_name'] = utf8_encode($item['File_name']); + $DisplayItem['File_size'] = $this->format_size($item['File_size']); + $DisplayItem['For_site'] = $site_names[$item['For_site']]; + + if (is_array($FileCategory['Files'])) { + array_push($FileCategory['Files'], $DisplayItem); + } + + } + + return true; + } + + + function format_size($value) + { + if (is_null($value)) { + return null; + } + + if ($value >= 1000000000) { + $value = floor($value/1000000000); + return "{$value} GB"; + } + if ($value >= 1000000) { + $value = floor($value/1000000); + return "{$value} MB"; + } + if ($value >= 1000) { + $value = floor($value/1000); + return "{$value} KB"; + } + + return "{$value} bytes"; + + } +} + +?> diff --git a/modules/document_repository/templates/menu_document_repository.tpl b/modules/document_repository/templates/menu_document_repository.tpl new file mode 100755 index 0000000..23cedfd --- /dev/null +++ b/modules/document_repository/templates/menu_document_repository.tpl @@ -0,0 +1,488 @@ + +{literal} + + + + + +{/literal} + +
+
+
+
+
+
+ Selection Filter + + +
+
+
+
+ +
{$form.File_name.html}
+ +
{$form.version.html}
+ +
{$form.uploaded_by.html}
+
+
+
+
+ +
{$form.File_type.html}
+ +
{$form.For_site.html}
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+

+ + The file was successfully uploaded. Loading changes in 3 seconds... +

+
+ +
+

+ + The file was successfully modified. Loading changes in 3 seconds... +

+
+ +
+

+ + The file was successfully deleted. Loading changes in 3 seconds... +

+
+ +
+

+ + New category successfully added! Loading changes in 3 seconds... +

+
+ +
+

+ + No files were found. +

+
+ +{assign "find" array(' ','>','(',')')} +{assign "replaceFind" array('_','_','_','_')} + + + + {section name=header loop=$headers} + + {/section} + + + + +
+ {if $headers[header].displayName == "Edit" || $headers[header].displayName == "Delete"} + {$headers[header].displayName} + {else} + + {$headers[header].displayName} + + {/if} +
+ + + + + + + + + + + +
+ + + diff --git a/modules/document_repository/test/TestPlan b/modules/document_repository/test/TestPlan new file mode 100644 index 0000000..5d7175e --- /dev/null +++ b/modules/document_repository/test/TestPlan @@ -0,0 +1,28 @@ +1. User has access to document repository iff he/she has permission “Access all documents” or is the super user. +2. Create a category and a sub category. +3. Check that the comments for a category are displayed properly. Also check that they can be edited/added. +3. Upload/delete a file. +4. Edit document property and save. Check that changes were saved properly. +5. Download a file and check content. +6. Add files in repository to test search according to file name, file type, version, site and uploaded by. +7. Check that expand/collapse works for non-empty categories. Check that when performing a search, the categories + containing the resulting files (and only those) are shown in a list format +8. Check that if a category contains special characters (e.g. space, dot or comma) it can be expanded. +9. Add two files with the same name but with different contents in two different categories. Verify that, when + downloaded, their contents are different. +10. Edit a file in the repository: check that “Last modified on” date is updated. +11. Upload a file using another user and then update it. Check that “Last updated by” field is updated. +12. User A edits a file. User B tries to delete the same file in the meantime. Check that he can't. +13. Check that preference “Receive Document Repository Notifications” works. +14. Check that performance is OK with large number of files. +15. Check that if you have the 'Receive document repository notification' activated you will receive an email each + time one of the following event occurs: + - Addition, deletion or modification of a file + - Addition of a category. + Also check that the www address contained in the notification email is correct. +16. Check that the following settings are in /etc/php5/apache2/php.ini: + session.gc_maxlifetime 10800 + max_execution_time 10800 + upload_max_filesize 1020M + post_max_size 1024M + diff --git a/modules/document_repository/test/document_repositoryTest.php b/modules/document_repository/test/document_repositoryTest.php new file mode 100644 index 0000000..1c51348 --- /dev/null +++ b/modules/document_repository/test/document_repositoryTest.php @@ -0,0 +1,30 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://github.com/aces/Loris + */ + +require_once __DIR__ . "/../../../test/integrationtests/LorisIntegrationTest.class.inc"; +class documentRepositoryTestIntegrationTest extends LorisIntegrationTest +{ + /** + * Tests that, when loading the document_repository module, some + * text appears in the body. + * + * @return void + */ + function testDocumentRepositoryDoespageLoad() + { + $this->safeGet($this->url . "/document_repository/"); + $bodyText = $this->webDriver->findElement(WebDriverBy::cssSelector("body"))->getText(); + $this->assertContains("Document Repository", $bodyText); + } +} +?> \ No newline at end of file diff --git a/modules/imaging_uploader/README.md b/modules/imaging_uploader/README.md new file mode 100644 index 0000000..a841607 --- /dev/null +++ b/modules/imaging_uploader/README.md @@ -0,0 +1,12 @@ +For the imaging uploader can handle large files, please update the apache configuration file +with the following values + +File to update : /etc/php5/apache2/php.ini + +Sample values: + +session.gc_maxlifetime = 10800 +max_input_time = 10800 +max_execution_time = 10800 +upload_max_filesize = 1024M +post_max_size = 1024M diff --git a/modules/imaging_uploader/READ_ME b/modules/imaging_uploader/READ_ME new file mode 100644 index 0000000..c26e20f --- /dev/null +++ b/modules/imaging_uploader/READ_ME @@ -0,0 +1,2 @@ +This module is overwritten to allow for the demenstration of uploading a file +to the imaging uploader without saving the file to the database diff --git a/modules/imaging_uploader/ajax/getUploadSummary.php b/modules/imaging_uploader/ajax/getUploadSummary.php new file mode 100644 index 0000000..a5ffd66 --- /dev/null +++ b/modules/imaging_uploader/ajax/getUploadSummary.php @@ -0,0 +1,96 @@ + + * @license Loris license + * @link https://www.github.com/Jkat/Loris-Trunk/ + */ + +// Get LORIS user issuing the request +$user =& User::singleton(); +if (!$user->hasPermission('imaging_uploader')) { + header("HTTP/1.1 403 Forbidden"); + exit; +} + +set_include_path( + get_include_path().":../../project/libraries:../../php/libraries:" +); +require_once "NDB_Client.class.inc"; +require_once "NDB_Config.class.inc"; + +$client = new NDB_Client(); +$client->initialize("../../project/config.xml"); + +$config = NDB_Config::singleton(); + +// create Database object +$DB =& Database::singleton(); + +// return bad request if uploadId is not in POST argument list +// or if it is not a number +if (empty($_POST['uploadId']) || !is_numeric($_POST['uploadId'])) { + header("HTTP/1.1 400 Bad Request"); + exit; +} else { + $uploadId = $_POST['uploadId']; +} + +// return bad request if summary is not in POST argument list +// of if it is neither 'true' nor 'false' +if (empty($_POST['summary']) + || ($_POST['summary'] != 'true' && $_POST['summary'] != 'false') +) { + header("HTTP/1.1 400 Bad Request"); + exit; +} else { + $summary = $_POST['summary'] == 'true'; +} + +// Fetch columns Inserting and InsertionComplete from table mri_upload +$query = "SELECT Inserting, InsertionComplete + FROM mri_upload + WHERE UploadId =:uploadId"; + +$row = $DB->pselectRow( + $query, + array('uploadId' => $uploadId) +); +$inserting = $row['Inserting']; +$insertionComplete = $row['InsertionComplete']; + +// Get notifications from table notification_spool. Only get those with +// Verbose == 'N' if summary is set to true +$query = "SELECT NotificationID, TimeSpooled, Error, Verbose, Message + FROM notification_spool + WHERE ProcessID = :processId"; +if ($summary) { + $query .= " AND Verbose = 'N'"; +} + +$notifications = $DB->pselect( + $query, + array('processId' => $uploadId) +); + +// Return JSON object encapsulating the response +print json_encode( + array( + 'inserting' => $inserting, + 'insertionComplete' => $insertionComplete, + 'notifications' => $notifications, + ) +); + +?> diff --git a/modules/imaging_uploader/ajax/read_log.php b/modules/imaging_uploader/ajax/read_log.php new file mode 100644 index 0000000..6695f2f --- /dev/null +++ b/modules/imaging_uploader/ajax/read_log.php @@ -0,0 +1,107 @@ + + * @license Loris License + * @link https://github.com/aces/Loris-Trunk +*/ + +$user =& User::singleton(); +if (!$user->hasPermission('mri_upload')) { + header("HTTP/1.1 403 Forbidden"); + exit; +} + +header("Cache-Control: no-store, no-cache, must-revalidate"); +require_once "../tools/generic_includes.php"; + + +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////get the log file-name//////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +$data_source_file = getLatestLogFile(); + +/** + * Create a temp file to avoid the original file to be truncated + */ +$temp_data_source_file = $data_source_file . ".tmp"; + +///file doesn't exist create it +if (file_exists($temp_data_source_file)) { + if (!file_exists($temp_data_source_file)) { + if (!copy($data_source_file, $temp_data_source_file)) { + print "cannot be copied"; + } + } +} + + +clearstatcache(); + +///create a new temp file for the info + + + +$file = fopen($temp_data_source_file, "r+"); + +////if the file is opened +if ($file) { + ////////Lock the file + if (flock($file, LOCK_EX)) { + + //get the data from the file + $data = fread($file, filesize($temp_data_source_file)); + ftruncate($file, 0);//truncate the file + fflush($file); //writes the buffered output to the file + flock($file, LOCK_UN); //locks the file + echo $data; + } else { + echo "could not lock file"; + } +} + + +/** + * Get the last changed log file using in the + * $paths['base'] . "/" . $paths['log']."/MRI_upload" directory + + * @return String latest log file + */ +function getLatestLogFile() +{ + $config = NDB_Config::singleton(); + $paths = $config->getSetting('paths'); + /////////////////////////////////////////////////////////////////////////// + ///////////////////get the path from the config file/////////////////////// + /////////////////////////////////////////////////////////////////////////// + if (empty($paths)) { + print "config setting is missing"; + error_log("ERROR: Config settings are missing"); + } + + /////////////////////////////////////////////////////////////////////////// + //////////////////////Get the directory name/////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + $log_directory = $paths['base'] . "/" . $paths['log']."/MRI_upload"; + //print "log directory " . $log_directory . "
"; + + //get the last file modified + /////////////////////////////////////////////////////////////////////////// + ////////////////find the file last changed///////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + $files = glob($log_directory."/*MRI_upload*"); + $files = array_combine($files, array_map("filemtime", $files)); + arsort($files); + $data_source_file = key($files); + if (!file_exists($data_source_file)) { + print "File doesn't exist"; + } + return $data_source_file; +} +?> \ No newline at end of file diff --git a/modules/imaging_uploader/css/imaging_uploader.css b/modules/imaging_uploader/css/imaging_uploader.css new file mode 100644 index 0000000..5dd12f7 --- /dev/null +++ b/modules/imaging_uploader/css/imaging_uploader.css @@ -0,0 +1,66 @@ +#log_box { + width: 600px; + height: 200px; + border: 2px solid #064785; + overflow: auto; + padding: 5px; +} + +progress { + /*position: relative;*/ + width: 400px; + height: 20px; + color: #1C70B6; + border-style: none; + border-radius: 10px; +} + +/* Firefox */ +progress::-moz-progress-bar { + background: #1C70B6; + border-radius: 10px; +} + +/* Chrome */ +progress::-webkit-progress-value { + background: #1C70B6; + border-radius: 10px; +} + +/* Polyfill */ +progress[aria-valuenow]:before { + background: #1C70B6; + border-radius: 10px; +} + +.progress{ + background-color: lightgrey; + height: 30px; +} + +#progresslabel { + width: 95%; + position: absolute; + z-index: 1000; + color: white; + text-align: center; + padding-top: 4px; +} + +.upload-logs-table-summary { + width: 95%; + height:130px; + background: #E8E8E8; + resize: vertical; + margin-left:30px; + padding: 5px; +} + +.upload-logs-table-detailed { + width: 95%; + height:400px; + background: #E8E8E8; + resize: vertical; + margin-left:30px; + padding: 5px; +} \ No newline at end of file diff --git a/modules/imaging_uploader/js/imaging_uploader.js b/modules/imaging_uploader/js/imaging_uploader.js new file mode 100644 index 0000000..be59e99 --- /dev/null +++ b/modules/imaging_uploader/js/imaging_uploader.js @@ -0,0 +1,488 @@ +/*global document: false, $: false, window: false, setTimeout:false, FormData:false*/ +function change() { + "use strict"; + $('#show').show(); + $("#show").css("cursor", "pointer"); + $('#hide').hide(); + $('#log_box').hide(); + $('#hide').bind('click', function () { + $('#log_box').hide('slow'); + $('#hide').hide(); + $('#show').show(); + }); + + $('#show').bind('click', function () { + $('#log_box').show('slow'); + $('#show').hide(); + $("#hide").css("cursor", "pointer"); + $('#hide').show(); + }); +} +/* +Prints messages into the log box. +*/ +function printMessage(message) { + "use strict"; + var previous = $("#log_box").html(), + next = previous + message; + $("#log_box").html(next); +} + +/* + Function sends requests to the server (read_log.php script) + and receives messages as strings, which it sends to printMessage. +*/ +function getMessage() { + "use strict"; + $.ajax({ + type: 'GET', + url: loris.BaseURL + '/imaging_uploader/ajax/read_log.php', + success: function (data) { + if (data.indexOf("completed") > -1 || data.indexOf("Error") > -1) { + if (data.indexOf("\n") > -1) { + data = data.replace("\n", "
"); + } + printMessage(data); + return; + } + if (data.indexOf("\n") > -1) { + data = data.replace("\n", "
"); + } + printMessage(data); + // call it again, long-polling + setTimeout(getMessage, 1000); + } + }); +} + +/* + Forms a date and time string to be printed + with the first default message to be consistent + with the fromat of messages received from the server +*/ +function getCurrentTime() { + "use strict"; + var date = new Date(), + day = ("0" + date.getDate()).slice(-2), + month = ("0" + (date.getMonth() + 1)).slice(-2), + hours = ("0" + date.getHours()).slice(-2), + minutes = ("0" + date.getMinutes()).slice(-2), + seconds = ("0" + date.getSeconds()).slice(-2), + result = "[" + date.getFullYear() + "-" + month + "-" + + day + " " + hours + ":" + minutes + ":" + seconds + "]"; + return result; +} + +/* + Updates progress bar value and label +*/ +function progressHandler(event) { + "use strict"; + var progressbar = $("#progressbar"), + progresslabel = $("#progresslabel"), + percent = Math.round((event.loaded / event.total) * 100); + progressbar.attr('value', percent); + progresslabel.text(percent + "%"); + if (percent === 100) { + progresslabel.text("Complete!"); + progresslabel.css('left', '-230px'); + } +} + +/* +Uploads file to the server, listening to the progress +in order to get the percentage uploaded as value for the progress bar +*/ +function uploadFile() { + "use strict"; + + $("#file-input").hide(); + $("#file-progress").show(); + var formData = new FormData($("#mri_upload")[0]); + formData.append("fire_away", "Upload"); + $.ajax({ + type: 'POST', + url: loris.BaseURL + "/imaging_uploader/", + data: formData, + cache: false, + contentType: false, + processData: false, + xhr: function () { + var xhr = new window.XMLHttpRequest(); + xhr.upload.addEventListener( + "progress", + function (evt) { + if (evt.lengthComputable) { + var progressbar = $("#progressbar"), + progresslabel = $("#progresslabel"), + percent = Math.round((evt.loaded / evt.total) * 100); + $(progressbar).width(percent + "%"); + $(progresslabel).html(percent + "%"); + progressbar.attr('aria-valuenow', percent); + } + }, + false + ); + return xhr; + }, + success: function (data) { + if (data.indexOf("The following errors occured while attempting to display this page:") > -1) { + document.open(); + document.write(data); + document.close(); + } else { + $("#filter").click(); + } + } + }); +} + +/* +Main function +*/ +$(function () { + "use strict"; + change(); + $(".submit-button").click( + function (e){ + if(e.currentTarget.id === "filter"){ + $("input[name=mri_file]").val(""); + $("#mri_upload").submit(); + } else if (e.currentTarget.id === "upload"){ + e.preventDefault(); + uploadFile(); + } + } + ); +}); + + +/** + * Represents the progress of the MRI pipeline run on a specific scan. + */ +function UploadProgress() { + /** + * Row (i.e. element) in the MRI upload table associated to this progress element. + */ + this._uploadRow = null; + /** + * Series of dots used in the text used to describe the progress object + * to indicate that the MRI pipeline is currently running. + */ + this._dots = ''; + /** + * Char used in the text used to describe the progress object + * to indicate that the MRI pipeline is currently running. + */ + this._animatedCharIndex = 0; + /** + * Server response to a POST request for the status of an upload progress. + */ + this._progressFromServer = null; + + /** + * Getter method for field _uploadRow. + */ + this.getUploadRow = function() { + return this._uploadRow; + }; + + /** + * Setter method for field _uploadRow. + */ + this.setUploadRow = function(uploadRow) { + this._uploadRow = uploadRow; + + this._progressFromServer = null; + this._dots = ''; + this._animatedCharIndex = 0; + }; + + /** + * Gets the upload ID associated to the current progress object. + */ + this.getUploadId = function() { + if(this._uploadRow == null) { + return null; + } + + return $.trim($(this._uploadRow).find('td').eq(1).text()); + } + + /** + * String representation of this object. + */ + this.getProgressText = function() { + var columns = $(this._uploadRow).find('td'), + uploadId = $.trim($(columns).eq(1).text()), + candid = $.trim($(columns).eq(3).text()), + pscid = $.trim($(columns).eq(4).text()), + visitLabel = $.trim($(columns).eq(5).text()) + + var progressType = $('select[name=LogType] option:selected').text() == 'Summary' ? 'Summary' : 'Details'; + + // All the messages in the notification_spool table + var notificationText = ''; + if(this._progressFromServer != null && this._progressFromServer.notifications != null) { + for(i=0; i'); + + $('select[name=LogType]').on('click', onUploadTypeChange); + + // Define behavior when MRI upload row is clicked + $('#mri_upload_table tbody tr').click( + function(event) { + // Stop server polling if any was taking place + if(uploadProgress.getUploadRow() != null) { + $(uploadProgress.getUploadRow()).css('background-color', 'white'); + setServerPolling(false); + } + + // If user clicked on the same row, it is interpreted as a de-selection: + // deselect row and set log text to 'nothing selected' + if(event.delegateTarget == uploadProgress.getUploadRow() && clickOnSameRowDeselects) { + $('textarea[name=UploadLogs]').val( + ' + +
+ +
+
+ +
+ + + {$form.hidden} + + + + + +{* This section is commented out because the functionality is not currently + in the backend + This functionality is expected to be implemented in future releases of LORIS + + + + + + + + +
+ -Hide log file +
+
+
+*} + +
+
+
+
+ Log viewer +
+
+ +
+
+ +
+ {$form.LogType.html} +
+
+
+
+ {$form.UploadLogs.html} +
+
+
+
+
+ + + + + + + +
+ +
+ + + + + {section name=header loop=$headers} + + {/section} + + + + {section name=item loop=$items} + + + {section name=piece loop=$items[item]} + {if $items[item][piece].name eq 'Progress'} + {if $items[item][piece].value} + + {else} + + {/if} + {elseif $items[item][piece].name eq 'Tarchive_Info'} + {if $items[item][piece].value} + + {else} + + {/if} + {elseif $items[item][piece].name eq 'number_of_mincInserted'} + {if (!empty($items[item][piece].value)) and $items[item][piece].value >0} + + {else} + + {/if} + {else} + + {/if} + {/section} + + {sectionelse} + + + + {/section} + +
+ No. + + + {$headers[header].displayName} + +
+ {if {$items[item][piece].value} eq 'Success'} + {$items[item][piece].value} ({$items[item][10].value} out of {$items[item][11].value}) + {else} + {$items[item][piece].value} + {/if} + + + View Details + + + + {$items[item][piece].value} + + + {$items[item][piece].value} +
No data found
+
+ diff --git a/modules/imaging_uploader/test/TestPlan b/modules/imaging_uploader/test/TestPlan new file mode 100644 index 0000000..3f27b15 --- /dev/null +++ b/modules/imaging_uploader/test/TestPlan @@ -0,0 +1,40 @@ + MRI upload module - Test plan + ============================= + +1. Check that you have access to the MRI upload page if you have either the mri_upload or superuser permission. +2. Check that when accessing the MRI Upload page, the search result table displays all the uploads done for all users. +3. Check that the visit label combo box contains all the possible visit labels (taken from the Visit_label column + in the session table for any Active candidate). +4. Try uploading an invalid file (a .jpg or .txt) with syntactically correct CandID, PSCID and visit labels. Make sure + an appropriate message is displayed in the Console Output at the top of the page. +5. Upload a tar.gz file that is not a valid DICOM archive and specify syntactically correct CandID, PSCID and visit + labels. Make sure an appropriate message is displayed in the Console Output at the top of the page. +6. Execute step 4 again, but with a .tgz and .zip file. +7. Upload a valid, anonymized .tar.gz DICOM archive with the correct CandID, PSCID and visit label. Verify that the + file appears in the table after loading is complete. +8. Click on the show-data buttona and check the contents of the upload table after the successful upload done in 6. + Make sure all fields are correct. Also check that: + - the Tarchive info column contains a link to the DICOM archive page with the details of the archive displayed. + - if the minc inserted column contains something, then it will be a clickable number (link) that takes you to + the MRI Browser page with all the valid scans loaded in the result table. + - check that mins created >= min inserted. If there is difference between the number in the minc created and + min inserted columns, then check that the invalid MRI scans appear on the MRI violations page. +9. Go to the Candidate Profile page and search for the candid of the candidate to which the scan belongs. Make sure + that column scan done is set to yes. Click on the 'Yes' link and verify that it takes you to the Imaging Browser + page with all the valid scans for this candidate loaded in the result table. +10. Go in the the profile details for the candidate found in step 8 and go into the visit table. Check that the MR + Scan done column is set to 'Yes' for the visit at which the scan was done. Click on the Yes link and make sure + you are taken to the Imaging Browser page with the valid scans performed for that candidate at that visit loaded + in the result table. +11. Go the the MRI upload page and search for the upload done in step 6 using (in turn) the CandID, PSID and visit + label. Verify that the upload done in step 5 is shown in the table each time. +12. Repeat steps 7-11 but with a .tgz and .zip file. +13. Upload a valid, anonymized .tar.gz DICOM archive but with a CandID that does not match the one in the archive + (PSCID and visit label should be correct though). Check that an appropriate message is written in the Console + Output. +14. Upload a valid, anonymized .tar.gz DICOM archive but with a PSCID that does not match the one in the archive + (CandID and visit label should be correct though). Check that an appropriate message is written in the Console + Output. +15. Upload a valid, anonymized .tar.gz DICOM archive but with a visit label that does not match the one in the + archive (CandID and PSCID should be correct though). Check that an appropriate message is written in the Console + Output. diff --git a/modules/imaging_uploader/test/imaging_uploaderTest.php b/modules/imaging_uploader/test/imaging_uploaderTest.php new file mode 100644 index 0000000..061ab8d --- /dev/null +++ b/modules/imaging_uploader/test/imaging_uploaderTest.php @@ -0,0 +1,12 @@ +safeGet($this->url . '/imaging_uploader/'); + $bodyText = $this->webDriver->findElement(WebDriverBy::cssSelector("body"))->getText(); + $this->assertContains("Imaging Upload", $bodyText); + } +} +?> diff --git a/modules/statistics/ajax/GetCSV.php b/modules/statistics/ajax/GetCSV.php new file mode 100644 index 0000000..a7bfac4 --- /dev/null +++ b/modules/statistics/ajax/GetCSV.php @@ -0,0 +1,77 @@ +makeCommandLine(); +$client->initialize(); + + + + +header("Content-type: text/plain"); +$DB = Database::singleton(); +$ConditionBindings = array(); +if(isset($_REQUEST['InstrumentY'])) { + $Instrument = $_REQUEST['InstrumentY']; +} +if(isset($_REQUEST['InstrumentX'])) { + $InstrumentX = $_REQUEST['InstrumentX']; +} +if(isset($_REQUEST['FieldY']) && !empty($_REQUEST['FieldY'])) { + $Field = "i." . $_REQUEST['FieldY']; +} else { + $Field = "Candidate_Age"; +} +if(isset($_REQUEST['FieldX']) && !empty($_REQUEST['FieldX'])) { + $FieldX = $_REQUEST['FieldX']; +} else { + $FieldX = 'Candidate_Age'; +} +if(isset($InstrumentX)) { + $FieldX = "i2." . $FieldX; +} + +if($Field == 'Candidate_Age') { + $Field = 'COALESCE(i.Candidate_Age, DATEDIFF(i.Date_taken,c.DoB) / 30)'; +} +if($FieldX == 'Candidate_Age') { + if(isset($InstrumentX)) { + $FieldX = 'COALESCE(i2.Candidate_Age, DATEDIFF(i2.Date_taken,c.DoB) / 30)'; + } else { + $FieldX = 'COALESCE(i.Candidate_Age, DATEDIFF(i.Date_taken,c.DoB) / 30)'; + } +} +$QueryCondition = "$Field IS NOT NULL AND $FieldX IS NOT NULL AND c.Active='Y' and c.Cancelled='N' and s.Active='Y' and s.Cancelled='N'"; +if(isset($_REQUEST['site']) && !empty($_REQUEST['site'])) { + $QueryCondition .= " AND c.CenterID=:Site"; + $ConditionBindings['Site'] = $_REQUEST['site']; + +} +if(isset($_REQUEST['Administration']) && !empty($_REQUEST['Administration'])) { + $QueryCondition .= " AND f.Administration=:Administration"; + $ConditionBindings['Administration'] = $_REQUEST['Administration']; +} +if(isset($_REQUEST['Visit_label']) && !empty($_REQUEST['Visit_label'])) { + $QueryCondition .= " AND s.Visit_label=:Visit_label"; + $ConditionBindings['Visit_label'] = $_REQUEST['Visit_label']; +} +$QueryTable = "$Instrument i join flag f ON (i.CommentID=f.CommentID) JOIN session s ON (s.ID=f.SessionID) JOIN candidate c USING (CandID)"; +if(isset($InstrumentX)) { + $QueryTable .= "JOIN flag f2 ON (f2.SessionID=f.SessionID AND f2.Test_name="; + $QueryTable .= "'$InstrumentX') JOIN $InstrumentX i2 ON (f2.CommentID=i2.CommentID) "; +} + + +$FullQuery = "SELECT c.PSCID as ID, $FieldX as X, $Field as Y, s.SubprojectID as Category, c.CandID, s.ID as SessionID, i.CommentID FROM $QueryTable WHERE $QueryCondition AND c.CenterID <> 1"; +$rows = $DB->pselect($FullQuery, $ConditionBindings); +foreach($rows as $row) { + foreach($row as $key => $val) { + print "\"$val\","; + } + print "\n"; + //print "\"$row[ID]\",\"$row[X]\",\"$row[Y]\",\"$row[Category]\"\n"; +} diff --git a/modules/statistics/ajax/GetScoreLabels.php b/modules/statistics/ajax/GetScoreLabels.php new file mode 100644 index 0000000..1b17234 --- /dev/null +++ b/modules/statistics/ajax/GetScoreLabels.php @@ -0,0 +1,24 @@ +makeCommandLine(); +$client->initialize(); + +require_once "Utility.class.inc"; + +$cols = Utility::getScoreColsForInstrument($_REQUEST['instrument']); +if(strrpos($_REQUEST['instrument'], "_proband") === false) { + print "Candidate_Age\n"; +} +foreach ($cols as $val) { + print "$val\n"; +} +?> diff --git a/modules/statistics/css/statistics.css b/modules/statistics/css/statistics.css new file mode 100644 index 0000000..aba0c10 --- /dev/null +++ b/modules/statistics/css/statistics.css @@ -0,0 +1,111 @@ +.no-js #loader { display: none; } +.js #loader { display: block; position: absolute; left: 100px; top: 0; } +.se-pre-con { + position: fixed; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + z-index: 9999; + background: url(images/loader-64x/Preloader_2.gif) center no-repeat #fff; +} + +.timesheet-daily-checkbox { + margin-left: 10px !important; + margin-right: 5px !important; + padding: 2px; + display:inline-block; + overflow:hidden +} + +td { + white-space: nowrap; +} + + +td.total { + background-color: #F0F0F0 ; + font-weight: bold; +} + +td.subtotal { + background-color: #F0F0F0 !important; + font-weight: bold; +} + +td.total.subtotal { + background-color: #F0F0F0 !important; + font-weight: bold; +} + +/*BEHAVIOURAL subtests tables*/ +table.fancytable th { + background-color: #1c70b6; + color: white; + padding: 0.3em; + border-right:2px groove silver; + border-bottom:1px groove #999999; + text-align: center; +} + +table.fancytable th:hover, table.listColorCoded th:hover { + /*background: #efefef; */ + background: #1c70b6; + color: white; + text-align: center; +} + +/*MRI general scan table*/ +/*DEMOGRAPHICS general table*/ +.pass, .status_active{ + background-color: #E5FFCC !important; +} +.fail, .status_inactive{ + background-color: #FFCCCC !important; +} +.noqc { + background-color: #FFFFCC !important; +} +.gender_male{ + background-color: #CCFFFF !important; +} +.gender_female{ + background-color: #FFCCE5 !important; +} + + +/*MRI big table*/ +.data tr td.total, .data tr td.visit_complete.total { + background-color: #F0F0F0 +} +.data tr td.complete +{ + background-color: #E5FFCC; +} +.data tr td.partial_run, .data tr td.partial_run.total +{ + background-color: #FFFFCC; +} +.data tr td.incomplete, .data tr td.no_scan, .data tr td.incomplete.total, .data tr td.no_scan.total +{ + background-color: #FFCCCC; +} +.data tr td.male +{ + background-color: #CCFFFF; +} +.data tr td.female +{ + background-color: #FFCCE5; +} + +.data tr td.male.total +{ + background-color: #CCFFFF; + font-weight: bold; +} +.data tr td.female.total +{ + background-color: #FFCCE5; + font-weight: bold; +} \ No newline at end of file diff --git a/modules/statistics/js/form_stats_MRI.js b/modules/statistics/js/form_stats_MRI.js new file mode 100644 index 0000000..568d285 --- /dev/null +++ b/modules/statistics/js/form_stats_MRI.js @@ -0,0 +1,47 @@ +/*global document, $*/ +$('#selectall').click( + function(event) { + if(this.checked) { + $('input:checkbox[id=MRIScans]').each( + function() { + this.checked = true; + } + ); + }else{ + $('input:checkbox[id=MRIScans]').each( + function() { + this.checked = false; + } + ); + } + } +); + +//freezecolumn not sufficient for complex tables +//$(document).ready(function(){ +// $("#scandata").DynamicTable({ "freezeColumn" : "scantype" }); +//}); + + +function updateMRITab() { + var MRIProject = document.getElementById("MRIProject"); + var MRIsite = document.getElementById("MRIsite"); + var Scans = document.getElementById("MRIScans"); + scanArray =[]; + $("input:checkbox[id=MRIScans]:checked").each( + function(){ + scanArray.push($(this).val()); + } + ); + + var request = $.ajax( + { + url: loris.BaseURL + '/statistics/stats_MRI/?dynamictabs=dynamictabs&MRIProject=' + MRIProject.value + '&MRIsite=' + MRIsite.value + '&Scans=' + scanArray, + type: 'GET', + data: 'html', + success: function(response) { + $('#mri').html(response); + } + } + ); +} diff --git a/modules/statistics/js/form_stats_behavioural.js b/modules/statistics/js/form_stats_behavioural.js new file mode 100644 index 0000000..55c2ab0 --- /dev/null +++ b/modules/statistics/js/form_stats_behavioural.js @@ -0,0 +1,92 @@ +/*global document, $, window, scrollContent*/ +function updateBehaviouralTab() { + var BehaviouralProject = document.getElementById("BehaviouralProject"); + var request = $.ajax( + { + url: loris.BaseURL + '/statistics/stats_behavioural/?dynamictabs=dynamictabs&BehaviouralProject=' + BehaviouralProject.value, + type: 'GET', + data: 'html', + success: function (response) { + $('#data_entry').html(response); + $(".dynamictable").DynamicTable(); + } + } + ); +} +function checkOverflow() { + 'use strict'; + var element = document.querySelector('#content'); + if ((element.offsetHeight < element.scrollHeight) || (element.offsetWidth < element.scrollWidth)) { + // your element have overflow + $("#content").addClass("col-xs-10 col-xs-offset-1"); + $("#scrollLeft").show(); + $("#scrollRight").show(); + } else { + //your element don't have overflow + $("#content").removeClass("col-xs-10 col-xs-offset-1"); + $("#scrollLeft").hide(); + $("#scrollRight").hide(); + } + element = document.querySelector('#contentDD'); + if ((element.offsetHeight < element.scrollHeight) || (element.offsetWidth < element.scrollWidth)) { + // your element have overflow + $("#contentDD").addClass("col-xs-10 col-xs-offset-1"); + $("#scrollLeftDD").show(); + $("#scrollRightDD").show(); + } else { + //your element don't have overflow + $("#contentDD").removeClass("col-xs-10 col-xs-offset-1"); + $("#scrollLeftDD").hide(); + $("#scrollRightDD").hide(); + } +} +function showStats(clicked) { + 'use strict'; + var id = clicked.id; + $('.' + id).show(); + $('#' + id).attr('colspan', '2'); + $('#' + id + "PIS").attr('colspan', '2'); + $('#' + id).attr('onClick', 'hideStats(this)'); + $('#' + id).addClass('stats-active'); + $('#' + id).attr('data-original-title', 'Click to minimize'); + checkOverflow(); +} +function hideStats(clicked) { + 'use strict'; + var id = clicked.id; + $('.' + id).hide(); + $('#' + id).attr('colspan', '1'); + $('#' + id + "PIS").attr('colspan', '1'); + $('#' + id).attr('onClick', 'showStats(this)'); + $('#' + id).removeClass('stats-active'); + $('#' + id).attr('data-original-title', 'Click to maximize'); + checkOverflow(); +} +$(document).ready( + function(){ + $.getScript(loris.BaseURL + "/js/modules/dynamic_table.table.js") + .done( + function(){ + Table.setup("content", "scrollRight", "scrollLeft"); + Table.checkOverflow("content", "scrollRight", "scrollLeft", "headcol"); + Table.setup("contentDD", "scrollRightDD", "scrollLeftDD"); + Table.checkOverflow("contentDD", "scrollRightDD", "scrollLeftDD", "headcolDD"); + } + ); + $(".spacer").height($(".centers").height()); + // checkOverflow(); + } +); +$(window).resize( + function(){ + $(".spacer").height($(".centers").height()); + Table.checkOverflow("contentDD", "scrollRightDD", "scrollLeftDD", "headcolDD"); + Table.checkOverflow("content", "scrollRight", "scrollLeft", "headcol"); + // checkOverflow(); + } +); +$( + function(){ + $(".tip").tooltip(); + } +); diff --git a/modules/statistics/js/form_stats_demographic.js b/modules/statistics/js/form_stats_demographic.js new file mode 100644 index 0000000..d0ec19d --- /dev/null +++ b/modules/statistics/js/form_stats_demographic.js @@ -0,0 +1,17 @@ +/*global document, $*/ +function updateDemographicTab() { + var DemographicSite = document.getElementById("DemographicSite"); + var DemographicProject = document.getElementById("DemographicProject"); + var DemographicInstrument = document.getElementById("DemographicInstrument"); + var request = $.ajax( + { + url: loris.BaseURL + '/statistics/stats_demographic/?dynamictabs=dynamictabs&DemographicSite=' + DemographicSite.value + '&DemographicProject=' + DemographicProject.value+'&DemographicInstrument='+DemographicInstrument.value, + type: 'GET', + data: 'html', + success: function(response) { + $('#demographics').html(response); + $(".dynamictable").DynamicTable(); + } + } + ); +} diff --git a/modules/statistics/js/form_stats_reliability.js b/modules/statistics/js/form_stats_reliability.js new file mode 100644 index 0000000..c197f5d --- /dev/null +++ b/modules/statistics/js/form_stats_reliability.js @@ -0,0 +1,15 @@ +/*global document, $*/ +function updateReliabilityTab() { + var ReliabilitySite = document.getElementById("ReliabilitySite"); + var ReliabilityProject = document.getElementById("ReliabilityProject"); + var request = $.ajax( + { + url: loris.BaseURL + '/statistics/stats_reliability/?dynamictabs=dynamictabs&ReliabilitySite=' + ReliabilitySite.value + '&ReliabilityProject=' + ReliabilityProject.value, + type: 'GET', + data: 'html', + success: function(response) { + $('#reliability').html(response); + } + } + ); +} diff --git a/modules/statistics/js/statistics.js b/modules/statistics/js/statistics.js new file mode 100644 index 0000000..96fe2c4 --- /dev/null +++ b/modules/statistics/js/statistics.js @@ -0,0 +1,26 @@ +$(document).ready( + function() { + $(".tab-pane").load($('.onLoad a').attr('value')); + $(".statsTabLink").click( + function(){ + $(".tab-pane").load( + $(this).attr('value'), + function() { + $(".dynamictable").DynamicTable(); + } + ); + $(".statsTab").removeClass("active"); + $(this).parent().addClass("active"); + $("#hiddenTabs").html($(this).html()); + $("#tabsContent").hide(); + $("#down").show(); + $("#up").hide(); + } + ) + } +); +function toggleTabs(){ + $("#tabsContent").toggle(); + $("#down").toggle(); + $("#up").toggle(); +} diff --git a/modules/statistics/js/table_statistics.js b/modules/statistics/js/table_statistics.js new file mode 100644 index 0000000..c6dae21 --- /dev/null +++ b/modules/statistics/js/table_statistics.js @@ -0,0 +1,75 @@ +/*global document, $*/ +$('#showVL').click( + function(event) { + if(this.checked) { + $('tr[id=visitrow]').each( + function() { + this.style.display = '' + } + ); + }else{ + $('tr[id=visitrow]').each( + function() { + this.style.display = 'none' + } + ); + } + } +); + +$(document).ready( + function(){ + $("#bigtable").DynamicTable({ "freezeColumn" : "tpcol" }); + } +); + + +function updateDemographicInstrument() { + var DemographicSite = document.getElementById("DemographicSite"); + var DemographicInstrument = document.getElementById("DemographicInstrument"); + + var request = $.ajax( + { + url: loris.BaseURL + '/statistics/stats_demographic/?dynamictabs=dynamictabs&DemographicSite=' + DemographicSite.value + '&DemographicInstrument=' + DemographicInstrument.value, + type: 'GET', + data: 'html', + success: function(page){ + $('#demographics').html(page); + $(".dynamictable").DynamicTable(); + } + } + ); +} + +function updateBehaviouralInstrument() { + var BehaviouralSite = document.getElementById("BehaviouralSite"); + var BehaviouralInstrument = document.getElementById("BehaviouralInstrument"); + var BehaviouralProject = document.getElementById("BehaviouralProject"); + var request = $.ajax( + { + url: loris.BaseURL + '/statistics/stats_behavioural/?dynamictabs=dynamictabs&BehaviouralSite=' + BehaviouralSite.value + '&BehaviouralInstrument=' + BehaviouralInstrument.value + '&BehaviouralProject=' + BehaviouralProject.value, + type: 'GET', + data: 'html', + success: function(page) { + $('#data_entry').html(page); + $(".dynamictable").DynamicTable(); + } + } + ); +} +function updateMRITable() { + var selectedMRI_TYPE = document.getElementById("mri_type"); + var MRIProject = document.getElementById("MRIProject"); + var request = $.ajax( + { + url: loris.BaseURL + '/statistics/stats_MRI/?dynamictabs=dynamictabs&mri_type=' + selectedMRI_TYPE.value + + '&MRIProject=' + MRIProject.value, + type: 'GET', + data: 'html', + success: function(page) { + $('#mri').html(page); + $(".dynamictable").DynamicTable(); + } + } + ); +} diff --git a/modules/statistics/php/NDB_Form_statistics.class.inc b/modules/statistics/php/NDB_Form_statistics.class.inc new file mode 100644 index 0000000..263dc41 --- /dev/null +++ b/modules/statistics/php/NDB_Form_statistics.class.inc @@ -0,0 +1,1049 @@ +hasPermission('data_entry'); + } + + function _inCenter($centerID, $Centres) + { + foreach ($Centres as $Centre) { + if($Centre['NumericID'] == $centerID) { + return true; + } + } + return false; + } + + function render_stats_table($sectionHeader, $tableHeader, $disclamer='', $subcats, $visits, $dropdown_name, $dropdown_opt, $dropdown_selected, $centres, $data, $Subsection="",$projectID=null) + { + $tpl_data = array(); + $tpl_data['test_name'] = $_REQUEST['test_name']; + $tpl_data['Subsection'] = $Subsection; + $tpl_data['Visits'] = $visits; + $smarty = new Smarty_neurodb("statistics"); + $tpl_data['SectionHeader'] = $sectionHeader; + $tpl_data['TableHeader'] = $tableHeader; + $tpl_data['Disclamer'] = $disclamer; + $tpl_data['Subcategories'] = $subcats; + $tpl_data['Subprojects'] = Utility::getSubprojectsForProject($projectID); + $tpl_data['DropdownName'] = $dropdown_name; + $tpl_data['DropdownOptions'] = $dropdown_opt; + $tpl_data['DropdownSelected'] = $dropdown_selected; + $tpl_data['Centers'] = $centres; + foreach($data as $row) { + $subproj = $row['SubprojectID']; + $vl = $row['VLabel']; + $subcat = $row['Subcat']; + $center = $row['CenterID']; + + if(array_search($vl, $visits) !== false + && array_search($subcat, $subcats) !== false + && $this->_inCenter($center, $centres) !== false + ) { + $tpl_data['data'][$subproj][$vl][$subcat] += $row['val']; + $tpl_data['data'][$subproj][$vl]['total'] += $row['val']; + $tpl_data['data'][$subproj][$subcat] += $row['val']; + $tpl_data['data'][$subproj]['total'] += $row['val']; + $tpl_data['data'][$vl][$subcat] += $row['val']; + $tpl_data['data'][$vl]['total'] += $row['val']; + $tpl_data['data'][$subproj]['C' . $center][$vl][$subcat] = $row['val']; + $tpl_data['data'][$subproj]['C' . $center][$vl]['total'] += $row['val']; + $tpl_data['data'][$subproj]['C' . $center][$subcat] += $row['val']; + $tpl_data['data'][$subproj]['C' . $center]['total'] += $row['val']; + $tpl_data['data']['C' . $center][$vl][$subcat] += $row['val']; + $tpl_data['data']['C' . $center][$vl]['total'] += $row['val']; + $tpl_data['data']['C' . $center][$subcat] += $row['val']; + $tpl_data['data']['C' . $center]['total'] += $row['val']; + $tpl_data['data'][$subcat] += $row['val']; + $tpl_data['data']['total'] += $row['val']; + } + + } + $smarty->assign($tpl_data); + $html = $smarty->fetch("table_statistics.tpl"); + return $html; + + } + + function statistics() + { + $DB =& Database::singleton(); + + $this->tpl_data['StatsTabs'] = $DB->pselect( + "SELECT ModuleName, SubModuleName, Description + FROM StatisticsTabs + ORDER BY OrderNo", + array() + ); + } + + function stats_general() + { + //$DB =& Database::singleton(); + } + + function stats_demographic() + { + $DB =& Database::singleton(); + + //This boolean is for optional use by project if the demographics table + // queries any information from the mri_parameter_form table + $this->tpl_data['mri_table_exists'] = true; + if (!$DB->tableExists('mri_parameter_form')) { + $this->tpl_data['mri_table_exists'] = false; + return; + } + + $this->tpl_data['showTable'] = true; + + //PROJECTS + $projects[null] = 'All Projects'; + foreach(Utility::getProjectList() as $key => $value) { + $projects[$key] = $value; + } + $currentProject = null; + if(isset($_REQUEST['DemographicProject']) && $_REQUEST['DemographicProject'] != '') { + $currentProject = $_REQUEST['DemographicProject']; + $this->tpl_data['CurrentProject'] = array( + 'ID' => $currentProject, + 'Name' => $projects[$currentProject], + ); + $Param_Project ='AND (c.ProjectID IS NULL OR c.ProjectID=:pid) '; + $this->params['pid'] =$_REQUEST['DemographicProject']; + } else { + $Param_Project =''; + } + + //SUBPROJECTS + $subprojList =""; + $subprojects = Utility::getSubprojectsForProject($currentProject); + foreach($subprojects as $key=>$val) { + $subprojList .= $key.","; + } + $subprojList = substr($subprojList, 0, -1); + if(!empty($subprojList)) { + $suproject_query ="AND s.SubprojectID IN ($subprojList)"; + } else { + $suproject_query =''; + } + + //SITES + if(isset($_REQUEST['DemographicSite']) && $_REQUEST['DemographicSite'] != '') { + $Param_Site ='AND (c.CenterID IS NULL OR c.CenterID=:sid) '; + $this->params['sid'] =$_REQUEST['DemographicSite']; + } else { + $Param_Site = ''; + } + $centers = $DB->pselect( + "SELECT CONCAT('C', CenterID) as ID, + CenterID as NumericID, + PSCArea as LongName, + Name as ShortName + FROM psc + WHERE CenterID <> '1' + AND Study_site = 'Y'", + array() + ); + $sites[null] ="All sites"; + foreach ($centers as $row) { + $sites[$row['NumericID']] = $row['ShortName']; + if($_REQUEST['DemographicSite'] == $row['NumericID']) { + $this->tpl_data['CurrentSite'] = array( + 'ID' => $row['NumericID'], + 'Name' => $row['LongName'], + ); + } + } + + $Visits = Utility::getExistingVisitLabels($currentProject); + + $this->tpl_data['Sites'] = $sites; + $this->tpl_data['Projects'] = $projects; + $this->tpl_data['Subprojects'] = $subprojects; + $this->tpl_data['Visits'] = $Visits; + + //REGISTERED CANDIDATES ROW + $result = $DB->pselect( + "SELECT s.subprojectid as rowid, + count(DISTINCT(c.PSCID)) as val + FROM candidate as c + LEFT JOIN session s ON (s.CandID=c.CandID) + WHERE c.CenterID <> '1' + AND c.PSCID <> 'scanner' + $Param_Project + $Param_Site + GROUP BY s.subprojectid", + $this->params + ); + + foreach($result as $row) { + $this->tpl_data['registered'][$row['rowid']] = $row['val']; + $this->tpl_data['registered']['total'] += $row['val']; + } + + //PARTICIPANT STATUS ACTIVE + $result = $DB->pselect( + "SELECT s.subprojectid as rowid, + count(DISTINCT(c.PSCID)) as val + FROM candidate as c + LEFT JOIN session s ON (s.CandID=c.CandID) + WHERE c.CenterID <> '1' + AND c.PSCID <> 'scanner' + AND c.Active='Y' + $Param_Project + $Param_Site + GROUP BY s.subprojectid", + $this->params + ); + + foreach($result as $row) { + $this->tpl_data['ps_active'][$row['rowid']] = $row['val']; + $this->tpl_data['ps_active']['total'] += $row['val']; + } + + //PARTICIPANT STATUS INACTIVE + $result = $DB->pselect( + "SELECT s.subprojectid as rowid, + count(DISTINCT(c.PSCID)) as val + FROM candidate as c + LEFT JOIN session s ON (s.CandID=c.CandID) + WHERE c.CenterID <> '1' + AND c.PSCID <> 'scanner' + AND c.Active<>'Y' + $Param_Project + $Param_Site + GROUP BY s.subprojectid", + $this->params + ); + + foreach($result as $row) { + $this->tpl_data['ps_inactive'][$row['rowid']] = $row['val']; + $this->tpl_data['ps_inactive']['total'] += $row['val']; + } + + //MALE + $result = $DB->pselect( + "SELECT s.subprojectid as rowid, + count(DISTINCT(c.PSCID)) as val + FROM candidate as c + LEFT JOIN session s ON (s.CandID=c.CandID) + WHERE coalesce(s.active, 'Y')='Y' + AND c.CenterID <> '1' + AND c.PSCID <> 'scanner' + AND c.Active='Y' + AND c.Gender='Male' + $Param_Project + $Param_Site + GROUP BY s.subprojectid", + $this->params + ); + + foreach($result as $row) { + $this->tpl_data['gender_male'][$row['rowid']] = $row['val']; + $this->tpl_data['gender_male']['total'] += $row['val']; + } + //FEMALE + $result = $DB->pselect( + "SELECT s.subprojectid as rowid, + count(DISTINCT(c.PSCID)) as val + FROM candidate as c + LEFT JOIN session s ON (s.CandID=c.CandID) + WHERE coalesce(s.active, 'Y')='Y' + AND c.CenterID <> '1' + AND c.PSCID <> 'scanner' + AND c.Active='Y' + AND c.Gender='Female' + $Param_Project + $Param_Site + GROUP BY s.subprojectid", + $this->params + ); + + foreach($result as $row) { + $this->tpl_data['gender_female'][$row['rowid']] = $row['val']; + $this->tpl_data['gender_female']['total'] += $row['val']; + } + + //AGE AVERAGE + $result = $DB->pselect( + "SELECT DISTINCT + s.subprojectid as rowid, + AVG(DATEDIFF(c.Date_registered,c.DoB)) as age + FROM candidate as c + JOIN session s ON (s.CandID=c.CandID) + WHERE coalesce(s.active, 'Y')='Y' + AND c.CenterID <> '1' + AND c.PSCID <> 'scanner' + AND c.Active='Y' + $Param_Project + $Param_Site + GROUP BY s.subprojectid", + $this->params + ); + + foreach($result as $row) { + $this->tpl_data['age_avg'][$row['rowid']] = round($row['age']*12/365, 2); + } + + //START BIG TABLE + $inst_dropdown = array_merge( + array( '' => 'Recruit Breakdown by Sex'), + Utility::getAllInstruments() + ); + $instrument_list = Utility::getAllInstruments(); + $this->tpl_data['all_instruments'] = $instrument_list; + if(isset($_REQUEST['DemographicInstrument']) && $_REQUEST['DemographicInstrument'] != '') { + $Subcategories = array( + 'Complete', + 'Incomplete', + ); + $result = $DB->pselect( + "SELECT count(*) as val, + f.Data_entry as Subcat, + c.CenterID as CenterID, + s.SubprojectID as SubprojectID, + s.visit_label as VLabel + FROM session s JOIN candidate c ON (s.CandID=c.CandID) + JOIN flag f ON (f.SessionID=s.ID) + JOIN $_REQUEST[DemographicInstrument] i USING(CommentID) + WHERE s.Active='Y' + AND c.CenterID <> '1' + AND f.Data_entry='Complete' + AND f.Administration='All' + AND f.CommentID NOT LIKE 'DDE%' + $Param_Project + GROUP BY c.CenterID, SubprojectID, VLabel, Subcat + UNION + SELECT count(*) as val, 'Incomplete' as Subcat, + c.CenterID as CenterID, + s.SubprojectID as SubprojectID, + s.visit_label as VLabel + FROM session s JOIN candidate c USING(CandID) + JOIN flag f ON (f.SessionID=s.ID) + JOIN $_REQUEST[DemographicInstrument] i USING(CommentID) + WHERE s.Active='Y' AND s.CenterID <> 1 + $Param_Project + AND f.CommentID NOT LIKE 'DDE%' + AND (f.Data_entry IS NULL OR f.Data_entry <> 'Complete') + GROUP BY c.CenterID, SubprojectID, VLabel, Subcat + ", + $this->params + ); + $this->tpl_data['RecruitsTable'] = $this->render_stats_table( + "Breakdown of Registered Candidates", + "Data Entry Completion Status for ". $instrument_list[$_REQUEST['DemographicInstrument']], + '', + $Subcategories, + $Visits, + "DemographicInstrument", + $inst_dropdown, + $_REQUEST['DemographicInstrument'], + $centers, + $result, + "demographics", + $currentProject + ); + } else { + $Subcategories = array( + 'Male', + 'Female', + ); + $result = $DB->pselect( + "SELECT c.CenterID as CenterID, + s.SubprojectID as SubprojectID, + s.visit_label as VLabel, + c.gender as Subcat, + count(s.CandID) as val + FROM session s JOIN candidate c ON (s.CandID=c.CandID) + WHERE s.active='Y' AND s.CenterID <> '1' + AND (s.Current_stage IN ('Visit', 'Screening', 'Approval') + $suproject_query) + AND COALESCE(s.Screening,'') NOT IN ('Failure', 'Withdrawal') + AND COALESCE(s.Visit,'') NOT IN ('Failure', 'Withdrawal') + GROUP BY c.CenterID, SubprojectID, VLabel, Subcat", + array() + ); + $this->tpl_data['RecruitsTable'] = $this->render_stats_table( + "Breakdown of Registered Candidates", + "Breakdown by Sex", + '', + $Subcategories, + $Visits, + "DemographicInstrument", + $inst_dropdown, + '', + $centers, + $result, + "demographics", + $currentProject + ); + } + } + + function stats_behavioural() + { + $DB =& Database::singleton(); + + $subprojList =""; + + $centers = $DB->pselect( + "SELECT CONCAT('C', CenterID) as ID, + CenterID as NumericID, + PSCArea as LongName, + Name as ShortName + FROM psc + WHERE CenterID <> '1' + AND Study_site = 'Y'", + array() + ); + + $this->tpl_data['Centers'] = $centers; + + $projects[null] = 'All Projects'; + foreach(Utility::getProjectList() as $key=>$value) { + $projects[$key] = $value; + } + $this->tpl_data['Projects'] = $projects; + foreach ($centers as $row) { + $this->tpl_data['Sites'][$row['NumericID']] = $row['LongName']; + if($_REQUEST['site'] == $row['NumericID']) { + $this->tpl_data['CurrentSite'] = array( + 'ID' => $row['NumericID'], + 'Name' => $row['LongName'], + ); + } + + } + + if(isset($_REQUEST['BehaviouralProject']) + && $_REQUEST['BehaviouralProject'] != '' + ) { + $currentProject = $_REQUEST['BehaviouralProject']; + $this->tpl_data['CurrentProject'] = array( + 'ID' => $currentProject, + 'Name' => $projects[$currentProject], + ); + $Param_Project ='AND (c.ProjectID IS NULL OR c.ProjectID=:pid) '; + $this->params['pid'] =$_REQUEST['BehaviouralProject']; + } else { + $Param_Project = ''; + } + $subprojects = Utility::getSubprojectsForProject($currentProject); + + $this->tpl_data['Subprojects'] = $subprojects; + foreach($subprojects as $key=>$val) { + $subprojList .= $key.","; + } + $subprojList = substr($subprojList, 0, -1); + if(!empty($subprojList)) { + $suproject_query ="AND s.SubprojectID IN ($subprojList)"; + } else { + $suproject_query =''; + } + + $Visits = Utility::getExistingVisitLabels($currentProject); + $this->tpl_data['Visits'] = $Visits; + + //---- BEHAVIORAL STATS ----- + $result = $DB->pselect( + "SELECT s.CenterID, + f.Data_Entry as Data_Entry, + s.visit_label as VLabel, + COUNT(s.CandID) as val + FROM session s JOIN candidate c ON (s.CandID=c.CandID) + JOIN flag f ON (f.SessionID=s.ID) + WHERE + s.Active='Y' AND c.Active='Y' + AND s.Current_stage <> 'Recycling Bin' + AND f.CommentID NOT LIKE 'DDE%' + $suproject_query + $Param_Project + GROUP by s.ID, s.CenterID, VLabel, f.Data_Entry", + $this->params + ); + + foreach($result as $row) { + // Put some things into variables to make the lines shorter + // so that they don't wrap. Note that "c"(enter array) needs to + // be a reference since we'll be modifying it. + $center = $row['CenterID']; + $vl = $row['VLabel']; + $c =& $this->tpl_data['behaviour']['C' . $center]; + + $c[$vl]['total'] += $row['val']; + $c['all']['total'] += $row['val']; + if($row['Data_Entry'] == 'Complete') { + $c[$vl]['complete'] += $row['val']; + $c['all']['complete'] += $row['val']; + } + if($c[$vl]['total'] != 0) { + $c[$vl]['percent'] = + floor($c[$vl]['complete'] / $c[$vl]['total']*100); + } + if($c['all']['total'] != 0) { + $c['all']['percent'] = + floor($c['all']['complete'] / $c['all']['total']*100); + } + } + // DDE STATS + $result = $DB->pselect( + "SELECT s.CenterID, + f.Data_Entry as Data_Entry, + s.visit_label as VLabel, + COUNT(s.CandID) as val + FROM session as s + JOIN candidate as c ON (s.CandID=c.CandID) + JOIN flag as f ON (f.SessionID=s.ID) + WHERE s.Active='Y' + AND s.Current_stage <> 'Recycling Bin' + AND f.CommentID LIKE 'DDE%' + AND c.Active='Y' + $suproject_query + $Param_Project + GROUP BY s.CenterID, VLabel, f.Data_Entry ORDER BY c.PSCID;", + $this->params + ); + foreach($result as $row) { + $center = $row['CenterID']; + $vl = $row['VLabel']; + $c =& $this->tpl_data['dde']['C' . $center]; + + $c[$vl]['total'] += $row['val']; + $c['all']['total'] += $row['val']; + if($row['Data_Entry'] == 'Complete') { + $c[$vl]['complete'] += $row['val']; + $c['all']['complete'] += $row['val']; + } + if($c[$vl]['total'] != 0) { + $c[$vl]['percent'] = + floor($c[$vl]['complete'] / $c[$vl]['total']*100); + } + if($c['all']['total'] != 0) { + $c['all']['percent'] = + floor($c['all']['complete'] / $c['all']['total']*100); + } + } + } + + function stats_MRI() + { + $DB =& Database::singleton(); + $bigTable_params =array(); + //if table is not in database handle by displaying error message + $this->tpl_data['mri_table_exists'] = true; + if (!$DB->tableExists('mri_parameter_form')) { + $this->tpl_data['mri_table_exists'] = false; + return; + } + + $this->tpl_data['showTable'] = true; + + //PROJECTS + $projects[null] = 'All Projects'; + foreach(Utility::getProjectList() as $key => $value) { + $projects[$key] = $value; + } + $currentProject = null; + if(isset($_REQUEST['MRIProject']) && $_REQUEST['MRIProject'] != '') { + $currentProject = $_REQUEST['MRIProject']; + $this->tpl_data['CurrentProject'] = array( + 'ID' => $currentProject, + 'Name' => $projects[$currentProject], + ); + $Param_Project ='AND (c.ProjectID IS NULL OR c.ProjectID=:pid) '; + $this->params['pid'] =$_REQUEST['MRIProject']; + $bigTable_params['pid'] =$_REQUEST['MRIProject']; + } else { + $Param_Project = ''; + } + + //SUBPROJECTS + $subprojList =""; + $subprojects = Utility::getSubprojectsForProject($currentProject); + foreach($subprojects as $key=>$val) { + $subprojList .= $key.","; + } + $subprojList = substr($subprojList, 0, -1); + if(!empty($subprojList)) { + $suproject_query ="AND s.SubprojectID IN ($subprojList)"; + } else { + $suproject_query =''; + } + //SITES + if(isset($_REQUEST['MRIsite']) && $_REQUEST['MRIsite'] != '') { + $Param_Site ='AND (c.CenterID IS NULL OR c.CenterID=:sid) '; + $this->params['sid'] =$_REQUEST['MRIsite']; + } else { + $Param_Site = ''; + } + $centers = $DB->pselect( + "SELECT CONCAT('C', CenterID) as ID, + CenterID as NumericID, + PSCArea as LongName, + Name as ShortName + FROM psc + WHERE CenterID <> '1' + AND Study_site = 'Y'", + array() + ); + $sites[null] ="All sites"; + foreach ($centers as $row) { + $sites[$row['NumericID']] = $row['ShortName']; + if($_REQUEST['MRIsite'] == $row['NumericID']) { + $this->tpl_data['CurrentSite'] = array( + 'ID' => $row['NumericID'], + 'Name' => $row['LongName'], + ); + } + } + + //GET SCAN TYPES + $Scan_type_results = $DB->pselect( + "SELECT mst.ID, mst.Scan_type + FROM mri_scan_type mst + JOIN mri_protocol mp ON (mst.ID=mp.Scan_type)", + array() + ); + + foreach ($Scan_type_results as $row) { + $scan_types[$row['ID']] = $row['Scan_type']; + } + + $scans_selected =array(); + if(isset($_REQUEST['Scans']) && $_REQUEST['Scans'] != '') { + $scans_selected_input = explode(",", $_REQUEST['Scans']); + } + if (empty($scans_selected_input)) { + $scans_selected =$scan_types; + } else { + foreach($scans_selected_input as $key => $scid) + { + $scans_selected[$scid] =$scan_types[$scid]; + } + } + + $Visits = Utility::getExistingVisitLabels($currentProject); + $this->tpl_data['scan_types'] = $scan_types; + $this->tpl_data['Scans_sel_box'] = array_keys($scans_selected); + $this->tpl_data['Scans_selected'] = $scans_selected; + $this->tpl_data['Sites'] = $sites; + $this->tpl_data['Projects'] = $projects; + $this->tpl_data['Subprojects'] = $subprojects; + $this->tpl_data['Visits'] = $Visits; + + //START BIG TABLE + $MRISubcategories = array( + 'Complete', + 'Partial Run', + 'No Scan', + ); + + //Check if a specific scan is requested otherwise display first + //available scan + if(!$_REQUEST['mri_type']) { + $MRI_Type = current($scan_types); + } else { + $MRI_Type = $scan_types[$_REQUEST['mri_type']]; + } + //TODO IBIS + if ($MRI_Type==='t1w') { + $MRI_Type ='T1'; + } elseif ($MRI_Type=='t2w') { + $MRI_Type ='T2'; + } elseif ($MRI_Type=='dti') { + $MRI_Type ='DTI'; + } elseif ($MRI_Type=='ep2d_bold') { + $MRI_Type ='BOLD'; + } elseif ($MRI_Type=='DTI65' || $MRI_Type=='DTI65_B1000' ) { + $MRI_Type ='Spectroscopy'; + } + + $MRI_Type_Field = $MRI_Type . "_Scan_Done"; + $MRIHeader = "$MRI_Type Breakdown"; + $CaseStatement = " + CASE($MRI_Type_Field) + WHEN 'Partial' THEN 'Partial Run' + WHEN 'No' THEN 'No Scan' + ELSE $MRI_Type_Field + END"; + + $result = $DB->pselect( + "SELECT s.SubprojectID, + c.CenterID, + s.Visit_label as VLabel, + $CaseStatement as Subcat, + COUNT(*) as val + FROM mri_parameter_form m + JOIN flag f USING (CommentID) + JOIN session s ON (f.SessionID=s.ID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE s.Current_stage <> 'Recycling Bin' + AND f.Administration <> 'None' + AND s.Active='Y' + AND c.Active='Y' + AND f.CommentID NOT LIKE 'DDE%' + $suproject_query + $Param_Project + GROUP BY Subcat, + s.SubprojectID, + c.CenterID, + s.Visit_label", + $bigTable_params + ); + + $M_Visits = Utility::getExistingVisitLabels($currentProject); + $this->tpl_data['MRI_Done_Table'] = $this->render_stats_table( + "Breakdown By Scan Type", + $MRIHeader, + "", + $MRISubcategories, + $M_Visits, + "mri_type", + $scan_types, + $_REQUEST['mri_type'], + $centers, + $result, + "mri", + $currentProject + ); + //END BIGTABLE + + //considers the naming convention of mri_parameter_form is "scanType_scan_done" + $Scan_data_results =array(); + foreach ($scans_selected as $key => $scan) { + $scan_params = array_merge(array('scan' => $scan), $this->params); + + //INSERT COUNT TOTAL + $Scan_data_results[$key]['insert_count']['total'] = $DB->pselectOne( + "SELECT COUNT(*) + FROM files fi + JOIN session s ON (fi.SessionID=s.ID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE s.Current_stage <> 'Recycling Bin' + AND s.Active='Y' + AND c.Active='Y' + AND fi.FileType='mnc' + AND fi.File LIKE CONCAT('%_', :scan, '_%') + $suproject_query + $Param_Project + $Param_Site + ", + $scan_params + ); + //INSERT COUNT Values + $count_data = $DB->pselect( + "SELECT s.SubprojectID as subID, COUNT(*) as cnt + FROM files fi + JOIN session s ON (fi.SessionID=s.ID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE s.Current_stage <> 'Recycling Bin' + AND s.Active='Y' + AND c.Active='Y' + AND fi.FileType='mnc' + AND fi.File LIKE CONCAT('%_', :scan, '_%') + $suproject_query + $Param_Project + $Param_Site + GROUP BY s.SubprojectID", + $scan_params + ); + foreach ($count_data as $row){ + $Scan_data_results[$key]['insert_count'][$row['subID']] = $row['cnt']; + //no_qc_count is computed by the difference between insertions + // and the sum of passed qc and failed qc + $Scan_data_results[$key]['no_qc_count'][$row['subID']] = $row['cnt']; + } + + //QC STATUS: PASSED TOTAL + $Scan_data_results[$key]['qc_pass_count']['total'] = $DB->pselectOne( + "SELECT COUNT(*) + FROM files_qcstatus fqc + JOIN files fi on (fi.FileID=fqc.FileID) + JOIN session s ON (fi.SessionID=s.ID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE s.Current_stage <> 'Recycling Bin' + AND fqc.FileId IS NOT NULL + AND s.Active='Y' + AND c.Active='Y' + AND fqc.QCStatus='Pass' + AND fi.File LIKE CONCAT('%_', :scan, '_%') + $suproject_query + $Param_Project + $Param_Site + ", + $scan_params + ); + //QC STATUS: PASSED values + $count_data = $DB->pselect( + "SELECT s.SubprojectID as subID, COUNT(*) as cnt + FROM files_qcstatus fqc + JOIN files fi on (fi.FileID=fqc.FileID) + JOIN session s ON (fi.SessionID=s.ID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE s.Current_stage <> 'Recycling Bin' + AND fqc.FileId IS NOT NULL + AND s.Active='Y' + AND c.Active='Y' + AND fqc.QCStatus='Pass' + AND fi.File LIKE CONCAT('%_', :scan, '_%') + $suproject_query + $Param_Project + $Param_Site + GROUP BY s.SubprojectID", + $scan_params + ); + foreach ($count_data as $row){ + $Scan_data_results[$key]['qc_pass_count'][$row['subID']] = $row['cnt']; + //no_qc_count is computed by the difference between insertions + // and the sum of passed qc and failed qc + $Scan_data_results[$key]['no_qc_count'][$row['subID']] = + $Scan_data_results[$key]['no_qc_count'][$row['subID']]-$row['cnt']; + } + + //QC STATUS: FAILED TOTAL + $Scan_data_results[$key]['qc_fail_count']['total'] = $DB->pselectOne( + "SELECT COUNT(*) + FROM files_qcstatus fqc + JOIN files fi on (fi.FileID=fqc.FileID) + JOIN session s ON (fi.SessionID=s.ID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE s.Current_stage <> 'Recycling Bin' + AND fqc.FileId IS NOT NULL + AND s.Active='Y' + AND c.Active='Y' + AND fqc.QCStatus='Fail' + AND fi.File LIKE CONCAT('%_', :scan, '_%') + $suproject_query + $Param_Project + $Param_Site + ", + $scan_params + ); + //QC STATUS: FAILED values + $count_data = $DB->pselect( + "SELECT s.SubprojectID as subID, COUNT(*) as cnt + FROM files_qcstatus fqc + JOIN files fi on (fi.FileID=fqc.FileID) + JOIN session s ON (fi.SessionID=s.ID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE s.Current_stage <> 'Recycling Bin' + AND fqc.FileId IS NOT NULL + AND s.Active='Y' + AND c.Active='Y' + AND fqc.QCStatus='Fail' + AND fi.File LIKE CONCAT('%_', :scan, '_%') + $suproject_query + $Param_Project + $Param_Site + GROUP BY s.SubprojectID", + $scan_params + ); + foreach ($count_data as $row){ + $Scan_data_results[$key]['qc_fail_count'][$row['subID']] = $row['cnt']; + //no_qc_count is computed by the difference between insertions + // and the sum of passed qc and failed qc + $Scan_data_results[$key]['no_qc_count'][$row['subID']] = + $Scan_data_results[$key]['no_qc_count'][$row['subID']]-$row['cnt']; + } + + $Scan_data_results[$key]['no_qc_count']['total'] = + $Scan_data_results[$key]['insert_count']['total'] - + $Scan_data_results[$key]['qc_pass_count']['total'] - + $Scan_data_results[$key]['qc_fail_count']['total']; + } + $this->tpl_data['scan_data_results'] = $Scan_data_results; + + } // End function + + + + //RELIABILITY + function stats_reliability() + { + $DB =& Database::singleton(); + + //PROJECTS + $projects[null] = 'All Projects'; + foreach(Utility::getProjectList() as $key => $value) { + $projects[$key] = $value; + } + + if(isset($_REQUEST['ReliabilityProject']) && $_REQUEST['ReliabilityProject'] != '') { + $currentProject = $_REQUEST['ReliabilityProject']; + $this->tpl_data['CurrentProject'] = array( + 'ID' => $currentProject, + 'Name' => $projects[$currentProject], + ); + $Param_Project ='AND (c.ProjectID IS NULL OR c.ProjectID=:pid) '; + $this->params['pid'] =$_REQUEST['ReliabilityProject']; + } else { + $Param_Project =''; + } + + //SITES + if(isset($_REQUEST['ReliabilitySite']) && $_REQUEST['ReliabilitySite'] != '') { + $Param_Site ='AND (c.CenterID IS NULL OR c.CenterID=:sid) '; + $this->params['sid'] =$_REQUEST['ReliabilitySite']; + } else { + $Param_Site = ''; + } + $centers = $DB->pselect( + "SELECT CONCAT('C', CenterID) as ID, + CenterID as NumericID, + PSCArea as LongName, + Name as ShortName + FROM psc + WHERE CenterID <> '1' + AND Study_site = 'Y'", + array() + ); + $sites[null] ="All sites"; + foreach ($centers as $row) { + $sites[$row['NumericID']] = $row['ShortName']; + if($_REQUEST['ReliabilitySite'] == $row['NumericID']) { + $this->tpl_data['CurrentSite'] = array( + 'ID' => $row['NumericID'], + 'Name' => $row['LongName'], + ); + } + } + + $this->tpl_data['Centers'] = $centers; + $this->tpl_data['Projects'] = $projects; + $this->tpl_data['Sites'] = $sites; + + $this->tpl_data['reliability_completion'] = $this->getReliabilityData($Param_Project, $Param_Site); + } + + function getReliabilityData($Param_Project, $Param_Site) + { + // Whether something is reliable or not isn't stored in the database, + // but calculated on the fly. + // As a result, we need to manually calculate it and maintain for each + // type of thing Tanya wants stats for here separately + + // Thresholds was copy/pasted from + include_once __DIR__ . "/../../reliability/php/NDB_Menu_Filter_reliability.class.inc"; + $thresholds = NDB_Menu_Filter_Reliability::getThreshholds(); + + $config = NDB_Config::singleton(); + $reliabilityConfig = $config->getSetting("ReliabilityInstruments"); + $tests = array(); + + foreach(Utility::toArray($reliabilityConfig) AS $reliabilityInstrument){ + $instrument_list = $reliabilityInstrument['Instrument']; + foreach($instrument_list AS $instrument){ + $tests[$instrument['Testname']] = $instrument['Displayname']; + } + } + + $reliable = array(); + foreach($tests as $inst => $name) { + $reliable[] = $this->_getSingleReliabilityData( + $Param_Project, + $Param_Site, + $name, + $inst, + $thresholds[$inst] + ); + } + + return array_merge($reliable); + } + function _getSingleReliabilityData($Param_Project, $Param_Site, $name, $inst_name, $cutoff) + { + $db = Database::singleton(); + $ret = array(); + $ret['name'] = $name; + $ret['complete'] = $db->pselectOne( + "SELECT count(*) + FROM reliability r + JOIN flag f USING(CommentID) + JOIN session s ON (s.ID=f.SessionID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE Instrument='$inst_name' + AND Invalid <> 'yes' + AND Reliability_score IS NOT NULL + AND c.CenterID <> 1 + $Param_Project + $Param_Site", + $this->params + ); + + $ret['reliable'] = $db->pselectOne( + "SELECT count(*) + FROM reliability r + JOIN flag f USING(CommentID) JOIN session s ON (s.ID=f.SessionID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE Instrument='$inst_name' + AND Invalid <> 'yes' + AND Reliability_score IS NOT NULL + AND Reliability_score > $cutoff + AND c.CenterID <> 1 + $Param_Project + $Param_Site", + $this->params + ); + + $ret['total'] = $db->pselectOne( + "SELECT count(*) + FROM reliability r + JOIN flag f USING(CommentID) + JOIN session s ON (s.ID=f.SessionID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE Instrument='$inst_name' + AND Invalid <> 'yes' + AND c.CenterID <> 1 + $Param_Project + $Param_Site", + $this->params + ); + + if($ret['total'] == 0) { + // These should probably be indeterminate instead of 0, but 0 + // looks cleaner. + $ret['percent_complete'] = '0'; + $ret['percent_reliable'] = '0'; + } else { + $ret['percent_complete'] = round($ret['complete'] / $ret['total'] * 100); + $ret['percent_reliable'] = round($ret['reliable'] / $ret['total'] * 100); + } + return $ret; + } + + /** + * Include the column formatter required to display the feedback link colours + * in the statistics menu + * + * @return array of javascript to be inserted + */ + function getJSDependencies() + { + $factory = NDB_Factory::singleton(); + $baseURL = $factory->settings()->getBaseURL(); + $deps = parent::getJSDependencies(); + return array_merge( + $deps, + array( + $baseURL . "/statistics/js/table_statistics.js", + $baseURL . "/statistics/js/statistics.js", + $baseURL . "/statistics/js/form_stats_behavioural.js", + $baseURL . "/statistics/js/form_stats_reliability.js", + $baseURL . "/statistics/js/form_stats_MRI.js", + $baseURL . "/statistics/js/form_stats_demographic.js", + ) + ); + } + +} // End class +?> diff --git a/modules/statistics/php/NDB_Menu_statistics_dd_site.class.inc b/modules/statistics/php/NDB_Menu_statistics_dd_site.class.inc new file mode 100644 index 0000000..277941b --- /dev/null +++ b/modules/statistics/php/NDB_Menu_statistics_dd_site.class.inc @@ -0,0 +1,87 @@ +query_criteria .= " AND s.CenterID =:cid "; + $this->query_vars['cid'] = $centerID; + } + if (!empty($projectID)) { + $this->query_criteria .= " AND c.ProjectID =:pid "; + $this->query_vars['pid'] = $projectID; + } + } + + function notexcluded($var) + { + $config =& NDB_Config::singleton(); + $statisticsConfig = $config->getSetting('statistics'); + $excluded_measures = array($statisticsConfig['excludedMeasures']); + + $key = array_search($var, $excluded_measures); + foreach ($excluded_measures as $key=>$val) { + if (in_array($var, $val) || $var == $val) { + return false; + } + } + return true; + } + + function __construct() + { + $this->instruments = Utility::getAllInstruments(); + foreach($this->instruments as $k=>$v) { + $this->instruments[$k] = $k; + } + + $this->instruments = array_filter($this->instruments, array(&$this, 'notexcluded')); + } + + function _CompleteCount($centerID, $projectID, $instrument) + { + $this->_checkCriteria($centerID, $projectID); + $DB =& Database::singleton(); + + return $DB->pselectOne( + "SELECT count(s.CandID) FROM session s, candidate c, flag f, {$instrument} i WHERE + s.ID=f.SessionID AND f.CommentID=i.CommentID + AND s.CandID=c.CandID + AND s.Active='Y' + AND s.Current_stage <> 'Recycling Bin' + $this->query_criteria + AND f.Data_entry='Complete' AND f.Administration='All' + AND i.CommentID LIKE 'DDE%' ORDER BY c.PSCID", + $this->query_vars + ); + } + + function _GetResults($centerID, $projectID, $instrument) + { + $this->_checkCriteria($centerID, $projectID); + $DB =& Database::singleton(); + $result = $DB->pselect( + "SELECT s.CandID, f.SessionID, i.CommentID, c.PSCID, lower(s.Visit_label) as Visit_label FROM session s, candidate c, flag f, {$instrument} i + WHERE s.ID=f.SessionID AND f.CommentID=i.CommentID AND + s.CandID=c.CandID AND s.Active='Y' + $this->query_criteria + AND s.Current_stage <> 'Recycling Bin' + AND (f.Data_entry is NULL OR f.Data_entry<>'Complete') AND i.CommentID LIKE 'DDE%' ORDER BY c.PSCID", + $this->query_vars + ); + return $result; + } + +} // End class +?> diff --git a/modules/statistics/php/NDB_Menu_statistics_mri_site.class.inc b/modules/statistics/php/NDB_Menu_statistics_mri_site.class.inc new file mode 100644 index 0000000..f79d841 --- /dev/null +++ b/modules/statistics/php/NDB_Menu_statistics_mri_site.class.inc @@ -0,0 +1,193 @@ +query_criteria .= " AND s.CenterID =:cid "; + $this->query_vars['cid'] = $centerID; + } + if (!empty($projectID)) { + $this->query_criteria .= " AND c.ProjectID =:pid "; + $this->query_vars['pid'] = $projectID; + } + } + + function _getIssueName($issue) + { + switch($issue) { + case "Tarchive_Missing": + return "MRI Parameter Form Completed but Missing tarchive entry"; + case "PF_Missing": + return "MRI Browser Populated, but no MRI parameter form completed"; + case "Files_Missing": + return "MRI Parameter Form Completed but nothing in MRI Browser"; + } + return parent::_getIssueName($issue); + } + + function _CompleteCount($centerID, $projectID, $issue) + { + return null; + } + + function _GetResults($centerID, $projectID, $issue) + { + $this->_checkCriteria($centerID, $projectID); + $DB =& Database::singleton(); + + $scan_types = $DB->pselect("SELECT Scan_type from mri_scan_type"); + $where = "WHERE ("; + $counter = 1; + foreach ($scan_types as $index=>$scan) { + foreach ($scan as $type=>$label) { + if ($counter < sizeof($scan_types)) { + $where .= "m.$label <> 'No' OR m.$label IS NULL OR "; + } + else { + $where .= "m.$label <> 'No' OR m.$label IS NULL )"; + } + $counter++; + } + } + switch($issue) { + case 'Tarchive_Missing': + $query = "SELECT DISTINCT f.CommentID as CommentID, + c.PSCID, s.ID as SessionID, s.CandID as CandID, + s.Visit_label + FROM flag f JOIN session s ON (f.SessionID=s.ID) + LEFT JOIN mri_parameter_form m ON (m.CommentID=f.CommentID) + LEFT JOIN tarchive t ON (s.CandID=MID(t.PatientName, 9, 6) AND MID(t.PatientName, 16, 7)=s.Visit_label) + LEFT JOIN candidate c ON (s.CandID=c.CandID) + $where + AND f.CommentID NOT LIKE 'DDE%' + AND f.Test_name='mri_parameter_form' + AND f.Administration <> 'None' + AND t.TarchiveID IS NULL + AND s.Active='Y' + AND s.CenterID <> '1' + AND f.Data_entry='Complete' + $this->query_criteria + ORDER BY c.PSCID"; + break; + case 'Files_Missing': + $where = "WHERE ("; + $counter = 1; + foreach ($scan_types as $index=>$scan) { + foreach ($scan as $type=>$label) { + if ($counter < sizeof($scan_types)) { + $where .= "m.$label <> 'No' OR "; + } + else { + $where .= "m.$label <> 'No')"; + } + $counter++; + } + } + + $query = "SELECT DISTINCT f.CommentID as CommentID, s.ID as SessionID, c.PSCID, s.CandID, s.Visit_label + FROM mri_parameter_form m LEFT JOIN flag f ON + (f.CommentID=m.CommentID) + LEFT JOIN session s ON (s.ID=f.SessionID) + LEFT JOIN files fi ON (fi.SessionID=f.SessionID) + LEFT JOIN candidate c ON (c.CandID=s.CandID) + $where + AND fi.FileID IS NULL + AND f.Data_entry= 'Complete' + AND s.Active='Y' + AND s.CenterID <> '1' + $this->query_criteria + ORDER BY c.PSCID"; + break; + case 'PF_Missing': $query = + "SELECT DISTINCT c.PSCID, s.ID as SessionID, + s.Visit_label + FROM files LEFT JOIN session s ON (files.SessionID=s.ID) + LEFT JOIN flag f on (f.SessionID=s.ID AND f.Test_name='mri_parameter_form' AND f.CommentID NOT LIKE 'DDE%') + LEFT JOIN mri_parameter_form mpf ON (mpf.CommentID=f.CommentID) + LEFT JOIN candidate c ON (c.CandID=s.CandID) + WHERE s.Active='Y' + AND c.PSCID <> 'scanner' + AND (f.ID IS NULL OR + f.Data_entry <> 'Complete' OR + f.Data_entry IS NULL) + AND s.CenterID <> '1' + $this->query_criteria + ORDER BY c.PSCID"; + break; + + } + if($query) { $result = $DB->pselect($query, $this->query_vars); + } + return $result; + } + + function setup() + { + $this->issues = array( + "Tarchive_Missing", + "Files_Missing", + "PF_Missing", + ); + if(isset($_GET['subtest'])) { + header("Location: ?test_name=statistics#data_entry"); + } + $DB =& Database::singleton(); + $center = $DB->pselectRow("SELECT CenterID as ID, PSCArea as Name FROM psc WHERE CenterID=:cid", array('cid' => $_REQUEST['CenterID'])); + $id = $center['ID']; + $name = $center['Name']; + // List of all visits. Add to it any time a new one is seen, so + // that we can iterate over it to display later, and leave blank + // cells for ones that are missing for a given issue in the + // template + $visits = array(); + + //$this->_setIssueName(); + $data = array(); + foreach($this->issues as $issue){ + $complete_count = $this->_CompleteCount($centerID, $projectID, $issue); + + $results = $this->_GetResults($centerID, $projectID, $issue); + + $test_url = $issue; + foreach($results as $row) { + if(!in_array($row['Visit_label'], $visits)) { + $visits[] = $row['Visit_label']; + } + $results[$row['Visit_label']][] = array( + 'test_url' => $test_url, + 'CandID' => $row['CandID'], + 'SessionID' => $row['SessionID'], + 'CommentID' => $row['CommentID'], + 'PSCID' => $row['PSCID'], + ); + } + $data[] = array( + 'name' => $this->_getIssueName($issue), + 'count' => $complete_count, + 'incompletes' => $results, + ); + } + + $this->tpl_data["data"] = $data; + $this->tpl_data["SiteName"] = $name; + sort($visits); + $this->tpl_data['AllVisits'] = $visits; + $this->tpl_data['NumVisitLabels'] = count($visits); + + // return parent::setup(); + + } + +} // End class +?> diff --git a/modules/statistics/php/NDB_Menu_statistics_site.class.inc b/modules/statistics/php/NDB_Menu_statistics_site.class.inc new file mode 100644 index 0000000..8eeb229 --- /dev/null +++ b/modules/statistics/php/NDB_Menu_statistics_site.class.inc @@ -0,0 +1,154 @@ +query_criteria .= " AND s.CenterID =:cid "; + $this->query_vars['cid'] = $centerID; + } + if (!empty($projectID)) { + $this->query_criteria .= " AND c.ProjectID =:pid "; + $this->query_vars['pid'] = $projectID; + } + } + + function _setInstrumentName() + { + $this->instruments = Utility::getAllInstruments(); + } + + // Helper function to get a friendlier version of the instrument name. + // This is mostly used for the MRI site statistics, which uses + // names like "Tarchive_Missing" for "MRI Parameter Form Completed but Missing Tarchive Entry" + + function _getIssueName($issue) + { + return $issue; + } + + function _getInstrumentName($instrument) + { + return $this->instruments[$instrument]; + } + + function _CompleteCount($centerID, $projectID, $instrument) + { + $this->_checkCriteria($centerID, $projectID); + $DB =& Database::singleton(); + $count = $DB->pselectOne( + "SELECT count(s.CandID) FROM session s, + candidate c, flag f, {$instrument} i + WHERE s.ID=f.SessionID AND f.CommentID=i.CommentID + AND s.CandID=c.CandID + AND s.Active='Y' + AND s.CenterID <> '1' + $this->query_criteria + AND f.Data_entry='Complete' + AND s.Current_stage <> 'Recycling Bin' + AND f.Administration='All' + AND i.CommentID NOT LIKE 'DDE%'", + $this->query_vars + ); + return $count; + } + + function _GetResults($centerID, $projectID, $instrument) + { + $this->_checkCriteria($centerID, $projectID); + $DB =& Database::singleton(); + $result = $DB->pselect( + "SELECT s.CandID, f.SessionID, i.CommentID, c.PSCID, + s.Visit_label + FROM session s, candidate c, flag f, + {$instrument} i + WHERE s.ID=f.SessionID AND f.CommentID=i.CommentID + AND s.CandID=c.CandID + AND s.Active='Y' + AND s.Current_stage <> 'Recycling Bin' + $this->query_criteria + AND (f.Data_entry is NULL OR f.Data_entry<>'Complete') + AND i.CommentID NOT LIKE 'DDE%' ORDER BY s.Visit_label, c.PSCID", + $this->query_vars + ); + return $result; + } + + function setup() + { + // If following a breadcrumb, redirect to the original statistics page + // since the test_name/subtest don't work the same for the site specific + // pages as for non-site specific + if(isset($_GET['subtest'])) { + header("Location: ?test_name=statistics#data_entry"); + } + $DB =& Database::singleton(); + if (!empty($_REQUEST['CenterID'])) { + $center = $DB->pselectRow("SELECT CenterID as ID, PSCArea as Name FROM psc WHERE CenterID =:cid", array('cid' => $_REQUEST['CenterID'])); + $centerID = $center['ID']; + $name = $center['Name']; + } + else + { + $name = 'All'; + } + if (!empty($_REQUEST['ProjectID'])) { + $projectID = $_REQUEST['ProjectID']; + } + else { + $projectID = ''; + } + // List of all visits. Add to it any time a new one is seen, so + // that we can iterate over it to display later, and leave blank + // cells for ones that are missing for a given instrument in the + // template + $visits = array(); + + $this->_setInstrumentName(); + $data = array(); + foreach($this->instruments as $instrument=>$label){ + $complete_count = $this->_CompleteCount($centerID, $projectID, $instrument); + + $results = $this->_GetResults($centerID, $projectID, $instrument); + + $test_url = $instrument; + + foreach($results as $row) { + if(!in_array($row['Visit_label'], $visits)) { + $visits[] = $row['Visit_label']; + } + $results[$row['Visit_label']][] = array( + 'test_url' => $test_url, + 'CandID' => $row['CandID'], + 'SessionID' => $row['SessionID'], + 'CommentID' => $row['CommentID'], + 'PSCID' => $row['PSCID'], + ); + } + $data[] = array( + 'name' => $label, + 'count' => $complete_count, + 'incompletes' => $results, + ); + } + + $this->tpl_data["data"] = $data; + $this->tpl_data["SiteName"] = $name; + sort($visits); + $this->tpl_data['AllVisits'] = $visits; + $this->tpl_data['NumVisitLabels'] = count($visits); + + } // End function +} // End class +?> diff --git a/modules/statistics/templates/form_statistics.tpl b/modules/statistics/templates/form_statistics.tpl new file mode 100644 index 0000000..60bef24 --- /dev/null +++ b/modules/statistics/templates/form_statistics.tpl @@ -0,0 +1,29 @@ +
+ +
+
+ + + +
+ +
+
+
+
+
+
diff --git a/modules/statistics/templates/form_stats_MRI.tpl b/modules/statistics/templates/form_stats_MRI.tpl new file mode 100644 index 0000000..c58114a --- /dev/null +++ b/modules/statistics/templates/form_stats_MRI.tpl @@ -0,0 +1,95 @@ +
+ +

General Statistics with QC Status

+
+ {html_options id="MRIsite" options=$Sites name="MRIsite" selected=$CurrentSite.ID class="form-control"} +
+
+ {html_options id="MRIProject" options=$Projects name="MRIProject" selected=$CurrentProject.ID class="form-control"} +
+

+
+ Select All + {html_checkboxes id="MRIScans" options=$scan_types name="MRIScans" selected=$Scans_sel_box class="timesheet-daily-checkbox"} + {*All Scan Types + {foreach item=scan key=scanid from=$scan_types} + {$scan} + {/foreach} + *} +
+

+ +

+
+ + + + + + {foreach from=$Subprojects item=name key=proj} + + {/foreach} + + + + + {foreach item=scan key=scanid from=$Scans_selected} + + + + {foreach from=$Subprojects item=name key=proj} + {if $scan_data_results[$scanid].insert_count[$proj] > 0} + + {else} + + {/if} + {/foreach} + + + + + + + {foreach from=$Subprojects item=name key=proj} + {if $scan_data_results[$scanid].qc_pass_count[$proj] > 0} + + {else} + + {/if} + {/foreach} + + + + + {foreach from=$Subprojects item=name key=proj} + {if $scan_data_results[$scanid].qc_fail_count[$proj] > 0} + + {else} + + {/if} + {/foreach} + + + + + {foreach from=$Subprojects item=name key=proj} + {if $scan_data_results[$scanid].no_qc_count[$proj] > 0} + + {else} + + {/if} + {/foreach} + + + {/foreach} + +
Scan TypeQC Status{$name}Total
{$scan}Scans Inserted{$scan_data_results[$scanid].insert_count[$proj]}0{$scan_data_results[$scanid].insert_count.total}
QC statusPassed{$scan_data_results[$scanid].qc_pass_count[$proj]}0{$scan_data_results[$scanid].qc_pass_count.total}
Failed{$scan_data_results[$scanid].qc_fail_count[$proj]}0{$scan_data_results[$scanid].qc_fail_count.total}
Not QCed{$scan_data_results[$scanid].no_qc_count[$proj]}0{$scan_data_results[$scanid].no_qc_count.total}
+
+ {if $mri_table_exists} + {$MRI_Done_Table} + {else} +

Oops

+

It seems like the "mri_parameter_form" table is missing in the database currently in use. This table is necessary in order to compute the MRI statistics on this page.

+ {/if} +
+ diff --git a/modules/statistics/templates/form_stats_behavioural.tpl b/modules/statistics/templates/form_stats_behavioural.tpl new file mode 100644 index 0000000..64c4d5d --- /dev/null +++ b/modules/statistics/templates/form_stats_behavioural.tpl @@ -0,0 +1,113 @@ +
+

Data Entry Statistics {if $CurrentProject} for {$CurrentProject.Name} {/if}

+ +
+ {html_options id="BehaviouralProject" options=$Projects name="BehaviouralProject" selected=$CurrentProject.ID class="form-control"} +
+ +

+ + + + + + {foreach from=$Centers item=center key=centername} + + {/foreach} + + + + {foreach from=$Centers item=center} + + + {/foreach} + + + + {foreach from=$Visits item=visit} + + + {foreach from=$Centers item=center key=centername} + + + {/foreach} + + {/foreach} + + + {foreach from=$Centers item=center key=centername} + + + {/foreach} + + + + {foreach from=$Centers item=center key=centername} + + {/foreach} + + +
+ {$center.LongName} +
VisitCompleted (%)Created
{$visit|upper}{$behaviour[$center.ID][$visit].complete|default:"0"} ({$behaviour[$center.ID][$visit].percent|default:"0"}%){$behaviour[$center.ID][$visit].total|default:"0"}
Total{$behaviour[$center.ID].all.complete|default:"0"} ({$behaviour[$center.ID].all.percent|default:"0"}%){$behaviour[$center.ID].all.total|default:"0"}
Per Instrument Stats + Please Click Here +
+ + + + Click here for breakdown per participant {if $CurrentSite} for {$CurrentSite.Name} {/if} {if $CurrentProject} for {$CurrentProject.Name} {/if} +

+

Double Data Entry Statistics:

+ + + + + + {foreach from=$Centers item=center key=centername} + + {/foreach} + + + + + {foreach from=$Centers item=center} + + + {/foreach} + + + + {foreach from=$Visits item=visit} + + + {foreach from=$Centers item=center key=centername} + + + {/foreach} + + {/foreach} + + + {foreach from=$Centers item=center key=centername} + + + {/foreach} + + + + {foreach from=$Centers item=center key=centername} + + {/foreach} + + +
+ {$center.LongName} +
VisitCompletedCreated
{$visit|upper}{$dde[$center.ID][$visit].complete|default:"0"} ({$dde[$center.ID][$visit].percent|default:"0"}%){$dde[$center.ID][$visit].total|default:"0"}
Total{$dde[$center.ID].all.complete|default:"0"} ({$dde[$center.ID].all.percent|default:"0"}%){$dde[$center.ID].all.total|default:"0"}
Per Instrument Stats + Please Click Here +
+ +
+ diff --git a/modules/statistics/templates/form_stats_demographic.tpl b/modules/statistics/templates/form_stats_demographic.tpl new file mode 100644 index 0000000..8205de1 --- /dev/null +++ b/modules/statistics/templates/form_stats_demographic.tpl @@ -0,0 +1,198 @@ +
+

General Demographic Statistics{if $CurrentSite} for {$CurrentSite.Name}{/if} + {if $CurrentProject} for {$CurrentProject.Name} {/if}

+ +
+ {html_options id="DemographicSite" options=$Sites name="DemographicSite" selected=$CurrentSite.ID class="form-control"} +
+
+ {html_options id="DemographicProject" options=$Projects name="DemographicProject" selected=$CurrentProject.ID class="form-control"} +
+ + + +

+ + + + + + {foreach from=$Subprojects item=name key=proj} + + {/foreach} + + + + + + + {if {$registered[$keyid]}>0} + + {else} + + {/if} + {foreach from=$Subprojects item=proj key=keyid} + {if {$registered[$keyid]}>0} + + {else} + + {/if} + {/foreach} + + + + + + {if {$registered[$NULL]}>0} + {if {$ps_active[$NULL]}>0} + + {else} + + {/if} + {else} + + {/if} + {foreach from=$Subprojects item=proj key=keyid} + {if {$registered[$keyid]}>0} + {if {$ps_active[$keyid]}>0} + + {else} + + {/if} + {else} + + {/if} + {/foreach} + {if {$registered.total}>0} + {if {$ps_active.total}>0} + + {else} + + {/if} + {else} + + {/if} + + + + {if {$registered[$NULL]}>0} + {if {$ps_inactive[$NULL]}>0} + + {else} + + {/if} + {else} + + {/if} + {foreach from=$Subprojects item=proj key=keyid} + {if {$registered[$keyid]}>0} + {if {$ps_inactive[$keyid]}>0} + + {else} + + {/if} + {else} + + {/if} + {/foreach} + {if {$registered.total}>0} + {if {$ps_inactive.total}>0} + + {else} + + {/if} + {else} + + {/if} + + + + + + + {if {$ps_active[$NULL]}>0} + {if {$gender_male[$NULL]}>0} + + {else} + + {/if} + {else} + + {/if} + {foreach from=$Subprojects item=proj key=keyid} + {if {$ps_active[$keyid]}>0} + {if {$gender_male[$keyid]}>0} + + {else} + + {/if} + {else} + + {/if} + {/foreach} + {if {$ps_active.total}>0} + {if {$gender_male.total}>0} + + {else} + + {/if} + {else} + + {/if} + + + + + {if {$ps_active[$NULL]}>0} + {if {$gender_female[$NULL]}>0} + + {else} + + {/if} + {else} + + {/if} + {foreach from=$Subprojects item=proj key=keyid} + {if {$ps_active[$keyid]}>0} + {if {$gender_female[$keyid]}>0} + + {else} + + {/if} + {else} + + {/if} + + {/foreach} + {if {$ps_active.total}>0} + {if {$gender_female.total}>0} + + {else} + + {/if} + {else} + + {/if} + + + + + {if {$age_avg[$NULL]}>0} + + {else} + + {/if} + {foreach from=$Subprojects item=proj key=keyid} + {if {$age_avg[$keyid]}>0} + + {else} + + {/if} + {/foreach} + + + +
DemographicsUndefined{$name}Total
Registered candidates{$registered[NULL]}0{$registered[$keyid]}0{$registered.total}
Participant StatusActive{$ps_active[$NULL]}/{$registered[$NULL]}0/{$registered[$NULL]}0/0{$ps_active[$keyid]}/{$registered[$keyid]}0/{$registered[$keyid]}0/0{$ps_active.total}/{$registered.total}0/{$registered.total}0/0
Inactive{$ps_inactive[$NULL]}/{$registered[$NULL]}0/{$registered[$NULL]}0/0{$ps_inactive[$keyid]}/{$registered[$keyid]}0/{$registered[$keyid]}0/0{$ps_inactive.total}/{$registered.total}0/{$registered.total}0/0
SexMale{$gender_male[$NULL]}/{$ps_active[$NULL]}0/{$ps_active[$NULL]}0/0{$gender_male[$keyid]}/{$ps_active[$keyid]}0/{$ps_active[$keyid]}0/0{$gender_male.total}/{$ps_active.total}0/{$ps_active.total}0/0
Female{$gender_female[$NULL]}/{$ps_active[$NULL]}0/{$ps_active[$NULL]}0/0{$gender_female[$keyid]}/{$ps_active[$keyid]}0/{$ps_active[$keyid]}0/0{$gender_female.total}/{$ps_active.total}0/{$ps_active.total}0/0
Age Average (months){$age_avg[$NULL]}0{$age_avg[$keyid]}0N/A
+ + {$RecruitsTable} +
diff --git a/modules/statistics/templates/form_stats_general.tpl b/modules/statistics/templates/form_stats_general.tpl new file mode 100644 index 0000000..edc8b95 --- /dev/null +++ b/modules/statistics/templates/form_stats_general.tpl @@ -0,0 +1,6 @@ +

Welcome to the statistics page.

+

This will display all of the statistics related to data acquisition, data processing and data entry as it relates to both the behavioural and imaging parts of this project. This is a work in progress, and as such more statistics will appear with time. Please feel free to contact us with feedback and suggestions.

+

The demographics tab currently includes statistics about the number of candidates registered in each cohort and provides statistics by site and gender. In the drop-down menu, you are also able to view per instrument the number of complete and incomplete instruments per site, timepoint, and cohort.

+

The behavioural tab currently includes statistics on the number of candidates who have completed each instrument per site and timepoint. The number of instruments for which data entry hasn't commenced is also displayed. This is a great tool for completing data entry. The same statistics are also available for double data entry. +

The MRI statistics tab currently displays the number of scans per site. For T1 scans based on the MRI parameter form entries, there is a breakdown of scans per site, cohort and timepoint. There is also a table depicting common issues that need to be dealt with regarding incomplete MRI parameter forms, scans not transferred properly, or missing scan archives.

+ diff --git a/modules/statistics/templates/form_stats_reliability.tpl b/modules/statistics/templates/form_stats_reliability.tpl new file mode 100644 index 0000000..2ac96fb --- /dev/null +++ b/modules/statistics/templates/form_stats_reliability.tpl @@ -0,0 +1,43 @@ + +
+

Reliability Statistics{if $CurrentProject} for {$CurrentProject.Name}{/if}{if $CurrentSite} for {$CurrentSite.Name} {else} for All Sites{/if}

+
+ {html_options id="ReliabilitySite" options=$Sites name="ReliabilitySite" selected=$CurrentSite.ID class="form-control"} +
+
+ {html_options id="ReliabilityProject" options=$Projects name="ReliabilityProject" selected=$CurrentProject.ID class="form-control"} +
+
+ +
+

+
+
+ + + + + + + + + + + + + {section name=item loop=$reliability_completion} + + + + + + + + + {/section} + +
Reliablity ModuleTotal FlaggedTotal CompleteTotal ReliablePercent CompletePercent Reliable
{$reliability_completion[item].name}{$reliability_completion[item].total}{$reliability_completion[item].complete}{$reliability_completion[item].reliable}{$reliability_completion[item].percent_complete}%{$reliability_completion[item].percent_reliable}%
+
+
+
+ diff --git a/modules/statistics/templates/menu_statistics_dd_site.tpl b/modules/statistics/templates/menu_statistics_dd_site.tpl new file mode 100755 index 0000000..c2bdf5a --- /dev/null +++ b/modules/statistics/templates/menu_statistics_dd_site.tpl @@ -0,0 +1,29 @@ +

{$SiteName} Double Data Entry Completion Statistics {$CurrentProject.Name}

+ + + + + + + + + + {foreach from=$AllVisits item=visit} + + {/foreach} + + {section name=item loop=$data} + + + + {foreach from=$AllVisits item=visit name=VisitLoop} + + {/foreach} + + {/section} +
InstrumentCompletion CountIncomplete Candidates
  {$visit}
{$data[item].name}{$data[item].count} + {foreach from=$data[item].incompletes[$visit] item=Candidate name=CandLoop} + {$Candidate.PSCID} +
+ {/foreach} +
diff --git a/modules/statistics/templates/menu_statistics_mri_site.tpl b/modules/statistics/templates/menu_statistics_mri_site.tpl new file mode 100755 index 0000000..32b0680 --- /dev/null +++ b/modules/statistics/templates/menu_statistics_mri_site.tpl @@ -0,0 +1,43 @@ +{literal} + + + +{/literal} +
+ + + + +
+
+

{$SiteName} MRI Integrity Statistics

+ + + + + + + + {foreach from=$AllVisits item=visit} + + {/foreach} + + {section name=item loop=$data} + + + {foreach from=$AllVisits item=visit name=VisitLoop} + + {/foreach} + + {/section} +
Issue typeIncomplete Entries
 {$visit}
{$data[item].name}{foreach from=$data[item].incompletes[$visit] item=Candidate name=CandLoop} + {if $Candidate.test_url == "PF_Missing"} + +
+ {else} +
+
+ {/if} + {$Candidate.PSCID}
+ {/foreach} +
diff --git a/modules/statistics/templates/menu_statistics_site.tpl b/modules/statistics/templates/menu_statistics_site.tpl new file mode 100755 index 0000000..db23069 --- /dev/null +++ b/modules/statistics/templates/menu_statistics_site.tpl @@ -0,0 +1,28 @@ +

{$SiteName} Completion Statistics

+ + + + + + + + + + {foreach from=$AllVisits item=visit} + + {/foreach} + + {section name=item loop=$data} + + + + {foreach from=$AllVisits item=visit name=VisitLoop} + + {/foreach} + + {/section} +
InstrumentCompletion CountIncomplete Candidates
  {$visit}
{$data[item].name}{$data[item].count}{foreach from=$data[item].incompletes[$visit] item=Candidate name=CandLoop} + {$Candidate.PSCID} +
+ {/foreach} +
diff --git a/modules/statistics/templates/table_statistics.tpl b/modules/statistics/templates/table_statistics.tpl new file mode 100755 index 0000000..1fa3591 --- /dev/null +++ b/modules/statistics/templates/table_statistics.tpl @@ -0,0 +1,242 @@ +{* Template to create a fancy stats table, such as the MRI breakdown + or behavioural data breakdown. + + It requires the following variables to be set: + - Header + - DropdownOptions (options in the dropdown to change what you're seeing) + - DropdownName (name that the selection is submitted as) + - DropdownSelected (current option selected) + - Subprojects (the valid subprojectIDs from the config file) + - Subcategories (the ways this data is broken down) + - Centers (the valid centerIDs from the psc table) + - Visits (the visits to display) + - Data (the data that populates the table) + *} + +

{$SectionHeader}

+

{$TableHeader}

+

{$Disclamer}

+ +
+ {if $Subsection=="demographics" } +
+ {html_options id="DemographicInstrument" options=$DropdownOptions name="$DropdownName" selected=$DropdownSelected class="form-control"} +
+ Show Visit Labels + + {/if} + + {if $Subsection==mri } +
+ {html_options id="mri_type" options=$DropdownOptions name="$DropdownName" selected=$DropdownSelected class="form-control"} +
+ Show Visit Labels + + {/if} + + {if $Subsection=="data_entry" } +
+ {html_options id="BehaviouralInstrument" options=$DropdownOptions name="$DropdownName" selected=$DropdownSelected class="form-control"} +
+ Show Visit Labels + + {/if} +
+ +
+ + + + {assign var='colspan' value=count($Subcategories)} + {foreach key=proj item=name from=$Subprojects} + + {/foreach} + + + + + {foreach key=proj item=name from=$Subprojects} + {* Go through each category once, and add the total + for each cohort *} + {foreach key=subcategory item=category from=$Subcategories} + + {/foreach} + {/foreach} + + {* And then each category once for the totals *} + {foreach key=subcategory item=category from=$Subcategories} + + {/foreach} + + + {foreach item=center from=$Centers} + {* Calculation for the colspan is: + (number of subprojects + 1 for total) + x + (number of subcategories + 1 for percent) + +1 for timepoint list + *} + {assign var='colspan' value=(count($Subcategories))*(count($Subprojects)+1)} + + + + {foreach item=visit from=$Visits key=title} + + {assign var="rowtotal" value="0"} + + {foreach key="proj" item="value" from=$Subprojects} + {assign var="subtotal" value="0" } + {foreach key=sub item=subcat from=$Subcategories} + {if $subcat@index eq 0} + {assign var="Numerator" value=$data[$proj][$center.ID][$visit][$Subcategories.0]} + {assign var="subtotal" value={$data[$proj][$center.ID][$visit].total}} + {if $subtotal > 0 and $Numerator > 0} + {assign var="percent" value={math equation="x*y/z" x=$Numerator y=100 z=$subtotal format="%.0f"}} + {else} + {assign var="percent" value='0'} + {/if} + + {else} + + {/if} + {/foreach} + {/foreach} + {* Totals for row *} + {foreach key=sub item=subcat from=$Subcategories} + {if $subcat@index eq 0} + {assign var="Numerator" value=$data[$center.ID][$visit][$Subcategories.0]} + {assign var="rowtotal" value=$data[$center.ID][$visit].total} + {if $rowtotal > 0 and $Numerator > 0} + {assign var="percent" value={math equation="x*y/z" x=$Numerator y=100 z=$rowtotal format="%.0f"}} + {else} + {assign var="percent" value='0'} + {/if} + + {else} + + {/if} + {/foreach} + + {/foreach} + + + {assign var="totalsitetotal" value="0"} + {foreach key=proj item=value from=$Subprojects} + {assign var="sitetotal" value="0"} + {foreach key=sub item=subcat from=$Subcategories} + {if $subcat@index eq 0} + {assign var="Numerator" value=$data[$proj][$center.ID][$Subcategories.0]} + {assign var="sitetotal" value=$data[$proj][$center.ID].total} + {if $sitetotal > 0 and $Numerator > 0} + {assign var="percent" value={math equation="x*y/z" x=$Numerator y=100 z=$sitetotal format="%.0f"}} + {else} + {assign var="percent" value='0'} + {/if} + + {else} + + {/if} + {/foreach} + {/foreach} + {foreach key=sub item=subcat from=$Subcategories} + {if $subcat@index eq 0} + {assign var="Numerator" value=$data[$center.ID][$Subcategories.0]} + {assign var="totalsitetotal" value=$data[$center.ID].total} + {if $totalsitetotal > 0 and $Numerator > 0} + {assign var="percent" value={math equation="x*y/z" x=$Numerator y=100 z=$totalsitetotal format="%.0f"}} + {else} + {assign var="percent" value='0'} + {/if} + + {else} + + {/if} + {/foreach} + + {/foreach} + + + + {* Totals at the bottom *} + + {assign var='colspan' value=(count($Subcategories))*(count($Subprojects)+1)} + + + + {foreach from=$Visits item=visit key=title} + + + {foreach from=$Subprojects key=proj item=value} + {assign var="subtotal" value="0" } + {foreach key=sub item=subcat from=$Subcategories} + {if $subcat@index eq 0} + {assign var="Numerator" value=$data[$proj][$visit][$Subcategories.0]} + {assign var="subtotal" value={$data[$proj][$visit].total}} + {if $subtotal > 0 and $Numerator > 0} + {assign var="percent" value={math equation="x*y/z" x=$Numerator y=100 z=$subtotal format="%.0f"}} + {else} + {assign var="percent" value='0'} + {/if} + + {else} + + {/if} + {/foreach} + {/foreach} + {assign var="finaltotal" value="0" } + {foreach key=sub item=subcat from=$Subcategories} + {if $subcat@index eq 0} + {assign var="Numerator" value=$data[$visit][$Subcategories.0]} + {assign var="finaltotal" value=$data[$visit].total} + {if $finaltotal > 0 and $Numerator > 0} + {assign var="percent" value={math equation="x*y/z" x=$Numerator y=100 z=$finaltotal format="%.0f"}} + {else} + {assign var="percent" value='0'} + {/if} + + {else} + + {/if} + {/foreach} + + {/foreach} + + + + {foreach key=proj item=name from=$Subprojects} + {* Calculate the total instead of just looking it up, because of the potential + for invalid visit labels throwing off the lookup, or some visits intentionally + being excluded from stats *} + {assign var="total" value="0"} + {foreach key=sub item=subcat from=$Subcategories} + {if $subcat@index eq 0} + {assign var="Numerator" value=$data[$proj][$Subcategories.0]} + {assign var="total" value=$data[$proj].total} + {if $total > 0 and $Numerator > 0} + {assign var="percent" value={math equation="x*y/z" x=$Numerator y=100 z=$total format="%.0f"}} + {else} + {assign var="percent" value='0'} + {/if} + + {else} + + {/if} + {/foreach} + {/foreach} + {* Totals for grand total *} + {foreach key=sub item=subcat from=$Subcategories} + {if $subcat@index eq 0} + {assign var="Numerator" value=$data[$Subcategories.0]} + {assign var="totaltotal" value=$data.total} + {if $totaltotal > 0 and $Numerator > 0} + {assign var="percent" value={math equation="x*y/z" x=$Numerator y=100 z=$totaltotal format="%.0f"}} + {else} + {assign var="percent" value='0'} + {/if} + + {else} + + {/if} + {/foreach} + +
Subproject{$name|capitalize}Total Across Subprojects
Categories{$category}{$category}
{$center.LongName}
{$title}{$data[$proj][$center.ID][$visit][$subcat]|default:"0"} ({$percent}%){$data[$proj][$center.ID][$visit][$subcat]|default:"0"}{$data[$center.ID][$visit][$subcat]|default:"0"} ({$percent}%){$data[$center.ID][$visit][$subcat]|default:"0"}
Site Total Across All Visits{$data[$proj][$center.ID][$subcat]|default:"0"} ({$percent}%){$data[$proj][$center.ID][$subcat]|default:"0"}{$data[$center.ID][$subcat]|default:"0"} ({$percent}%){$data[$center.ID][$subcat]|default:"0"}
Total
{$title}{$data[$proj][$visit][$subcat]|default:"0"} ({$percent}%){$data[$proj][$visit][$subcat]|default:"0"}{$data[$visit][$subcat]|default:"0"} ({$percent}%){$data[$visit][$subcat]|default:"0"}
Grand Total{$data[$proj][$subcat]|default:"0"} ({$percent}%){$data[$proj][$subcat]|default:"0"}{$data[$subcat]|default:"0"} ({$percent}%){$data[$subcat]|default:"0"}
diff --git a/modules/statistics/test/Statistics_Test.php b/modules/statistics/test/Statistics_Test.php new file mode 100644 index 0000000..0a39f1c --- /dev/null +++ b/modules/statistics/test/Statistics_Test.php @@ -0,0 +1,40 @@ +safeGet($this->url . '/statistics/'); + + try { + // If this is the mobile view, we need to expand the dropdown + // before the stats links are visible. + $expand = $this->webDriver->findElement(WebDriverBy::ID("down")); + $expand->click(); + } catch(ElementNotVisibleException $e) { + // Using the desktop version, so the mobile link isn't visible and + // doesn't need to be clicked. + } + + // Ensure that Demographic Statistics link is there. There's nothing special + // about Demographics, it's just a randomly chosen default tab to ensure that + // something shows up. Ideally, this should loop through the StatisticsTabs + // table and ensure that they all appear. + try { + $link = $this->webDriver->findElement(WebDriverBy::PartialLinkText("Demographic Statistics")); + $this->assertContains("Demographic", $link->getText()); + } catch(NoSuchElementException $e) { + print $this->webDriver->getPageSource(); + $this->fail("Could not find demographic tab link"); + } + } + + public function testGeneralDescriptionTabLoads() + { + $this->safeGet($this->url . '/statistics/stats_general/?dynamictabs=dynamictabs'); + $header = $this->webDriver->findElement(WebDriverBy::XPath("//div[@id = 'page']/h2")); + $this->assertContains("Welcome to the statistics page", $header->getText()); + + } +} +?> diff --git a/modules/statistics/test/TestPlan.md b/modules/statistics/test/TestPlan.md new file mode 100644 index 0000000..edd9938 --- /dev/null +++ b/modules/statistics/test/TestPlan.md @@ -0,0 +1,47 @@ +#Statistics Test Plan + +1. Check that you can only access the page if you have the data_entry permission. +2. Try clicking on all the tabs. Do they appear to be working? +3. On each page, check that tables are aligned and look good, and columns and rows are properly/adequately labelled. + +### Demographic Statistics +4. Click on the Demographic Statistics tab. For the general statistics table, compare the data in the table to that in the database to ensure that it is being queried correctly. +5. Check the general statistics filters. Try filtering by both site and project separately. Check that the filters only apply to the general statistics table. +6. In the breakdown table, try filtering by each of the different instruments. Does each filter appear to be working? Check that it only applies to the breakdown table. +7. For the gender breakdown, compare the data in the table to that in the database to ensure that it is being queried correctly. +8. For the gender breakdown, check to see if the % Male is being calculated properly by looking at the data that's in the table. +9. For one of the data entry completeness breakdowns, compare the data in the table to that in the database to ensure that it is being queried correctly. +10. For one of the data entry completeness breakdowns, check to see if the % complete is being calculated properly by looking at the data that is in the table. + +### Behavioural Statistics +11. Click on the Behavioural Statistics tab. Try using the project filter Data Entry Statistics table. Does it work? Does it change what appears in the top table only (perhaps it should be made clearer that it only applies to the top table)? +12. For both tables, check that the % Completion makes sense in relation to the values in the table. +13. For both tables, check that the data in the table matches what is stored in the database. +14. Check that the "Please click here" links work in the Data Entry Statistics table. +15. After following this link, check that the incomplete candidates are correct in comparison to the incomplete forms in the database. +16. In the incomplete forms table, check that the candidate links take you to the appropriate candidate instrument forms. +17. Returning to the behavioural statistics page, check that the "Click here for breakdown per participant" link below the Data Entry Statistics table works. +18. Verify that this link takes you to page with completion statistics for all sites +19. Returning to the behaviouarl statistics page, check that the "Please click here" links work in the Double Data Entry Statistics table +20. After following this link, check that the candidates requiring double data entry are correct in comparison to the forms requiring double data entry in the database. +21. On the incomplete double data entry page, check that the candidate links take you to the appropriate data entry page for that candidate +22. Returning to the behavioral statistics tab, check that the "Click here for breakdown per participant for" link below the Double Data Entry Statistics table works. +23. Verify that this link takes you to page with double data entry statistics for all sites +24. Returning to the behavioural statistics page, verify that if you do not have the access_all_profiles permission you should not be able to click through to forms for candidates from other sites. +25. Check the the breadcrumbs from Completion stats page back to BVL stats tab within Statistics appear and allow you to use the "back" button to previous pages (future feature). + +### Reliability Statistics +26. Click on the Reliability Statistics tab. Try using the reliability filter. Check that you can filter by site and by projects, separately. +27. For one of the reliability modules, check that the data in each column makes sense in comparison to data in the database. +28. There should only be one reliability table on this page right? + +### MRI Statistics +29. Click on the MRI statistics tab. Try using the filter query. Filter by site and by project separately to ensure they are both working. +30. Look at the first table on this page (the first row is 'Complete' in MRI parameter form). Do the numbers in each cell match what is in the database. Should these columns have headers? +31. Looking at the MRI integrity statistics, do the numbers in the table make sense in comparison to what's stored in the database? +32. Try following the "Click Here for breakdown per participant" links. Do they take you to the appropriate page? +33. On the breakdown page you just loaded, are the incomplete candidates listed correct in comparison to what's listed as incomplete in the database? +34. Try clicking on one of the incomplete candidate links. Does it take you to the correct page in the imaging browser? +35. Returning to the main MRI statistics page, try using the scan breakdown filter. Filter by each type of scan. Does it work? +36. For one of the scan breakdowns, does the information in the table make sense in comparison to what is in the database? Does the % complete appear to be being calculated correctly. +37. Does the grand total row make sense with the data that is listed in the table? diff --git a/modules/user_accounts/css/user_accounts.css b/modules/user_accounts/css/user_accounts.css new file mode 100644 index 0000000..abd34e6 --- /dev/null +++ b/modules/user_accounts/css/user_accounts.css @@ -0,0 +1,7 @@ +.newUserAccountNotes { + padding-left:0%; +} + +.newUserAccountNotes li { + font-weight: bold; +} diff --git a/modules/user_accounts/php/NDB_Form_user_accounts.class.inc b/modules/user_accounts/php/NDB_Form_user_accounts.class.inc new file mode 100644 index 0000000..07afb26 --- /dev/null +++ b/modules/user_accounts/php/NDB_Form_user_accounts.class.inc @@ -0,0 +1,970 @@ + +* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 +* @link https://www.github.com/aces/Loris/ +*/ + +/** +* Implements the user account page +* +* @category Main +* @package User_Account +* @author Loris Team +* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 +* @link https://www.github.com/aces/Loris/ +*/ +class NDB_Form_User_Accounts extends NDB_Form +{ + /** + * Determines whether this form is in edit or create mode. + * + * @return boolean true if in crete mode, false otherwise. + */ + function isCreatingNewUser() + { + return $this->page == 'edit_user' && $this->identifier == ''; + } + + /** + * Controls who's got access to this page, namely those who have the + * 'user_accounts' and who either have permission + * 'user_accounts_multisite' or whose site matches the site of the user they + * are trying to edit. + * + * @return true if user has access, false otherwise. + */ + function _hasAccess() + { + // create user object + $editor =& User::singleton(); + + if ($this->page == 'edit_user') { + if (!$this->isCreatingNewUser()) { + $user =& User::factory($this->identifier); + } + + if ($editor->hasPermission('user_accounts')) { + if ($editor->hasPermission('user_accounts_multisite')) { + return true; + } + + if ($this->isCreatingNewUser()) { + return true; + } + + return $editor->getData('CenterID') == $user->getData('CenterID'); + } + + return false; + } + + // User can always access My Preferences page + return true; + } + + /** + * Computes the initial values this page will be filled with. + * + * @return the default values for the initial state of this page. + */ + function _getDefaults() + { + $defaults = array(); + + if (!$this->isCreatingNewUser()) { + $user =& User::factory($this->identifier); + // get the user defaults + $defaults = $user->getData(); + // remove the password hash + unset($defaults['Password_md5']); + + // get the user's permissions + $perms = $user->getPermissionIDs(); + + // set the user's permission defaults + foreach ($perms as $value) { + $defaults["permID[$value]"] = 'on'; + } + + // Prevent Javascript injection on all fields + $defaults['UserID'] = htmlspecialchars($defaults['UserID']); + $defaults['First_name'] = htmlspecialchars($defaults['First_name']); + $defaults['Last_name'] = htmlspecialchars($defaults['Last_name']); + $defaults['Real_name'] = htmlspecialchars($defaults['Real_name']); + $defaults['Email'] = htmlspecialchars($defaults['Email']); + $defaults['Degree'] = htmlspecialchars($defaults['Degree']); + $defaults['Institution'] = htmlspecialchars($defaults['Institution']); + $defaults['Address'] = htmlspecialchars($defaults['Address']); + $defaults['City'] = htmlspecialchars($defaults['City']); + $defaults['State'] = htmlspecialchars($defaults['State']); + $defaults['Zip_code'] = htmlspecialchars($defaults['Zip_code']); + $defaults['Country'] = htmlspecialchars($defaults['Country']); + $defaults['Fax'] = htmlspecialchars($defaults['Fax']); + + } + + return $defaults; + } + + /** + * Determines if the user is currently editing his/her own account. + * + * @return true if the user is editing his/her own account, false otherwise. + */ + function _isEditingOwnAccount() + { + $editor = &User::singleton(); + return !$this->isCreatingNewUser() + && ($editor->getUsername() == $this->identifier); + } + + /** + * Processes the data entered in the form. + * + * @param array $values values entered in the form. + * + * @return void + */ + function _process($values) + { + $config = NDB_Config::singleton(); + + //The arrays that contain the edited permissions + $permissionsRemoved = array(); + $permissionsAdded = array(); + + $editor =& User::singleton(); + + // build the "real name" + $values['Real_name'] = $values['First_name'] . ' ' . $values['Last_name']; + + //create the user + if (!is_null($this->identifier)) { + $user =& User::factory($this->identifier); + } else { + // Since the form has been validated there are two possibilities: + // - UID is set + // - UID is not set but the "Match UID to email" checkbox is checked + $effectiveUID = $values['NA_UserID'] == 'on' + ? $values['Email'] : $values['UserID']; + $user =& User::factory($effectiveUID); + } + + ////Get the current permissions///// + $current_permissionids = $user->getPermissionIDs(); + + $permIDs = array(); + // store the permission IDs + if (!empty($values['permID'])) { + $permIDs = $values['permID']; + } + unset($values['permID']); + + // store whether to send an email or not + if (!empty($values['SendEmail'])) { + $send = $values['SendEmail']; + } + unset($values['SendEmail']); + + //store the supervisors emails + if (!empty($values['supervisorEmail'])) { + $supervisorEmails = $values['supervisorEmail']; + } + unset($values['supervisorEmail']); + + // make user name match email address + if (!empty($values['NA_UserID'])) { + $values['UserID'] = $values['Email']; + } + if ($this->isCreatingNewUser()) { + $values['UserID'] = trim($values['UserID']); + } + unset($values['NA_UserID']); + + // generate new password + if (!empty($values['NA_Password'])) { + $values['Password_md5'] = User::newPassword(); + $values['Password_expiry'] = '0000-00-00'; + } + unset($values['NA_Password']); + + // If editing a user and nothing was specified in the password text field + // remove Password_md5 from the value set, otherwise Password_md5 + // will be set to '' by the system + if ($values['Password_md5'] == '' && !$this->isCreatingNewUser()) { + unset($values['Password_md5']); + } + + // make the set + foreach ($values as $key => $value) { + $set[$key] = $value; + } + + // update the user + if ($this->isCreatingNewUser()) { + // insert a new user + $success = User::insert($set); + $user =& User::factory($set['UserID']); + } else { + // update the user + $user =& User::factory($this->identifier); + $success = $user->update($set); + } + + // prepend two random characters + if (isset($set['Password_md5'])) { + // Update CouchDB. Must do before password is salted/hashed. + $expiry = isset($values['Password_expiry']) + ? $values['Password_expiry'] : null; + $user->updatePassword($set['Password_md5'], $expiry); + } + + // update the user permissions if applicable + // If the user is editing his/her own account, skip the part where + // changes to the permissions are handled (there should not be any + // change to the user's permission set since all the checkboxes are + // disabled) + if (!$this->_isEditingOwnAccount()) { + $success = $user->removePermissions(); + + // Check for new permissions + if (!empty($permIDs)) { + foreach ($permIDs as $key => $value) { + /* if the user didn't have the permission + and the permission is now assigned then insert + insert into the user_account_history as 'I' + */ + if (!(in_array($key, $current_permissionids))) { + $user->insertIntoUserAccountHistory($key, 'I'); + $permissionsAdded[] = $this->getDescriptionUsingPermID($key); + } + } + } + + if (!empty($current_permissionids)) { + // Check for permissions that are to be deleted + foreach ($current_permissionids as $key) { + //if the permission existed before and it's removed now/// + ///Then insert into the user_account_history as 'D' + if (!in_array($key, array_keys($permIDs))) { + $user->insertIntoUserAccountHistory($key, 'D'); + $permissionsRemoved[] + = $this->getDescriptionUsingPermID($key); + } + } + } + + // send the selected supervisors an email + // (only if permissions have changed for the user) + if (isset($supervisorEmails)) { + foreach ($supervisorEmails as $email => $checkValue) { + if (!empty($permissionsAdded) || !empty($permissionsRemoved)) { + if ($checkValue == 'on') { + $msg_data['current_user'] = $editor->getFullname(); + $msg_data['study'] = $config->getSetting('title'); + $msg_data['realname'] = $values['Real_name']; + $msg_data['username'] = $user->getUsername(); + $msg_data['permissions_added'] = $permissionsAdded; + $msg_data['permissions_removed'] = $permissionsRemoved; + Email::send( + $email, + 'permissions_change_notify_supervisor.tpl', + $msg_data + ); + } + } + } + } + + if (!empty($permIDs)) { + $user->addPermissions(array_keys($permIDs)); + } + } + + // send the user an email + if (!empty($send)) { + // create an instance of the config object + $config =& NDB_Config::singleton(); + + // send the user an email + $msg_data['study'] = $config->getSetting('title'); + $msg_data['url'] = $config->getSetting('url'); + $msg_data['realname'] = $values['Real_name']; + $msg_data['username'] = $user->getUsername(); + $msg_data['password'] = $values['Password_md5']; + + $template = (is_null($this->identifier)) + ? 'new_user.tpl' : 'edit_user.tpl'; + Email::send($values['Email'], $template, $msg_data); + } + + $this->tpl_data['success'] = true; + $this->form->freeze(); + } + + /** + * Controls the output/behaviour of the form when in "edit" mode + * (i.e user already exists in the database). + * + * @return void + */ + // @codingStandardsIgnoreStart + function edit_user() + { + // @codingStandardsIgnoreEnd + $this->redirect = "test_name=$this->name"; + + ///get the value for additional_user_info flag + $config =& NDB_Config::singleton(); + $additional_user_info = $config->getSetting('additional_user_info'); + + //------------------------------------------------------------ + + // it is a new user + if ($this->identifier == '') { + // user name + $group[] = $this->createText('UserID', 'User name'); + $group[] = $this->createCheckbox( + 'NA_UserID', + 'Make user name match email address' + ); + $this->addGroup( + $group, + 'UserID_Group', + 'User name', + $this->_GUIDelimiter, + false + ); + unset($group); + + } else { + // It is an existing user: + // display user name + $this->addScoreColumn('UserID', 'User name'); + } + + // password + $group[] = $this->createPassword('Password_md5'); + $group[] = $this->createCheckbox('NA_Password', 'Generate new password'); + $this->addGroup( + $group, + 'Password_Group', + 'Password', + $this->_GUIDelimiter, + false + ); + $this->addPassword('__Confirm', 'Confirm Password'); + unset($group); + + // The supplied pattern is: + // - must have at least one non-whitespace characters + // - once leading and trailing spaces are stripped, the field should + // not exceed 120 chars + $onInvalidMsg + = "this.setCustomValidity('First name is required and " + . "should not exceed 120 characters')"; + $this->addBasicText( + 'First_name', + 'First name', + array( + 'oninvalid' => $onInvalidMsg, + 'onchange' => "this.setCustomValidity('')", + 'pattern' => '^\s*\S.{0,119}\s*$', + 'required' => true, + ) + ); + // The supplied pattern is: + // - must have at least one non-whitespace characters + // - once leading and trailing spaces are stripped, the field should + // not exceed 120 chars + $onInvalidMsg + = "this.setCustomValidity('Last name is required and " + . "should not exceed 120 characters')"; + $this->addBasicText( + 'Last_name', + 'Last name', + array( + 'oninvalid' => $onInvalidMsg, + 'onchange' => "this.setCustomValidity('')", + 'pattern' => '^\s*\S.{0,119}\s*$', + 'required' => true, + ) + ); + + // extra info + + ////if the option is not set or if it's and it's true then display it + + if ($additional_user_info) { + $this->addBasicText('Degree', 'Degree'); + $this->addBasicText('Position_title', 'Academic Position'); + $this->addBasicText('Institution', 'Institution'); + $this->addBasicText('Department', 'Department'); + $this->addBasicText('Address', 'Street Address'); + $this->addBasicText('City', 'City'); + $this->addBasicText('State', 'State/Province'); + $this->addBasicText('Zip_code', 'Zip/Postal Code'); + $this->addBasicText('Country', 'Country'); + $this->addBasicText('Fax', 'FAX'); + } + + // email address + $group[] = $this->createText('Email', 'Email address'); + $group[] = $this->createCheckbox('SendEmail', 'Send email to user'); + $this->addGroup( + $group, + 'Email_Group', + 'Email address', + $this->_GUIDelimiter, + false + ); + unset($group); + + // Add a confirm email text field only if creating a new user + // (to make sure that account creation email goes through) + if ($this->isCreatingNewUser()) { + $this->addBasicText('__ConfirmEmail', 'Confirm Email'); + } + + //------------------------------------------------------------ + + // get user permissions + $editor =& User::singleton(); + + // center ID + if ($editor->hasPermission('user_accounts_multisite')) { + // get the list of study sites - to be replaced by the Site object + $siteOptions =& Utility::getSiteList(false); + } else { + // allow only to add to their own site + $siteOptions + = array( + $editor->getData('CenterID') => $editor->getData('Site'), + ); + } + $this->addSelect('CenterID', 'Site', $siteOptions); + + // active + $this->addSelect('Active', 'Active', array('Y' => 'Yes', 'N' => 'No')); + $this->addSelect( + 'Pending_approval', + 'Pending approval', + array( + 'Y' => 'Yes', + 'N' => 'No', + ) + ); + + //------------------------------------------------------------ + + // get the editor's permissions + $perms = $editor->getPermissionsVerbose(); + $lastRole = ''; + foreach ($perms as $row) { + if ($row['type'] != $lastRole) { + $lastRole = $row['type']; + $group[] = $this->form->createElement( + 'static', + null, + null, + '' + . "

" + .ucwords($row['type']) + . '

' + . "
" + ); + } + + // If the user is editing his/her own account, disable all permission + // checkboxes. Note that they will not be submitted when the form is + // saved + $attribs = array("class" => "perm_$lastRole"); + if ($this->_isEditingOwnAccount()) { + $attribs['disabled'] = true; + } + $group[] = $this->createCheckbox( + 'permID['.$row['permID'].']', + htmlspecialchars($row['description']) . "
", + $attribs + ); + } + $this->addGroup($group, 'PermID_Group', 'Permissions', "", false); + unset($group); + + //getting users name and emails to create checkboxes + // to email supervisors on permissions changes + $DB_factory =& NDB_Factory::singleton(); + $DB = $DB_factory->database(); + + $query = "SELECT u.Real_Name, u.email FROM permissions p + JOIN user_perm_rel up ON (p.permID = up.PermID) + JOIN users u ON (u.ID = up.userID) + WHERE p.code = 'send_to_dcc'"; + + $results = $DB->pselect($query, array()); + + $group[] = $this->form->createElement( + 'static', + null, + null, + '
' + . "

" + . "Data Supervisors to Email

" + . "
" + ); + + $attribs = $this->_isEditingOwnAccount() ? array('disabled' => true) : null; + foreach ($results as $row) { + $group[] = $this->createCheckbox( + 'supervisorEmail[' . $row['email'] .']', + htmlspecialchars($row['Real_Name']) . "
", + $attribs + ); + } + + $this->addGroup($group, 'Supervisors_Group', 'Supervisors', "", false); + unset($group); + + if (!$this->isCreatingNewUser()) { + $user =& User::factory($this->identifier); + + // add hidden permissions if editor has less permissions than user + // being edited + $perms = array_diff( + $user->getPermissionIDs(), + $editor->getPermissionIDs() + ); + foreach ($perms as $value) { + $this->addHidden("permID[$value]", 1); + } + } + + //------------------------------------------------------------ + + // unique key and password rules + $this->form->addFormRule(array(&$this, '_validateEditUser')); + } + + /** + * Controls the output/behaviour of the form is used to edit + * the user's preferences. + * + * @return void + */ + // @codingStandardsIgnoreStart + function my_preferences() + { + // @codingStandardsIgnoreEnd + $this->identifier = $_SESSION['State']->getUsername(); + + ///get the value for additional_user_info flag + $config =& NDB_Config::singleton(); + $additional_user_info = $config->getSetting('additional_user_info'); + + //------------------------------------------------------------ + + // user name + $this->addScoreColumn('UserID', 'User name'); + + // full name + // The supplied pattern is: + // - must have at least one non-whitespace characters (i.e. required) + // - once leading and trailing spaces are stripped, the field should + // not exceed 120 chars + $fistNameInvalidMsg = "First name is required and " + . "should not exceed 120 characters"; + $this->addBasicText( + 'First_name', + 'First name', + array( + 'oninvalid' => "this.setCustomValidity('$firstNameInvalidMsg')", + 'onchange' => "this.setCustomValidity('')", + 'pattern' => '^\s*\S.{0,119}\s*$', + 'required' => true, + ) + ); + // The supplied pattern is: + // - must have at least one non-whitespace characters (i.e. required) + // - once leading and trailing spaces are stripped, the field should + // not exceed 120 chars + $lastNameInvalidMsg = "Last name is required and " + . "should not exceed 120 characters"; + $this->addBasicText( + 'Last_name', + 'Last name', + array( + 'oninvalid' => "this.setCustomValidity('$lastNameInvalidMsg')", + 'onchange' => "this.setCustomValidity('')", + 'pattern' => '^\s*\S.{0,119}\s*$', + 'required' => true, + ) + ); + + // extra info + ////if the option is not set or if it's and it's true then display it + if ($additional_user_info) { + $this->addBasicText('Degree', 'Degree'); + $this->addBasicText('Position_title', 'Academic Position'); + $this->addBasicText('Institution', 'Institution'); + $this->addBasicText('Department', 'Department'); + $this->addBasicText('Address', 'Street Address'); + $this->addBasicText('City', 'City'); + $this->addBasicText('State', 'State/Province'); + $this->addBasicText('Zip_code', 'Zip/Postal Code'); + $this->addBasicText('Country', 'Country'); + $this->addBasicText('Fax', 'FAX'); + } + + // email address + $this->addBasicText( + 'Email', + 'Email address', + array( + 'oninvalid' => "this.setCustomValidity('Email address is required')", + 'onchange' => "this.setCustomValidity('')", + ) + ); + + // email address rules + $this->addRule('Email', 'Email address is required', 'required'); + $this->addRule('Email', 'Your email address must be valid', 'email'); + $this->addRule( + 'Email', + 'Your email address must be less than 255 characters long', + 'maxlength', + 255 + ); + + // password + $this->form->addElement('password', 'Password_md5', 'New Password'); + $this->form->addElement('password', '__Confirm', 'Confirm Password'); + + // document repository notifications + $editor =& User::singleton(); + if ($editor->hasPermission('document_repository_view') + || $editor->hasPermission('document_repository_delete') + ) { + $doc_Repo_Notifications_Options + = array( + 'N' => 'No', + 'Y' => 'Yes', + ); + $this->addSelect( + 'Doc_Repo_Notifications', + 'Receive Document Repository email notifications', + $doc_Repo_Notifications_Options + ); + } + + //------------------------------------------------------------ + + // unique key and password rules + $this->form->addFormRule(array(&$this, '_validateMyPreferences')); + } + + + /** + * Validates the data entered in the edit user form. + * + * @param array $values what the user entered on the form. + * + * @return array $errors all the errors found. + */ + function _validateEditUser($values) + { + // create DB object + $DB =& Database::singleton(); + $errors = array(); + + //============================================ + // Validate UserID and NA_UserID + //============================================ + if ($this->isCreatingNewUser()) { + // Clicked on "UID == email" and specified a UID + if (!empty($values['UserID']) && $values['NA_UserID'] == 'on') { + $errors['UserID_Group'] + = 'You cannot enter a user name ' + . 'if you want it to match the email address'; + } elseif (empty($values['UserID']) && $values['NA_UserID'] != 'on') { + // Not clicked on "UID == email" and not specified a UID + $errors['UserID_Group'] + = 'You must enter a user name ' + . 'or choose to make it match the email address'; + } elseif (!empty($values['UserID']) + || ($values['NA_UserID'] == 'on' && $values['Email']) + ) { + // Either specified a UID or clicked on "UID = email" + // with a non-empty email + $effectiveUID = empty($values['UserID']) + ? $values['Email'] : $values['UserID']; + + $effectiveUID = trim($effectiveUID); + + // check username's uniqueness + $result = $DB->pselectOne( + "SELECT COUNT(*) FROM users WHERE UserID = :UID", + array('UID' => $effectiveUID) + ); + + if ($result > 0) { + $errors['UserID_Group'] = 'The user name already exists'; + } + + if (strlen($effectiveUID) > 255) { + $errors['UserID_Group'] + = 'The user name must not exsceed 255 characters'; + } + } + } + + //================================== + // Password validation + //================================== + if (!is_null($this->identifier)) { + $pass = $DB->pselectOne( + "SELECT COALESCE(Password_hash, Password_md5) " + . "as Current_password FROM users WHERE UserID = :UID", + array('UID' => $this->identifier) + ); + + //case of new user the password column will be null + //so either password should be set or + // password should be generated + if (is_null($pass) + && empty($values['Password_md5']) + && $values['NA_Password'] != 'on' + ) { + $errors['Password_Group'] + = 'Please specify password or click Generate new password'; + } + } + + // if password is user-defined, and user wants to change password + if (empty($values['NA_Password']) + && (!empty($values['Password_md5']) || !empty($values['__Confirm'])) + ) { + $isPasswordStrong = User::isPasswordStrong( + $values['Password_md5'], + array( + $values['__Confirm'], + isset($values['UserID']) ? $values['UserID'] : $this->identifier, + $values['Email'], + ), + array( + '==', + '!=', + '!=', + ) + ); + + // check password strength + if (!$isPasswordStrong) { + $errors['Password_Group'] + = 'The password is weak, or the passwords do not match'; + } else { + // New password must be different than current one + if (!$this->_passwordChanged($DB, $values['Password_md5'])) { + $errors['Password_Group'] = 'New and old passwords ' + . 'are identical: please choose another one'; + } + } + } + + // if password is generated then the email user button should be clicked + if ($values['NA_Password'] == "on" && $values['SendEmail'] != "on") { + $errors['Email_Group'] + = 'When generating a new password, ' + . 'please notify the user by checking Send email to user box'; + } + + if ($values['NA_Password'] == 'on' && $values['Password_md5'] != '') { + $errors['Password_Group'] = 'You must leave the password field empty ' + . 'if you want the system to generate one for you'; + } + + if (is_null($this->identifier) + && ($values['NA_Password'] != 'on') + && empty($values['Password_md5']) + ) { + $errors['Password_Group'] = 'Password is required'; + } + + //====================================== + // Validate Email + //====================================== + + // If an email was entered + if (!empty($values['Email'])) { + $emailError = $this->_getEmailError($DB, $values['Email']); + if (!is_null($emailError)) { + $errors['Email_Group'] = $emailError; + } elseif ($this->isCreatingNewUser()) { + if ($values['Email'] != $values['__ConfirmEmail']) { + $errors['__ConfirmEmail'] = 'Email and confirmed email ' + . ' do not match'; + } + } + } else { + // No email entered: error + $errors['Email_Group'] = 'You must enter an email address'; + } + + return $errors; + } + + /** + * Determines if the new password that the user entered is different + * than its current password (the one stored in the database). + * + * @param Database $db database object. + * @param string $newPassword the new password the user entered. + * + * @return bool true if the password changed, false otherwise. + */ + function _passwordChanged($db, $newPassword) + { + //--- Get current password stored in database + $passwordQuery + = "SELECT Password_hash, Password_md5 + FROM users + WHERE UserID = :UID"; + + $passwords = $db->pselectRow( + $passwordQuery, + array('UID' => $this->identifier) + ); + + // If we are using PHP5.5+ and entry Password_has in the DB is not null + // use method password_verify to check if the password changed + if (function_exists('password_verify') + && !is_null($passwords['Password_hash']) + ) { + return !password_verify($newPassword, $passwords['Password_hash']); + } elseif (!function_exists('password_verify') + && !is_null($passwords['Password_md5']) + ) { + // If PHP version < 5.5, use old MD5Unsalt method to check for + // password change + return !User::MD5Unsalt($newPassword, $passwords['Password_md5']); + } + + return true; + } + + /** + * Validates the data entered in the form when editing one's preferences. + * + * @param array $values values the user entered in the form. + * + * @return array $errors all the errors found. + */ + function _validateMyPreferences($values) + { + // create DB object + $DB =& Database::singleton(); + $errors = array(); + + // if password is user-defined, and user wants to change password + if (!empty($values['Password_md5'])) { + // check password strength + $isPasswordStrong = User::isPasswordStrong( + $values['Password_md5'], + array( + $values['__Confirm'], + $this->identifier, + $values['Email'], + ), + array( + '==', + '!=', + '!=', + ) + ); + if (!$isPasswordStrong) { + $errors['Password_md5'] + = 'The password is weak, or the passwords do not match'; + } else { + // New password must be different than current one + if (!$this->_passwordChanged($DB, $values['Password_md5'])) { + $errors['Password_Group'] = 'New and old passwords ' + . 'are identical: please choose another one'; + } + } + } + + // Validate email + $emailError = $this->_getEmailError($DB, $values['Email']); + if (!is_null($emailError)) { + $errors['Email'] = $emailError; + } + + return $errors; + } + + /** + * Validates that en email address entered for a given user + * (either new or existing) is valid and unique. + * + * @param Database $DB database object. + * @param string $email user's email. + * + * @return string error message if email is invalid, null otherwise. + */ + private function _getEmailError($DB, $email) + { + // remove illegal characters + $email = filter_var($email, FILTER_SANITIZE_EMAIL); + + // check email address' uniqueness + $query = "SELECT COUNT(*) FROM users WHERE Email = :VEmail "; + $params = array('VEmail' => $email); + if (!is_null($this->identifier)) { + $query .= " AND userID <> :UID"; + $params['UID'] = $this->identifier; + } + $result = $DB->pselectOne($query, $params); + + // Email already exists in database + if ($result > 0) { + return 'The email address already exists'; + } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + // If email not syntactically valid + return "Invalid email address"; + } + + return null; + } + + /** + * Gets the complete description for a permission. + * + * @param int $permID permission ID. + * + * @return string the description. + */ + function getDescriptionUsingPermID($permID) + { + $db_factory =& NDB_Factory::singleton(); + $db = $db_factory->database(); + + $permission = $db->pselectOne( + "SELECT Description FROM permissions WHERE permID =:pID", + array('pID' => $permID) + ); + if (is_array($permission) && count($permission)) { + list(,$description) = each($permission[0]); + } + return $permission; + } +} +?> diff --git a/modules/user_accounts/php/NDB_Menu_Filter_user_accounts.class.inc b/modules/user_accounts/php/NDB_Menu_Filter_user_accounts.class.inc new file mode 100644 index 0000000..4b3f322 --- /dev/null +++ b/modules/user_accounts/php/NDB_Menu_Filter_user_accounts.class.inc @@ -0,0 +1,72 @@ +hasPermission('user_accounts'); + } + + function _setupVariables() + { + $user =& User::singleton(); + + // the base query + $query = " FROM users LEFT JOIN psc ON (psc.CenterID = users.CenterID) WHERE 1=1 "; + if (!$user->hasPermission('user_accounts_multisite')) { + $query .= " AND users.CenterID = '" . $user->getData('CenterID') . "' "; + } + + // set the class variables + $this->columns = array("COALESCE(psc.Name,'Not Assigned') AS PSC", 'UserID AS Username', 'Real_name AS Full_name', 'Email', 'Active', 'Pending_approval'); + $this->query = $query; + $this->order_by = 'Username'; + $this->validFilters = array('users.CenterID', 'users.UserID', 'users.Real_name', 'users.Email', 'users.Active', 'users.Examiner', 'users.Pending_approval'); + + $this->formToFilter = array( + 'centerID' => 'users.CenterID', + 'active' => 'users.Active', + 'userID' => 'users.UserID', + 'real_name' => 'users.Real_name', + 'email' => 'users.Email', + 'pending' => 'users.Pending_approval' + ); + return true; + } + + + function _setFilterForm() + { + // create user object + $user =& User::singleton(); + + // PSC + if ($user->hasPermission('user_accounts_multisite')) { + // get the list of study sites - to be replaced by the Site object + $list_of_sites = Utility::getSiteList(false); + if(is_array($list_of_sites)) $list_of_sites = array('' => 'All') + $list_of_sites; + } + else { + // allow only to view own site data + $site =& Site::singleton($user->getData('CenterID')); + $list_of_sites = array($user->getData('CenterID') => $user->getData('Site')); + } + + // add form elements + $this->addSelect('centerID', 'Site:', $list_of_sites); + $this->addSelect('active', 'Active:', array('' => 'All', 'Y' => 'Y', 'N' => 'N')); + $this->addBasicText('userID', 'Username:'); + $this->addBasicText('real_name', 'Full name:'); + $this->addBasicText('email', 'Email:'); + $this->addSelect('pending', 'Pending Approval:', array(''=>'All', 'N'=>'N', 'Y'=>'Y')); + + return true; + } +} +?> diff --git a/modules/user_accounts/templates/#form_edit_user.tpl# b/modules/user_accounts/templates/#form_edit_user.tpl# new file mode 100644 index 0000000..d28baa3 --- /dev/null +++ b/modules/user_accounts/templates/#form_edit_user.tpl# @@ -0,0 +1,330 @@ +
+{literal} + + +{/literal} +
+ {if $form.errors} + + {/if} +
+
+

Password Rules

+
    +
  • The password must be at least 8 characters long
  • +
  • The password must contain at least 1 letter, 1 number and 1 character from !@#$%^&*()
  • +
  • The password and the user name must not be the same
  • +
  • The password and the email address must not be the same
  • +
+

Notes

+
    +
  • It is recommended to use an email address as the username, for clarity and uniqueness.
  • +
  • When generating a new password, please notify the user by checking 'Send email to user' box below!
  • +
+
+
+

Add/Edit User

+ + + {if $form.errors.UserID_Group} +
+ {else} +
+ {/if} + {if $form.UserID_Group != null} + +
+ {$form.UserID_Group.html} +
+ {if $form.errors.UserID_Group} +
+ {$form.errors.UserID_Group} +
+ {/if} + {else} + +
+ {$form.UserID.html} +
+ + {/if} +
+ +
+ {if $form.errors.Password_Group} +
+ {else} +
+ {/if} + +
+ {$form.Password_Group.html} +
+ {if $form.errors.Password_Group} +
+ {$form.errors.Password_Group} +
+ {/if} +
+
+ +
+ {$form.__Confirm.html} +
+
+ + {if $form.errors.First_name} +
+ {else} +
+ {/if} + +
+ {$form.First_name.html} +
+ {if $form.errors.First_name} +
+ {$form.errors.First_name} +
+ {/if} +
+ {if $form.errors.Last_name} +
+ {else} +
+ {/if} + +
+ {$form.Last_name.html} +
+ {if $form.errors.Last_name} +
+ {$form.errors.Last_name} +
+ {/if} +
+ {if $form.Degree} +
+ +
+ {$form.Degree.html} +
+
+ {/if} + {if $form.Position_title} +
+ +
+ {$form.Position_title.html} +
+
+ {/if} + {if $form.Institution} +
+ +
+ {$form.Institution.html} +
+
+ {/if} + {if $form.Department} +
+ +
+ {$form.Department.html} +
+
+ {/if} + {if $form.Address} +
+ +
+ {$form.Address.html} +
+
+ {/if} + {if $form.City} +
+ +
+ {$form.City.html} +
+
+ {/if} + {if $form.State} +
+ +
+ {$form.State.html} +
+
+ {/if} + {if $form.ZipCode} +
+ +
+ {$form.Zip_code.html} +
+
+ {/if} + {if $form.Country} +
+ +
+ {$form.Country.html} +
+
+ {/if} + {if $form.Fax} +
+ +
+ {$form.Fax.html} +
+
+ {/if} + {if $form.errors.Email_Group} +
+ {else} +
+ {/if} + +
+ {$form.Email_Group.html} +
+ {if $form.errors.Email_Group} +
+ {$form.errors.Email_Group} +
+ {/if} +
+
+ +
+ {$form.CenterID.html} +
+
+
+ +
+ {$form.Active.html} +
+
+
+ +
+ {$form.Pending_approval.html} +
+
+
+ +
+
+ {$form.PermID_Group.html} +
+
+
+
+ +
+
+ {$form.Supervisors_Group.html} +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + diff --git a/modules/user_accounts/templates/form_edit_user.tpl b/modules/user_accounts/templates/form_edit_user.tpl new file mode 100644 index 0000000..7aabc49 --- /dev/null +++ b/modules/user_accounts/templates/form_edit_user.tpl @@ -0,0 +1,351 @@ +
+ +{literal} + +{/literal} +
+ {if $form.errors} + + {/if} +
+
+

Password Rules

+
    +
  • The password must be at least 8 characters long
  • +
  • The password must contain at least 1 letter, 1 number and 1 character from !@#$%^&*()
  • +
  • The password and the user name must not be the same
  • +
  • The password and the email address must not be the same
  • +
+

Notes

+
    +
  • It is recommended to use an email address as the username, for clarity and uniqueness.
  • +
  • When generating a new password, please notify the user by checking 'Send email to user' box below!
  • +
+
+
+

Add/Edit User

+ + + {if $form.errors.UserID_Group} +
+ {else} +
+ {/if} + {if $form.UserID_Group != null} + +
+ {$form.UserID_Group.html} +
+ {if $form.errors.UserID_Group} +
+ {$form.errors.UserID_Group} +
+ {/if} + {else} + +
+ {$form.UserID.html} +
+ + {/if} +
+ +
+ {* {if $form.errors.Password_Group} +
+ {else} +
+ {/if} + +
+ {$form.Password_Group.html} +
+ {if $form.errors.Password_Group} +
+ {$form.errors.Password_Group} +
+ {/if} +
+
+ +
+ {$form.__Confirm.html} +
+
+ + *} + + {if $form.errors.First_name} +
+ {else} +
+ {/if} + +
+ {$form.First_name.html} +
+ {if $form.errors.First_name} +
+ {$form.errors.First_name} +
+ {/if} +
+ {if $form.errors.Last_name} +
+ {else} +
+ {/if} + +
+ {$form.Last_name.html} +
+ {if $form.errors.Last_name} +
+ {$form.errors.Last_name} +
+ {/if} +
+ {if $form.Degree} +
+ +
+ {$form.Degree.html} +
+
+ {/if} + {if $form.Position_title} +
+ +
+ {$form.Position_title.html} +
+
+ {/if} + {if $form.Institution} +
+ +
+ {$form.Institution.html} +
+
+ {/if} + {if $form.Department} +
+ +
+ {$form.Department.html} +
+
+ {/if} + {if $form.Address} +
+ +
+ {$form.Address.html} +
+
+ {/if} + {if $form.City} +
+ +
+ {$form.City.html} +
+
+ {/if} + {if $form.State} +
+ +
+ {$form.State.html} +
+
+ {/if} + {if $form.ZipCode} +
+ +
+ {$form.Zip_code.html} +
+
+ {/if} + {if $form.Country} +
+ +
+ {$form.Country.html} +
+
+ {/if} + {if $form.Fax} +
+ +
+ {$form.Fax.html} +
+
+ {/if} + {if $form.errors.Email_Group} +
+ {else} +
+ {/if} + +
+ {$form.Email_Group.html} +
+ {if $form.errors.Email_Group} +
+ {$form.errors.Email_Group} +
+ {/if} +
+ {if $form.__ConfirmEmail} + {if $form.errors.__ConfirmEmail} +
+ {else} +
+ {/if} + +
+ {$form.__ConfirmEmail.html} +
+ {if $form.errors.__ConfirmEmail} +
+ {$form.errors.__ConfirmEmail} +
+ {/if} +
+ {/if} +
+ +
+ {$form.CenterID.html} +
+
+
+ +
+ {$form.Active.html} +
+
+
+ +
+ {$form.Pending_approval.html} +
+
+
+ +
+
+ {$form.PermID_Group.html} +
+
+
+
+ +
+
+ {$form.Supervisors_Group.html} +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + diff --git a/modules/user_accounts/templates/form_my_preferences.tpl b/modules/user_accounts/templates/form_my_preferences.tpl new file mode 100644 index 0000000..428e273 --- /dev/null +++ b/modules/user_accounts/templates/form_my_preferences.tpl @@ -0,0 +1,84 @@ +
+
+

Password Rules

+
    +
  • The password must be at least 8 characters long
  • +
  • The password must contain at least 1 letter, 1 number and 1 character from !@#$%^&*()
  • +
  • The password and the user name must not be the same
  • +
  • The password and the email address must not be the same
  • +
+

Edit My Information

+ {foreach from=$form.errors item=error} +
    +
  • {$error}
  • +
+ {/foreach} +
+
+ +
+ {$form.UserID.html} +
+
+
+
+ +
+ {$form.First_name.html} +
+
+
+ +
+ {$form.Last_name.html} +
+
+ +
+ {$form.Email.html} +
+
+ {* +
+ +
+ {$form.Password_md5.html} +
+
+
+ +
+ {$form.__Confirm.html} +
+
+ i*} +
+ +
+ {$form.Doc_Repo_Notifications.html} +
+
+
+
+ +
+
+ +
+
+{$form.hidden} +
diff --git a/modules/user_accounts/templates/menu_user_accounts.tpl b/modules/user_accounts/templates/menu_user_accounts.tpl new file mode 100644 index 0000000..c5d441e --- /dev/null +++ b/modules/user_accounts/templates/menu_user_accounts.tpl @@ -0,0 +1,150 @@ + + + +
+
+
+
+
+ Selection Filter + + +
+
+
+
+ +
+ {$form.centerID.html} +
+
+
+ +
+ {$form.userID.html} +
+
+
+
+
+ +
+ {$form.active.html} +
+
+
+ +
+ {$form.real_name.html} +
+
+
+
+
+ +
+ {$form.pending.html} +
+
+
+ +
+ {$form.email.html} +
+
+
+
+
+ +
+ {$form.examiner.html} +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ + + + + + + + + +
+ + + + + + + + {section name=header loop=$headers} + + {/section} + + + + {section name=item loop=$items} + + + {section name=piece loop=$items[item]} + + {/section} + + {sectionelse} + + {/section} + + + +
No.{$headers[header].displayName}
+ {if $items[item][piece].name == "Username"} + {$items[item][piece].value|escape} + {else} + {$items[item][piece].value|escape} + {/if} +
No users found
+ + + diff --git a/modules/user_accounts/test/Lost_Password_Test_Plan.md b/modules/user_accounts/test/Lost_Password_Test_Plan.md new file mode 100644 index 0000000..fc8bd21 --- /dev/null +++ b/modules/user_accounts/test/Lost_Password_Test_Plan.md @@ -0,0 +1,9 @@ +# Lost Password Test Plan + +1. On the LORIS login page, click the "Forgot your password?" link and ensure you are taken to the lost password form. +2. Type a username that's not in use into the form and click "Submit". A warning should appear notifying the user that the name does not exist. +3. Enter your username in the form, or a username with your email attached to it. Click the submit button. A notice should come up saying that an email was sent. +4. Click the "Return to login screen". +5. Enter your username and the password emailed to you. Ensure that you can login with this password. You should be prompted to change your password upon successful login. +6. Change your password upon the prompt and log out again. +7. Try logging back in with your new password. \ No newline at end of file diff --git a/modules/user_accounts/test/Request_Account_test_plan.md b/modules/user_accounts/test/Request_Account_test_plan.md new file mode 100644 index 0000000..268d744 --- /dev/null +++ b/modules/user_accounts/test/Request_Account_test_plan.md @@ -0,0 +1,17 @@ +#Request account feature - Test Plan +*Stored under User Accounts module test/ directory* + +### Request Account form: +1. Verify that form returns error message for each field left blank +2. Verify that form returns appropriate error message for each field with invalid value entered (e.g. name < 3 chars, invalid email) +3. Verify that verification code is enforced (must match exactly) +4. Verify that new verification code loads on every refresh of the page +5. Verify that clicking "Submit" button with valid form data will load page acknowledging receipt (Thank you page) +6. Thank-you Receipt-acknowledgement page: test "Return to Loris login page" button + +###Approving new User Account Request: +7. Log in as another user who has permission:user_accounts and does Not have permission:user_accounts_multisite. Verify that new account request notification is counted in Dashboard (count has incremented). +8. Verify that Dashboard "Accounts pending approval" link will load the User Accounts module filtered for all accounts where Pending(=1/Yes) and Site is not yet assigned +9. Approve a new user account request (set Pending=No, set Site, check "Notify" and "Generate new password" boxes, set various permissions). +10. Verify that new user is notified by email when account is approved. +11. Once account is approved, try logging in with new user credentials diff --git a/modules/user_accounts/test/Reset_Password_Test_Plan.md b/modules/user_accounts/test/Reset_Password_Test_Plan.md new file mode 100644 index 0000000..8d78cde --- /dev/null +++ b/modules/user_accounts/test/Reset_Password_Test_Plan.md @@ -0,0 +1,12 @@ +# Reset Password Test Plan + +1. Change your password expiry in the backend so that it is a date in the past. +2. Login to LORIS. You should see an update password page. +3. Try out different passwords. When you type a password that is not in accordance with the listed rules, you should get an error notice: + - The password must be at least 8 characters long + - The password must contain at least 1 letter, 1 number and 1 character from !@#$%^&*() + - The password and the user name must not be the same + - The password and the email address must not be the same. +4. Try entering unmatched passwords. You should get an error notice. +5. Try entering the same password that you currently have. You should get an error notice. +5. Create a new password which complies with the rules and submit. Logout. Try logging back in with new password to see if it works. \ No newline at end of file diff --git a/modules/user_accounts/test/TestPlan b/modules/user_accounts/test/TestPlan new file mode 100644 index 0000000..8342578 --- /dev/null +++ b/modules/user_accounts/test/TestPlan @@ -0,0 +1,69 @@ + User Account module - Test plan + =============================== + +1. Make sure you do not have access to the User Account page if you do not have at least one of these permissions: + - There can be only one Highlander. + - User management / Survey module. +2. If you have access to the User Module, you should see all the users if you have permission 'Across all sites edit + create users'. Otherwise, only the users that belong to your site are shown. +3. Check that the Clear Form button works. +4. Search according to all criteria: site, user name, active, full name, pending approval, email and examiner. +5. Check that if Site is set to 'All' in the filter, users that have a different site than the user logged in are + returned by the search (if any). +6. Try to add a user with a user name already taken: check that you can't. + +When creating or editing a user: (subtest: edit_user) +======================================================== + +7. Check that password rules are enforced. +8. Check that saving fails if you do not enter at least these informations: + - User name. + - Password (and confirm password). + - First name. + - Last name. + - Email. +9. Check that if password and confirmed password do not match you get an error. +10. Check that if you do not enter an email address that is syntactically valid you get an error. +10a. Check that when creating a new user, there is an additional "Confirm email" text field on the page and that you + get an error message if email and confirmed email don't match when submitting the form. +10b. Make sure that the confirm email text field is not on the edit user page (only on the create new user page). +11. For an existing active user, edit the user's account and click 'Generate new password' and check 'Send email'. + Save. Check that an email is sent to the user with the new password. Check that the password rules are enforced + for this new password. Check that after logging in, the user is immediately asked to update his/her password. +12. Check that if creating a new user an email is sent to him/her (requires email server). Also check that when a new + user is logging in for the first time he/she is asked to change his/her password. +12a. Check that when creating a new user, leading and trailing spaces in the username are stripped. +12b. Check that you can create a new user with name 00 (double zero). +12c. Check that you can delete one of the additional fields (organization, fax, etc...) that was previously set and that the save is performed. +13. Check that if modifying a password for a user an email is sent to that user containing the new password (requires + email server). +14. Check that when editing a user account it is not possible to set the password to its actual value (i.e. it needs to change). +15. Check that if the 'Display additional information' entry is set to false in the admin module, fields Degree, + Academic Position, Institution, Department, Street Address, City, State/Province, Zip/Postal Code, Country and + FAX are not shown. +16. Setting the Pending Approval for user X prevents user X from logging in until his/her account is approved. +17. Setting the Active=”No” for user X prevents user X from logging in until his/her account is active again. +18 Check that modifications made to the basic user infos are displayed when the user table is reloaded. +19. When editing an existing user: Check that Reset button restores previous settings (does not wipe all settings, or + retain any changes). +20. When editing an existing user: Check that Back button takes you to the User Account page and does not save any + changes. +21. When editing an existing user: Clicking on the 'User Account' breadcrumb takes you to the User Account page + without saving any changes to the user profile. + +On the My Preferences page: +========================== + +22. Check that all users (even those with NO permissions) have access to the My Preferences page. +23. Change the user’s password. Check that the password rules are enforced.. +24. Check that if password and confirmed password do not match you get an error. +25. Check that saving fails if any field is left blank (except password). +26. Check that if you do not enter an email address that is syntactically valid you get an error. +27. Modify any field on the page and save, and go to the User Account page. Check that the modifications are + displayed when looking at the modified user account. +28. Set the 'Receive doc repo notifications' to No. Upload a new document (any). Check that no email is sent to the + user. +29. Set the 'Receive doc repo notifications' to Yes. Upload a new document (any). Check that an email is sent to the + user (requires email server). +30. Clicking on the 'User Account' breadcrumb takes you to the User Account page without saving any changes. If you + do not have access to the user account module, the system should tell you so. diff --git a/modules/user_accounts/test/user_accountsTest.php b/modules/user_accounts/test/user_accountsTest.php new file mode 100644 index 0000000..92b90ee --- /dev/null +++ b/modules/user_accounts/test/user_accountsTest.php @@ -0,0 +1,369 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://github.com/aces/Loris + */ +require_once __DIR__ + . "/../../../test/integrationtests/LorisIntegrationTest.class.inc"; +/** + * UserAccountsIntegrationTest + * + * @category Test + * @package Loris + * @author Nicolas Brossard + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://github.com/aces/Loris + */ +class UserAccountsIntegrationTest extends LorisIntegrationTest +{ + private static $_UNIT_TESTER = array( + 'Data Coordinating Center', + 'UnitTester', + 'Unit Tester', + 'tester@example.com', + 'Y', + 'N', + ); + private static $_ADMIN = array( + 'Data Coordinating Center', + 'admin', + 'Admin account', + 'admin@localhost', + 'Y', + 'N', + ); + /** + * Tests that, when loading the User accounts module, some + * text appears in the body. + * + * @return void + */ + function testUserAccountsDoespageLoad() + { + $this->safeGet($this->url . "/user_accounts/"); + $bodyText = $this->safeFindElement( + WebDriverBy::cssSelector("body") + )->getText(); + $this->assertContains("User Accounts", $bodyText); + } + /** + * Tests that, when loading the User accounts module > edit_user submodule, some + * text appears in the body. + * + * @return void + */ + function testUserAccountsEditUserDoespageLoad() + { + $this->safeGet($this->url . "/user_accounts/edit_user/"); + $bodyText = $this->safeFindElement( + WebDriverBy::cssSelector("body") + )->getText(); + $this->assertContains("Edit User", $bodyText); + $this->assertEquals( + "password", + $this->safeFindElement( + WebDriverBy::Name("Password_md5") + )->getAttribute("type") + ); + $this->assertEquals( + "checkbox", + $this->safeFindElement( + WebDriverBy::Name("NA_Password") + )->getAttribute("type") + ); + $this->assertEquals( + "password", + $this->safeFindElement( + WebDriverBy::Name("__Confirm") + )->getAttribute("type") + ); + } + /** + * Tests that, when loading the User accounts module > my_preference submodule + * some text appears in the body. + * + * @return void + */ + function testUserAccountsMyPreferencesDoespageLoad() + { + $this->safeGet($this->url . "/user_accounts/my_preferences/"); + $bodyText = $this->safeFindElement( + WebDriverBy::cssSelector("body") + )->getText(); + $this->assertContains("My Preferences", $bodyText); + } + /** + * Tests that searching for users using thei user IDs works + * + * @return void + */ + function testSearchForUsers() + { + $this->safeGet($this->url . "/user_accounts/"); + $bodyText = $this->safeFindElement( + WebDriverBy::cssSelector("body") + ); + $bodyText->getText(); + $this->_assertSearchBy( + array('userID' => 'my_nonexistent_user_ID'), + null + ); + $this->_assertSearchBy( + array('userID' => 'UnitTester'), + array(self::$_UNIT_TESTER) + ); + $this->_assertSearchBy( + array('userID' => 'unittester'), + array(self::$_UNIT_TESTER) + ); + $this->_assertSearchBy( + array('userID' => 'n'), + array( + self::$_ADMIN, + self::$_UNIT_TESTER, + ) + ); + } + /** + * Tests various user account edit operations. + * + * @return void + */ + function testUserAccountEdits() + { + $this->_verifyUserModification( + 'user_accounts', + 'UnitTester', + 'First_name', + 'NewFirst' + ); + $this->_verifyUserModification( + 'user_accounts', + 'UnitTester', + 'Last_name', + 'NewLast' + ); + $this->_verifyUserModification( + 'user_accounts', + 'UnitTester', + 'Active', + 'No' + ); + $this->_verifyUserModification( + 'user_accounts', + 'UnitTester', + 'Email', + 'newemail@gmail.com' + ); + $this->_verifyUserModification( + 'user_accounts', + 'UnitTester', + 'Pending_approval', + 'No' + ); + } + /** + * Tests various My Preference page edit operations. + * + * @return void + */ + function testMyPreferencesEdits() + { + $this->_verifyUserModification( + 'user_accounts/my_preferences', + 'UnitTester', + 'First_name', + 'NewFirst' + ); + $this->_verifyUserModification( + 'user_accounts/my_preferences', + 'UnitTester', + 'Last_name', + 'NewFirst' + ); + $this->_verifyUserModification( + 'user_accounts/my_preferences', + 'UnitTester', + 'Email', + 'newemail@gmail.com' + ); + } + /** + * Tests that the creation of a new user works. + * + * @return void + */ + function testAddNewUser() + { + $this->safeGet($this->url . "/user_accounts/"); + $this->safeClick(WebDriverBy::Name('button')); + $field = $this->safeFindElement(WebDriverBy::Name('UserID')); + $field->clear(); + $field->sendKeys('userid'); + + // Click somehow does not work but this should be + // equivalent + $element = $this->safeFindElement(WebDriverBy::Name('NA_Password')); + $element->sendKeys(WebDriverKeys::RETURN_KEY); + + $field = $this->safeFindElement(WebDriverBy::Name('First_name')); + $field->clear(); + $field->sendKeys('first'); + $field = $this->safeFindElement(WebDriverBy::Name('Last_name')); + $field->clear(); + $field->sendKeys('last'); + $field = $this->safeFindElement(WebDriverBy::Name('Email')); + $field->clear(); + $field->sendKeys('email@gmail.com'); + $field = $this->safeFindElement(WebDriverBy::Name('__ConfirmEmail')); + $field->clear(); + $field->sendKeys('email@gmail.com'); + $this->safeClick(WebDriverBy::Name('SendEmail')); + $this->safeClick(WebDriverBy::Name('fire_away')); + $this->_accessUser('user_accounts', 'userid'); + $field = $this->safeFindElement(WebDriverBy::Name('First_name')); + $this->assertEquals($field->getAttribute('value'), 'first'); + $field = $this->safeFindElement(WebDriverBy::Name('Last_name')); + $this->assertEquals($field->getAttribute('value'), 'last'); + $field = $this->safeFindElement(WebDriverBy::Name('Email')); + $this->assertEquals($field->getAttribute('value'), 'email@gmail.com'); + } + + /** + * Modifies a field on either the user account or my preferences page + * and checks that the modification was recorded in the database. + * + * @param string $page either 'user_accounts' or + 'user_accounts/my_preferences'. + * @param string $userId ID of the user to modify. + * @param string $fieldName name of the field (on the HTML page) that should + * be modified. + * @param string $newValue new field value. + * + * @return void + */ + function _verifyUserModification($page, $userId, $fieldName, $newValue) + { + $this->_accessUser($page, $userId); + $field = $this->safeFindElement(WebDriverBy::Name($fieldName)); + if ($field->getTagName() == 'input') { + $field->clear(); + $field->sendKeys($newValue); + } else { + $selectField = new WebDriverSelect($field); + $selectField->selectByVisibleText($newValue); + } + $this->safeClick(WebDriverBy::Name('fire_away')); + $this->_accessUser($page, $userId); + $field = $this->safeFindElement(WebDriverBy::Name($fieldName)); + if ($field->getTagName() == 'input') { + $this->assertEquals($field->getAttribute('value'), $newValue); + } else { + $selectField = new WebDriverSelect($field); + $this->assertEquals( + $selectField->getFirstSelectedOption()->getText(), + $newValue + ); + } + } + /** + * Does one of two things: either accesses the My Preferences page of the + * current user of the user account page for the user whose ID is passed + * as argument. + * + * @param string $page either 'user_accounts' or + 'user_accounts/my_preferences' + * @param string $userId ID of the user whose page should be accessed. + * + * @return void. + */ + function _accessUser($page, $userId) + { + $this->safeGet($this->url . "/$page/"); + if ($page == 'user_accounts') { + $this->safeClick(WebDriverBy::LinkText($userId)); + } + } + /** + * Performs a candidate search using the specified criteria and verifies + * the candidates obtained. + * + * @param array $criteria criteria for the search. + * @param string $expectedResults the candidates that should be returned. + * + * @return void. + */ + private function _assertSearchBy(array $criteria, $expectedResults) + { + foreach ($criteria as $elementName => $elementValue) { + $element = $this->safeFindElement( + WebDriverBy::Name($elementName) + ); + $element->clear(); + $element->sendKeys($elementValue); + } + $this->safeClick(WebDriverBy::Name("filter")); + $this->_assertUserTableContents('dynamictable', $expectedResults); + } + /** + * Compares the content of the candidate table with an expected content. + * + * @param string $className class name of the HTML table. + * @param string $expectedRows array of candidates that the table should contain. + * + * @return void + */ + private function _assertUserTableContents($className, $expectedRows) + { + $dataTable = $this->safeFindElement( + WebDriverBy::ClassName($className) + ); + if (is_null($expectedRows)) { + $this->assertContains('No users found', $dataTable->getText()); + } else { + $actualRows = $dataTable->findElements( + WebDriverBy::xpath('.//tbody//tr') + ); + $this->assertEquals( + count($actualRows), + count($expectedRows), + "Number of users returned should be " + . count($expectedRows) . ", not " . count($actualRows) + ); + for ($i=1; $i<=count($actualRows); $i++) { + $elements = $actualRows[$i-1]->findElements( + WebDriverBy::xpath('.//td') + ); + $actualColumns = array(); + foreach ($elements as $e) { + $actualColumns[] = $e->getText(); + } + $expectedColumns = $expectedRows[$i-1]; + array_unshift($expectedColumns, "$i"); + $this->assertEquals( + $actualColumns, + $expectedColumns, + "Users at row $i differ" + ); + } + } + } + /** + * Performed after every test. + * + * @return void + */ + function tearDown() + { + $this->DB->delete("users", array("UserID" => 'userid')); + parent::tearDown(); + } +} +?> diff --git a/reliability/NDB_Reliability_bmi_reliability.class.inc b/reliability/NDB_Reliability_bmi_reliability.class.inc new file mode 100644 index 0000000..90e63ee --- /dev/null +++ b/reliability/NDB_Reliability_bmi_reliability.class.inc @@ -0,0 +1,365 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/loris/ + */ + +require_once "NDB_Reliability.class.inc"; + +/** + * Creates the form elements for the Boston_Diagnostic_Aphasia_Exam instrument + * + * @category Reliability_Instrument + * @package BMI + * @author Stella Lee + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/loris/ + */ +class NDB_Reliability_bmi_reliability extends NDB_Reliability +{ + + function _getAgeInMonths() + { + throw new LorisException("Could not calculate age because don't know if subject or proband"); + } + function _getDefaults() + { + $DB =& Database::singleton(); + $result = $DB->pselect("SELECT * FROM " . $DB->escape($this->name), array()); + + //print_r($this->identifier); + if (!empty($this->identifier)) { + // set the bmi_reliability values + $row = $DB->pselectRow( + "SELECT * FROM " . $DB->escape($this->name) . " WHERE CommentID=:cid AND reliability_center_id=:rcid", + array( + 'cid' => $this->identifier, + 'rcid' => $this->reliability_center_id, + ) + ); + + // Set the defaults + $sites = array( + 1 => 'DCC', + 2 => 'MTL', + 3 => 'OTT', + 4 => 'ROM', + ); + + $initial_table = preg_replace('/_reliability/', '', $this->name); + + foreach ($row as $key=>$value) { + $defaults[$key] = $value; + } + $defaults['CommentID'] = "" . $row['CommentID'] . ""; + + } else { + echo("Identifer empty! No ID has been chosen. Please contact the DCC.
"); + } + + return $defaults; + } + + + function score() + { + //holds raw question values + $score_record = array(); + //holds calculated + $db =& Database::singleton(); + + // null scores + //$this->_nullScores(); + $initial_table = preg_replace('/_reliability/', '', $this->name); + + //Get values from both tables (reliability and original) + $query = "SELECT * FROM " . $db->escape($this->name) . " WHERE CommentID=:cid AND reliability_center_id=:rcid"; + $query1 = "SELECT * FROM " . $db->escape($initial_table) . " WHERE CommentID=:cid"; + + $reliability_record = $db->pselectRow($query, array('cid' => $this->identifier, 'rcid' => $this->reliability_center_id)); + $instrument_record = $db->pselectRow($query1, array('cid' => $this->identifier)); + + $mismatches = 0; + $denominator = 0; + $algorithm_mismatches = 0; + $algorithm_denominator = 0; + $algorithm_questions = array( + 'height_feet', + 'height_inches', + 'weight_lbs', + ); + + foreach ($instrument_record as $key=>$value) { + if (!in_array($key, $algorithm_questions)) { + continue; + } + + $rel_key = $key; + $algorithm_question = in_array($key, $algorithm_questions); + $rel_value = $reliability_record[$rel_key]; + if (!empty($value) && !empty($rel_value)) { + + $denominator++; + if ($algorithm_question) { + $algorithm_denominator++; + } + + if ($this->_checkEquivalency($value, $rel_value)) { + continue; + } + $mismatches++; + if ($algorithm_question) { + $algorithm_mismatches++; + } + } + } + // Score the reliability + $reliability['Reliability_score_all_items'] = (($denominator-$mismatches)/$denominator*100); + + // Do the update here for the reliability scores - saving the scores + $result = $db->update("{$this->name}", $reliability, array("CommentID" => $this->identifier, 'reliability_center_id' => $this->reliability_center_id)); + $result = $db->update("reliability", array("Reliability_score" => round(min($reliability['Reliability_score_all_items'], $reliability['Reliability_score_scored_items']), 3)), array("CommentID" => $this->identifier, "Instrument" => preg_replace("/_reliability$/", "", $this->name), 'reliability_center_id' => $this->reliability_center_id)); + // Update the main reliability table with the overall reliability score + $result1 = $db->update("reliability", array('reliability_score' => $reliability['Reliability_score_all_items']), array("CommentID" => $this->identifier)); + } // end function score + + function _checkEquivalency($val, $rel_val) + { + if ($val === $rel_val) { + return true; + } + return false; + } + function _nullScores() + { + $db =& Database::singleton(); + + // set the scores to NULL + foreach ($this->scores as $val) { + $scores[$val] =null; + } + + // update the scores + $success = $db->update("{$this->name}", $scores, array("CommentID" => $this->identifier, 'reliability_center_id' => $this->reliability_center_id)); + return; + } + + function bmi_reliability() + { + $this->create_form(); + } + + + function create_form() + { + $this->_addMetadataFields(); + $this->form->addElement("static", "reliability_center", "Site of Reliability Test:"); + + //Scoring header + $this->form->addElement('header', 'instrument_title', "Scoring:"); + // $this->form->addElement("static", "Reliability_score_scored_items", "Reliability Score All items(%):"); + $this->form->addElement("static", "Reliability_score_all_items", "Reliability Scored items(%):"); + $this->form->addElement("static", "CommentID", "CommentID:"); + + //display test name + $this->form->addElement('header', null, 'BMI'); + $this->addNumericElement('height_feet', "Height (feet)"); + $this->addNumericElement('height_inches', "Height (inches)"); + $this->addNumericElement('weight_lbs', "Weight (lbs)"); + } // End function + + function _cleanTo3Digits($string) + { + return ereg_replace("[^0-9]", "", substr($string, 0, 3)); + } + + function _process($values) + { + $DB =& Database::singleton(); + + $user =& User::singleton(); + + $row = $DB->pselectRow( + "SELECT * FROM " . $DB->escape($this->name) . " WHERE CommentID=:cid AND reliability_center_id=:rcid", + array( + 'cid' => $this->identifier, + 'rcid' => $this->reliability_center_id, + ) + ); + + $invalid = $values['invalid']; + $DB->update("reliability", array('invalid' => $invalid), array("CommentID" => $this->identifier, "Instrument" => preg_replace("/_reliability$/", "", $this->name), "reliability_center_id" => $this->reliability_center_id)); + unset($values['invalid']); + + if ($invalid == "yes") { + $this->form->freeze(); + $this->tpl_data['success'] = true; + return; + } + foreach ($values as $key=>$value) { + if ($key == 'Date_taken' || $key == 'DoB_proband') { + $values[$key] = $this->_getDatabaseDate($values[$key]); + } + } + if (empty($row)) { + + $values['CommentID'] = $this->identifier; + $values['reliability_center_id'] = $this->reliability_center_id; + // insert the event + $success = $DB->insert($this->name, $values); + } else { + // update the event + $success = $DB->update( + $this->name, + $values, + array( + 'CommentID' => $this->identifier, + 'reliability_center_id' => $this->reliability_center_id, + ) + ); + } + + $scoreResult = $this->score(); + + $this->form->freeze(); + $this->tpl_data['success'] = true; + } + + function _validate($values) + { + $DB =& Database::singleton(); + $query = "SELECT count(*) AS counter FROM candidate WHERE CandID=:cid and PSCID=:pid"; + $recordsFound = $DB->pselectOne($query, array('cid' => $values['CandID'], 'pid' => $values['PSCID'])); + $errors = array(); + + if ($recordsFound < 1) { + $errors['CandID'] = "Specified DCCID and PSCID do not exist or do not match."; + } + + return $errors; + } + + function display() + { + if (!$this->form->isFrozen()) { + // hidden values + $this->form->addElement( + 'hidden', + 'test_name', + $this->name + ); + $this->form->addElement( + 'hidden', + 'subtest', + $this->page + ); + $this->form->addElement( + 'hidden', + 'identifier', + $this->identifier + ); + $this->form->addElement( + 'hidden', + 'reliability_center_id', + $this->reliability_center_id + ); + $this->form->addElement( + 'submit', + 'fire_away', + 'Save Data', + 'class=button' + ); + } + + // get the defaults + $localDefaults = $this->_getDefaults(); + if (!is_array($localDefaults)) { + $localDefaults = array(); + } + // set the quickform object defaults + $this->form->setDefaults(array_merge($this->defaults, $localDefaults)); + + // trim all values + $this->form->applyFilter('__ALL__', 'trim'); + + // display the HTML_Quickform object + $smarty = new Smarty_neurodb; + + // display the HTML_Quickform object + return $this->form->toHTML(); + + $renderer =& new HTML_QuickForm_Renderer_Default(); + $renderer->setFormTemplate( + "{content}
" + ); + $renderer->setElementTemplate( + " + * + {label} + {error}
+ {element} + " + ); + $renderer->setElementTemplate( + "{label} + \t{element}", + "static" + ); + $renderer->setHeaderTemplate( + "
{header}" + ); + $this->form->accept($renderer); + return $renderer->toHtml(); + + } // end function display + + + /** + * Wrapper to create a field that only accepts a number, with an + * accompanying status field. + * + * @param string $field The database field in which the response + * will be stored + * @param string $label The question text to display + * @param unknown $options Does not appear to be used? + * + * @return none + */ + function addNumericElement($field, $label, $options = null) + { + $group[] = $this->createText($field, $label); + $this->WrapperNumericElements[$field] = $group[0]; + $group[] = $this->createSelect( + $field . "_status", + null, + array( + null => "", + 'not_answered' => "Not Answered", + ), + array('class' => 'form-control input-sm not-answered') + ); + $this->addGroup($group, $field . "_group", $label, null, false); + unset($group); + $this->addGroupRule( + $field . "_group", + array(array(array("Value must be numeric.", 'numeric'))) + ); + $this->XINRegisterRule( + $field, + array($field . '_status{@}=={@}'), + 'This field is required', + $field . '_group' + ); + } + +} +?> diff --git a/tables_sql/bmi.sql b/tables_sql/bmi.sql new file mode 100644 index 0000000..9ece7fb --- /dev/null +++ b/tables_sql/bmi.sql @@ -0,0 +1,25 @@ +CREATE TABLE `bmi` ( +`CommentID` varchar(255) NOT NULL default '', + `UserID` varchar(255) default NULL, + `Examiner` varchar(255) default NULL, + `Testdate` timestamp NOT NULL, + `Data_entry_completion_status` enum('Incomplete','Complete') NOT NULL default 'Incomplete', +`Date_taken` date default NULL, +`Candidate_Age` varchar(255) default NULL, +`Window_Difference` varchar(255) default NULL, +`unit_classification` enum('metric','standard') default NULL, +`height_feet` numeric default NULL, +`height_feet_status` enum('not_answered') default NULL, +`height_inches` numeric default NULL, +`height_inches_status` enum('not_answered') default NULL, +`weight_lbs` numeric default NULL, +`weight_lbs_status` enum('not_answered') default NULL, +`height_cms` numeric default NULL, +`height_cms_status` enum('not_answered') default NULL, +`weight_kgs` numeric default NULL, +`weight_kgs_status` enum('not_answered') default NULL, +`bmi` varchar(255) default NULL, +`bmi_category` varchar(255) default NULL, +PRIMARY KEY (`CommentID`) +); +REPLACE INTO test_names (Test_name, Full_name, Sub_group) VALUES ('bmi', 'BMI Calculator', 1); diff --git a/tables_sql/medical_history.sql b/tables_sql/medical_history.sql new file mode 100644 index 0000000..35a58eb --- /dev/null +++ b/tables_sql/medical_history.sql @@ -0,0 +1,34 @@ +CREATE TABLE `medical_history` ( +`CommentID` varchar(255) NOT NULL default '', + + `UserID` varchar(255) default NULL, + + `Examiner` varchar(255) default NULL, + + `Testdate` timestamp NOT NULL, + + `Data_entry_completion_status` enum('Incomplete','Complete') NOT NULL default 'Incomplete', +`Date_taken` date default NULL, +`Candidate_Age` varchar(255) default NULL, +`Window_Difference` varchar(255) default NULL, +`arthritis` enum('yes','no') default NULL, +`arthritis_age` varchar(255) default NULL, +`pulmonary_issues` enum('yes','no') default NULL, +`pulmonary_issues_specific` varchar(255) default NULL, +`hypertension` enum('yes','no') default NULL, +`hypertension_while_pregnant` enum('yes','no') default NULL, +`hypertension_while_pregnant_age` varchar(255) default NULL, +`concussion_or_head_trauma` enum('yes','no') default NULL, +`concussion_1_description` varchar(255) default NULL, +`concussion_1_hospitalized` enum('yes','no') default NULL, +`concussion_1_age` varchar(255) default NULL, +`concussion_2_description` varchar(255) default NULL, +`concussion_2_hospitalized` enum('yes','no') default NULL, +`concussion_2_age` varchar(255) default NULL, +`concussion_3_description` varchar(255) default NULL, +`concussion_3_hospitalized` enum('yes','no') default NULL, +`concussion_3_age` varchar(255) default NULL, +`current_concussion_symptoms` varchar(255) default NULL, +PRIMARY KEY (`CommentID`) + + ); diff --git a/tables_sql/mri_parameter_form.sql b/tables_sql/mri_parameter_form.sql new file mode 100644 index 0000000..2b3f1f7 --- /dev/null +++ b/tables_sql/mri_parameter_form.sql @@ -0,0 +1,72 @@ +CREATE TABLE `mri_parameter_form` ( +`CommentID` varchar(255) NOT NULL default '', + + `UserID` varchar(255) default NULL, + + `Examiner` varchar(255) default NULL, + + `Testdate` timestamp NOT NULL, + + `Data_entry_completion_status` enum('Incomplete','Complete') NOT NULL default 'Incomplete', +`Date_taken` date default NULL, +`Candidate_Age` varchar(255) default NULL, +`Window_Difference` varchar(255) default NULL, +`Scanner_Type` enum('hospital','research','other') default NULL, +`Scanner_Type_other` varchar(255) default NULL, +`session_attempts` varchar(255) default NULL, +`session_attempts_status` enum('not_answered') default NULL, +`wait_time` time default NULL, +`wait_time_status` enum('dnk','refusal','not_answered') default NULL, +`total_duration` time default NULL, +`total_duration_status` enum('dnk','refusal','not_answered') default NULL, +`sedation` enum('Yes','No') default NULL, +`medication` enum('Yes','No','not_answered') default NULL, +`medication_specify` varchar(255) default NULL, +`t1_Scan_done` enum('No','Complete','Partial','not_answered') default NULL, +`t1_number_attempts` varchar(255) default NULL, +`t1_Scan_done_date` date default NULL, +`t1_Scan_done_date_status` enum('not_answered') default NULL, +`t1_Comments` text default NULL, +`t1_Comments_status` enum('not_answered') default NULL, +`t2_Scan_done` enum('No','Complete','Partial','not_answered') default NULL, +`t2_number_attempts` varchar(255) default NULL, +`t2_Scan_done_date` date default NULL, +`t2_Scan_done_date_status` enum('not_answered') default NULL, +`t2_Comments` text default NULL, +`t2_Comments_status` enum('not_answered') default NULL, +`dti_Scan_done` enum('No','Complete','Partial','not_answered') default NULL, +`dti_number_attempts` varchar(255) default NULL, +`dti_Scan_done_date` date default NULL, +`dti_Scan_done_date_status` enum('not_answered') default NULL, +`dti_Comments` text default NULL, +`dti_Comments_status` enum('not_answered') default NULL, +`DTI65Dir_Scan_done` enum('No','Complete','Partial','not_answered') default NULL, +`DTI65Dir_number_attempts` varchar(255) default NULL, +`DTI65Dir_Scan_done_date` date default NULL, +`DTI65Dir_Scan_done_date_status` enum('not_answered') default NULL, +`DTI65Dir_Comments` text default NULL, +`DTI65Dir_Comments_status` enum('not_answered') default NULL, +`fMRI_Scan_done` enum('No','Complete','Partial','not_answered') default NULL, +`fMRI_Number_Complete_Runs` enum('1','2','3') default NULL, +`fMRI_number_attempts` varchar(255) default NULL, +`fMRI_Scan_done_date` date default NULL, +`fMRI_Scan_done_date_status` enum('not_answered') default NULL, +`fMRI_child_awake` enum('No','Yes','Possibly') default NULL, +`fMRI_Comments` text default NULL, +`fMRI_Comments_status` enum('not_answered') default NULL, +`Spectroscopy_Scan_done` enum('No','Complete','Partial','not_answered') default NULL, +`Spectroscopy_number_attempts` varchar(255) default NULL, +`Spectroscopy_Scan_done_date` date default NULL, +`Spectroscopy_Scan_done_date_status` enum('not_answered') default NULL, +`Spectroscopy_long_echo_acquired` enum('acquired','not_acquired') default NULL, +`Spectroscopy_short_echo_acquired` enum('acquired','not_acquired') default NULL, +`Spectroscopy_water_scan_acquired` enum('acquired','not_acquired') default NULL, +`Spectroscopy_Comments` text default NULL, +`Spectroscopy_Comments_status` enum('not_answered') default NULL, +`gre_accquired` enum('yes','no','not_answered') default NULL, +`gre_fieldmap` varchar(255) default NULL, +`coordinate_system` varchar(255) default NULL, +`normalization` varchar(255) default NULL, +PRIMARY KEY (`CommentID`) + + ); diff --git a/tables_sql/radiology_review.sql b/tables_sql/radiology_review.sql new file mode 100644 index 0000000..33eb7e0 --- /dev/null +++ b/tables_sql/radiology_review.sql @@ -0,0 +1,25 @@ +CREATE TABLE `radiology_review` ( +`CommentID` varchar(255) NOT NULL default '', + + `UserID` varchar(255) default NULL, + + `Examiner` varchar(255) default NULL, + + `Testdate` timestamp NOT NULL, + + `Data_entry_completion_status` enum('Incomplete','Complete') NOT NULL default 'Incomplete', +`Date_taken` date default NULL, +`Candidate_Age` varchar(255) default NULL, +`Window_Difference` varchar(255) default NULL, +`Scan_done` enum('no','yes','not_answered') default NULL, +`MRI_date` date default NULL, +`MRI_date_status` enum('not_answered') default NULL, +`Review_date` date default NULL, +`Review_date_status` enum('not_answered') default NULL, +`Review_results` enum('normal','abnormal','atypical','not_answered') default NULL, +`abnormal_atypical_exclusionary` enum('exclusionary','non_exclusionary','not_answered') default NULL, +`Incidental_findings` text default NULL, +`Incidental_findings_status` enum('not_answered') default NULL, +PRIMARY KEY (`CommentID`) + + ); diff --git a/templates/login.tpl b/templates/login.tpl new file mode 100644 index 0000000..885f244 --- /dev/null +++ b/templates/login.tpl @@ -0,0 +1,45 @@ +
+
+
+ +
+
+
+
+

{$study_title}

+
+
+
+

{$study_description}

+
+
+
+
+
+
diff --git a/tools/drop_all_tables.php b/tools/drop_all_tables.php new file mode 100644 index 0000000..f6fa42f --- /dev/null +++ b/tools/drop_all_tables.php @@ -0,0 +1,53 @@ + + * @license Loris license + * @link https://www.github.com/aces/Loris-Trunk/ + */ +set_include_path(get_include_path().":".__DIR__."/../../php/libraries:"); +require_once __DIR__ . "/../../vendor/autoload.php"; +//require_once "NDB_Config.class.inc"; + +$client = new NDB_Client(); +$client->makeCommandLine(); +$client->initialize(__DIR__."/../config.xml"); +$config = NDB_Config::singleton(); + +$db =& Database::singleton(); +$database = $config->getSetting('database'); + +$base = $config->getSetting('base'); +$db->_trackChanges = false; + +$filename = __DIR__ . "/../tables_sql/drop_all_tables_statements.sql"; +$output = ""; +$output .="SET FOREIGN_KEY_CHECKS=0; \n"; + +echo "\n#################################################################\n\n". + "This Script will generate a DROP TABLE statement for every table ". + "currently in the database. \nThe output file is ". + "tables_sql/drop_all_tables_statements.sql and includes foreign key ". + "checks disabling and re-enabling.\n". + "\n#################################################################\n\n"; + +$table_names = $db->pselect("show tables", array()); +$database_name= $database['database']; +foreach ($table_names as $key=>$table) +{ + $output .= "DROP TABLE IF EXISTS `".$table["Tables_in_$database_name"]."`;\n"; +} +$output .="SET FOREIGN_KEY_CHECKS=1; \n"; +$fp=fopen($filename, "w"); +fwrite($fp, $output); +fclose($fp); + +?> +