* @version 1.0 * @since 20 Aug 2014 17:20:09 */ require_once 'models/Model.inc.php'; class Participant extends Model { // The database fields used by this model. public static $fields = array( 'account_id' => '', 'participant_id' => '', 'phone' => '', 'status' => '', 'last_active_datetime' => '', 'added_datetime' => '', 'added_by_user_id' => '', 'modified_datetime' => '', 'modified_by_user_id' => '', 'include_in_current_survey' => '', ); /* * * * @access public * @param * @return * @author Quinn Comendant * @version 1.0 * @since 16 Nov 2014 17:45:40 */ static public function insert($frm, $source='web admin') { global $auth, $cache; $db =& DB::getInstance(); $app =& App::getInstance(); // Remove any stale cached list data. $cache->delete('participant list'); // Create the record under the user's account, unless an account_id was provided. $account_id = isset($frm['account_id']) && is_numeric($frm['account_id']) ? $frm['account_id'] : $auth->get('account_id'); // Insert record data. $app->logMsg(sprintf('%s with data %s', __METHOD__, getDump($frm)), LOG_DEBUG, __FILE__, __LINE__); $db->query(" INSERT INTO participant_tbl ( account_id, phone, status, source, added_datetime, added_by_user_id, modified_datetime, modified_by_user_id ) VALUES ( '" . $db->escapeString($account_id) . "', '" . $db->escapeString($frm['phone']) . "', 'unknown', '" . $db->escapeString($source) . "', NOW(), '" . $db->escapeString($auth->get('user_id')) . "', NOW(), '" . $db->escapeString($auth->get('user_id')) . "' ) "); $last_insert_id = mysql_insert_id($db->getDBH()); // Create version. $version = Version::getInstance($auth); $version->create('participant_tbl', 'participant_id', $last_insert_id, $frm['phone']); return $last_insert_id; } /* * * * @access public * @param * @return * @author Quinn Comendant * @version 1.0 * @since 09 Nov 2013 23:45:27 */ static public function bulkInsert($phone_list) { $app =& App::getInstance(); // Parse submitted numbers into array. $nums = preg_split('/[\n,;]/', $phone_list); $nums = preg_grep('/\d/', $nums); $nums = array_map('trim', $nums); $valid_nums = array(); $invalid_nums = array(); $preexisting_nums = array(); foreach ($nums as $n) { $n = preg_replace('/[^+\d]/', '', $n); if (parent::get(array('phone' => $n))) { // Participant already exists. $preexisting_nums[] = $n; } else if (!Validator::checkRegex($n, '/^\+\d+$/')) { // Invalid number. $invalid_nums[] = $n; } else { // OK! Valid. $valid_nums[] = $n; self::insert(array('phone' => $n), 'imported'); } } if (sizeof($valid_nums) != sizeof($nums)) { $app->raiseMsg(sprintf(_("%s out of %s phone numbers were imported."), sizeof($valid_nums), sizeof($nums)), MSG_NOTICE, __FILE__, __LINE__); if (sizeof($preexisting_nums) > 0) { $app->raiseMsg(sprintf(_("%s duplicate %s skipped."), sizeof($preexisting_nums), sizeof($preexisting_nums) == 1 ? _("number was") : _("numbers were")), MSG_NOTICE, __FILE__, __LINE__); } if (sizeof($invalid_nums) > 0) { $app->raiseMsg(sprintf(_("The following numbers were skipped these because they weren't in full international format including ‘+’ and country-code:
%s
"), join('
', $invalid_nums)), MSG_NOTICE, __FILE__, __LINE__); } } else { $app->raiseMsg(sprintf(_("%s phone numbers were imported. None were skipped."), sizeof($valid_nums)), MSG_SUCCESS, __FILE__, __LINE__); } } /* * Subscribes a phone number to our participants database. * * @access public * @param string $phone Phone number to be subscribed. * @return int The participant_id of the newly added phone number. * @author Quinn Comendant * @version 1.0 * @since 09 Nov 2013 17:18:03 */ static public function optIn($phone, $virtual_number) { $db =& DB::getInstance(); $app =& App::getInstance(); if (!preg_match('/^\+\d+$/', $phone)) { $app->logMsg(sprintf('Join failed; invalid number: %s', $phone), LOG_WARNING, __FILE__, __LINE__); return false; } // Get account_id of the virtual number that received this SMS. $survey = Survey::get(array('virtual_number' => $virtual_number)); if (!$survey) { $app->logMsg(sprintf('No account matching virtual_number: %s', $virtual_number), LOG_ERR, __FILE__, __LINE__); return false; } // Insert record data. $app->logMsg(sprintf('%s with phone: %s virtual_number: %s', __METHOD__, $phone, $virtual_number), LOG_DEBUG, __FILE__, __LINE__); $db->query(" INSERT INTO participant_tbl ( account_id, phone, status, source, added_datetime, added_by_user_id, modified_datetime, modified_by_user_id ) VALUES ( '" . $db->escapeString($survey['account_id']) . "', '" . $db->escapeString($phone) . "', 'unknown', 'opt-in', NOW(), NULL ) "); $last_insert_id = mysql_insert_id($db->getDBH()); $app->logMsg(sprintf('New participant_id %s joined by sms', $last_insert_id), LOG_INFO, __FILE__, __LINE__); return $last_insert_id; } /* * Sets the status of a participant to 'opted out' so they will not be included in surveys. * * @access public * @param string $phone Phone number to be opted-out. * @return * @author Quinn Comendant * @version 1.0 * @since 09 Nov 2013 17:18:03 */ static public function optOut($phone) { $db =& DB::getInstance(); $app =& App::getInstance(); if (!preg_match('/^\+\d+$/', $phone)) { $app->logMsg(sprintf('Opt out failed; invalid number: %s', $phone), LOG_NOTICE, __FILE__, __LINE__); return false; } // Get account_id of the virtual number that received this SMS. $survey = Survey::get(array('virtual_number' => $virtual_number)); if (!$survey) { $app->logMsg(sprintf('No account matching virtual_number: %s', $virtual_number), LOG_ERR, __FILE__, __LINE__); return false; } // Insert record data. $app->logMsg(sprintf('%s with phone: %s', __METHOD__, $phone), LOG_DEBUG, __FILE__, __LINE__); $db->query(" UPDATE participant_tbl SET status = 'opted out', last_active_datetime = NOW() WHERE phone = '" . $db->escapeString($phone) . "' AND survey_id = '" . $db->escapeString($survey['survey_id']) . "' "); $p = parent::get(array('phone' => $phone, 'survey_id' => $survey['survey_id'])); $app->logMsg(sprintf('Participant_id %s opted out by sms', $p['participant_id']), LOG_INFO, __FILE__, __LINE__); return $p['participant_id']; } /* * * * @access public * @param * @return * @author Quinn Comendant * @version 1.0 * @since 16 Nov 2014 17:45:40 */ static public function update($frm) { global $auth, $lock, $cache; $db =& DB::getInstance(); $app =& App::getInstance(); $lock->select('participant_tbl', 'participant_id', $frm['participant_id']); if ($lock->isLocked() && !$lock->isMine()) { $lock->dieErrorPage(); } // Remove any stale cached list data. $cache->delete('participant list'); // Create the record under the user's account, unless an account_id was provided. $account_id = isset($frm['account_id']) && is_numeric($frm['account_id']) ? $frm['account_id'] : $auth->get('account_id'); // Update record data. $app->logMsg(sprintf('%s with data %s', __METHOD__, getDump($frm)), LOG_DEBUG, __FILE__, __LINE__); $db->query(" UPDATE participant_tbl SET account_id = '" . $db->escapeString($account_id) . "', phone = '" . $db->escapeString($frm['phone']) . "', modified_datetime = NOW(), modified_by_user_id = '" . $db->escapeString($auth->get('user_id')) . "' WHERE participant_id = '" . $db->escapeString($frm['participant_id']) . "' "); // Create version. $version = Version::getInstance($auth); $version->create('participant_tbl', 'participant_id', $frm['participant_id'], $frm['phone']); // Unlock record. $lock->remove(); } /* * * * @access public * @param * @return * @author Quinn Comendant * @version 1.0 * @since 16 Nov 2014 17:45:40 */ static public function delete($id) { global $lock, $cache, $locally_carried_queries; $db =& DB::getInstance(); $app =& App::getInstance(); $lock->select('participant_tbl', 'participant_id', $id); if ($lock->isLocked() && !$lock->isMine()) { $lock->dieErrorPage(); } // Remove any stale cached list data. $cache->delete('participant list'); // Delete the record. $db->query("DELETE FROM participant_tbl WHERE participant_id = '" . $db->escapeString($id) . "'"); // Unlock record. $lock->remove(); } /* * * * @access public * @param * @return * @author Quinn Comendant * @version 1.0 * @since 16 Nov 2014 17:45:40 */ static public function getPaginatedList($where_clause='') { global $auth, $page, $so, $cache; $db =& DB::getInstance(); $app =& App::getInstance(); // Build search query if available. if (getFormData('q', false)) { $qry_words = preg_split('/[^\w]/', getFormData('q')); for ($i=0; $iescapeString($qry_words[$i]) . "%' ) "; } } if (getFormData('filter___///__', false)) { // Limit by filter. $where_clause .= (empty($where_clause) ? 'WHERE' : 'AND') . " __///__ = '" . $db->escapeString(getFormData('filter___///__')) . "'"; } // Count the total number of records so we can do something about the page numbers. $qid = $db->query(" SELECT COUNT(*) FROM participant_tbl $where_clause "); list($num_results) = mysql_fetch_row($qid); // Set page numbers now we know (needed for next step). $page->setTotalItems($num_results); $page->calculate(); // Final SQL, with sort and page limiters. $sql = " SELECT participant_tbl.*, a1.username AS added_by_username, a2.username AS modified_by_username FROM participant_tbl LEFT JOIN user_tbl a1 ON (participant_tbl.added_by_user_id = a1.user_id) LEFT JOIN user_tbl a2 ON (participant_tbl.modified_by_user_id = a2.user_id) $where_clause " . $so->getSortOrderSQL() . " " . $page->getLimitSQL() . " "; // Use a cache hash to determine if the result-set has changed. // A unique key for this query, with the total_items in case db records // were added since the last cache. This identifies a unique set of // cached data, but we must refer to the list that is cached by a more // generic name. so that we can flush the cache (if records updated) // without knowing the hash. $cache_hash = md5($sql . '|' . $page->total_items); $tmp_prefs = new Prefs('session', array('storagetype' => 'session')); if ($tmp_prefs->get('cache_hash') != $cache_hash) { $cache->delete('participant list'); $tmp_prefs->set('cache_hash', $cache_hash); } // First try to return from the cache. if ($cache->exists('participant list')) { return $cache->get('participant list'); } // The list was not cached, so issue the real query. $qid = $db->query($sql); $list = array(); while ($row = mysql_fetch_assoc($qid)) { $list[] = $row; } // Save this list into the cache. if (isset($list) && !empty($list)) { $cache->set('participant list', $list); } return $list; } /* * * * @access public * @param * @return * @author Quinn Comendant * @version 1.0 * @since 16 Nov 2014 17:45:40 */ static public function endParticipantSurvey() { } /* * Returns values of the participant_survey_tbl for the requested participant_id and survey_id. * * @access public * @param int $participant_id * @param int $survey_id * @return mixed Return value from mysql_fetch_assoc() for the query. * @author Quinn Comendant * @version 1.0 * @since 09 Nov 2013 15:48:56 */ static public function getSurveyStatus($participant_id, $survey_id) { $db =& DB::getInstance(); $qid = $db->query(" SELECT * FROM participant_survey_tbl WHERE participant_id = '" . $db->escapeString($participant_id) . "' AND survey_id = '" . $db->escapeString($survey_id) . "' "); return mysql_fetch_assoc($qid); } /* * Returns the current question for the specified participant in the underway survey. This function may fail is two surveys are run at once. * * @access public * @param int $participant_id The participant_id of the participant * @return mixed The value returned from Question::get(). * @author Quinn Comendant * @version 1.0 * @since 08 Nov 2013 23:05:57 */ static public function getCurrentQuestion($participant_id) { $db =& DB::getInstance(); $qid = $db->query(" SELECT participant_survey_tbl.current_question_id FROM participant_tbl LEFT JOIN participant_survey_tbl ON (participant_tbl.participant_id = participant_survey_tbl.participant_id) LEFT JOIN survey_tbl ON (participant_survey_tbl.survey_id = survey_tbl.survey_id) WHERE participant_tbl.participant_id = '" . $db->escapeString($participant_id) . "' AND survey_tbl.status = 'underway' "); if (!list($current_question_id) = mysql_fetch_row($qid)) { $app->logMsg(sprintf('Failed to get current question for participant_id %s', $participant_id), LOG_NOTICE, __FILE__, __LINE__); return false; } return Question::get(array('question_id' => $current_question_id)); } /* * A simple test to see if a participant was sent a certain question. * * @access public * @param int $participant_id * @param int $question_id * @return bool True of the user has already received the question. * @author Quinn Comendant * @version 1.0 * @since 02 Nov 2013 22:59:17 */ static public function wasSentQuestion($participant_id, $question_id) { $db =& DB::getInstance(); $qid = $db->query(" SELECT 1 FROM participant_question_tbl WHERE participant_id = '" . $db->escapeString($participant_id) . "' AND question_id = '" . $db->escapeString($question_id) . "' "); return mysql_num_rows($qid) == 1; } }