/[drupal]/contributions/modules/cre/cre.module
ViewVC logotype

Diff of /contributions/modules/cre/cre.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.9.2.2.2.13, Mon Jul 23 13:53:10 2007 UTC revision 1.9.2.2.2.14, Mon Nov 3 13:17:48 2008 UTC
# Line 1  Line 1 
1  <?php  <?php
2  // $Id: cre.module,v 1.9.2.2.2.12 2007/07/23 13:51:27 hickory Exp $  // $Id: $
3  /*  /*
4  A generalized recommendation engine. Depends on votingapi.module. see README.txt  A generalized recommendation engine. Depends on votingapi.module. see README.txt
5  */  */
# Line 21  function cre_perm() { Line 21  function cre_perm() {
21  */  */
22  function cre_menu($may_cache) {  function cre_menu($may_cache) {
23    $items = array();    $items = array();
24    if ($may_cache) {    if ($may_cache) {
25      $items[] = array('path' => 'admin/settings/cre',      $items[] = array('path' => 'admin/settings/cre',
       'title' => t('CRE'),  
       'description' => t('Various settings to control how and what content to recommend'),  
       'callback' => 'cre_overview',  
       'type' => MENU_NORMAL_ITEM  
       );  
   
     $items[] = array('path' => 'admin/settings/cre/settings',  
26        'title' => t('Content Recommendation Engine'),        'title' => t('Content Recommendation Engine'),
27        'description' => t('Site-wide settings for the Content Recommendation Engine'),        'description' => t('Site-wide settings for the Content Recommendation Engine'),
28        'callback' => 'drupal_get_form',        'callback' => 'drupal_get_form',
# Line 41  function cre_menu($may_cache) { Line 34  function cre_menu($may_cache) {
34    return $items;    return $items;
35  }  }
36    
 /**  
 * Menu callback; displays the cre admin overview.  
 */  
 function cre_overview() { // FIXME: necessary?  
   $menu = menu_get_item(NULL, 'admin/settings/cre');  
   $content = system_admin_menu_block($menu);  
   $output = theme('admin_block_content', $content);  
   return $output;  
 }  
   
37  function cre_settings() {  function cre_settings() {
38    $form['cre_max_install_votes'] = array(    $form['cre_max_install_votes'] = array(
39      '#type' => 'textfield',      '#type' => 'textfield',
# Line 59  function cre_settings() { Line 42  function cre_settings() {
42      '#default_value' => variable_get('cre_max_install_votes', 0),      '#default_value' => variable_get('cre_max_install_votes', 0),
43      );      );
44    
45      $start = variable_get('cre_starting_position', 0);
46      $max = db_result(db_query("SELECT MAX(vote_id) FROM votingapi_vote"));
47      $form['cre_index_status'] = array(
48        '#type' => 'markup',
49        '#value' => '<strong><em>' . $start / $max . '</em></strong> of the site indexed',
50      );
51    
52    $form['cre_query_type'] = array(    $form['cre_query_type'] = array(
53      '#type' => 'radios',      '#type' => 'radios',
54      '#title' => t('Select a query type'),      '#title' => t('Select a query type'),
# Line 91  function cre_settings() { Line 81  function cre_settings() {
81  function cre_modify_avg_difference($uid, $nid, $content_type) {  function cre_modify_avg_difference($uid, $nid, $content_type) {
82    if ($result = db_query("SELECT DISTINCT r.content_id, r2.value - r.value as rating_difference, r.content_type as content_type1, r2.content_type as content_type2 FROM {votingapi_vote} r, {votingapi_vote} r2 WHERE r.uid = %d AND r2.content_id = %d AND r2.uid = r.uid AND r.content_type = '%s'", $uid, $nid, $content_type)) {    if ($result = db_query("SELECT DISTINCT r.content_id, r2.value - r.value as rating_difference, r.content_type as content_type1, r2.content_type as content_type2 FROM {votingapi_vote} r, {votingapi_vote} r2 WHERE r.uid = %d AND r2.content_id = %d AND r2.uid = r.uid AND r.content_type = '%s'", $uid, $nid, $content_type)) {
83      while ($row = db_fetch_object($result)){      while ($row = db_fetch_object($result)){
84    
85        // check to see if the pair of content ($itemID and $other_node) are already in the cre_similarity_matrix table        db_query("UPDATE {cre_similarity_matrix} SET count = count + 1, sum = sum + %d WHERE content_id1 = %d AND content_id2 = %d AND content_type1 = '%s'", $row->rating_difference, $nid, $row->content_id, $content_type);
86        if (db_result(db_query("SELECT COUNT(*) FROM {cre_similarity_matrix} WHERE content_id1 = %d AND content_id2 = %d AND content_type1 = '%s'", $nid, $row->content_id, $content_type))){        // update the second row only if the two nodes are different
87          //update the two rows        if ($nid != $row->content_id) {
88          db_query("UPDATE {cre_similarity_matrix} SET count = count + 1, sum = sum + %d WHERE content_id1 = %d AND content_id2 = %d AND content_type1 = '%s'", $row->rating_difference, $nid, $row->content_id, $content_type);          db_query("UPDATE {cre_similarity_matrix} SET count=count+1, sum=sum-%d WHERE content_id1 = %d AND content_id2 = %d AND content_type1 = '%s'", $row->rating_difference, $row->content_id, $nid, $content_type);
   
         // update the second row only if the two nodes are different  
         if ($nid != $row->content_id) {  
           db_query("UPDATE {cre_similarity_matrix} SET count=count+1, sum=sum-%d WHERE content_id1 = %d AND content_id2 = %d AND content_type1 = '%s'", $row->rating_difference, $row->content_id, $nid, $content_type);  
         }  
89        }        }
90        else {        if (!db_affected_rows()) {
91          // if this is  a new 'pairing' create two rows          // if this is  a new 'pairing' create two rows
92          db_query("INSERT INTO {cre_similarity_matrix} (content_id1, content_id2, content_type1, content_type2, count, sum) VALUES (%d, %d, '%s', '%s', 1, %d)", $nid, $row->content_id, $row->content_type1, $row->content_type2, $row->rating_difference);          db_query("INSERT INTO {cre_similarity_matrix} (content_id1, content_id2, content_type1, content_type2, count, sum) VALUES (%d, %d, '%s', '%s', 1, %d)", $nid, $row->content_id, $row->content_type1, $row->content_type2, $row->rating_difference);
93    
# Line 116  function cre_modify_avg_difference($uid, Line 101  function cre_modify_avg_difference($uid,
101    }    }
102  }  }
103    
 /*  
 * Returns an array of the top n content  
 *  
 * @param $uid  
 * user id for the personalized recommendation  
 *  
 *@param $calling_module  
 *specifies the name of the calling module. This string is then used to invoke hook_cre_query() in that module  
 *and only that module.  
 *  
 * @param $n default 10  
 * specifies the number of contents to return  
 *  
 * @param $target_type  
 * the type of content that the calling function would like  
 *  
 * @param $tag default 'vote'  
 * same as in votingapi. See votingapi  
 *  
 * @param $reference_type default NULL  
 * When set only those votes whos content_type is equal to this  
 * will be considered for the recommendations.  
 *  
 * @return  
 * returns an array of objs with content_id and score  
 */  
 function cre_top($uid, $callback, $n = 10, $target_type = 'node', $tag = 'vote', $reference_type = NULL, $callback_args = array()){  
   
   // create query obj  
   $query = new _cre_query($uid, $target_type, $tag, $reference_type);  
   
   // invoke callback function  
   $callback($query, $uid, $callback_args);  
   //dprint_r($query);  
   
   // execute query obj  
   if (!($query_string = $query->execute()) || !($result = db_query($query_string))) return NULL;  
   //dprint_r($query_string);  
   
   // check to see if recommending based on votingapi point system - use percent system for accurate or fast system, otherwise points  
   $value_type = (variable_get('cre_query_type', 'accurate') == 'accurate' || 'fast') ? 'percent' : 'points';  
   
   $count = 0;  
   $items = array();  
   while ($item = db_fetch_object($result)) {  
     // use this content if it hasn't been rated by the uid  
     if (!$user_check_result = votingapi_get_vote($target_type, $item->content_id, $value_type, $tag, $uid)) {  
       $items[] = $item;  
       $count++;  
     }  
     if ($count == $n) break;  
   }  
   
   // do something special when user has rated all nodes: return top rated nodes?  
   return empty($items) ? '' : $items;  
 }  
   
 /*  
 * Returns an array of content objs  
 *  
 * @param $n  
 * The number of objs to return  
 *  
 * @param $nid  
 * content_id that the return objects will be similar too  
 *  
 * @param $target_type  
 * Type of content to return from this function  
 *  
 * @param $tag  
 * votingapi tag. See Votingapi.module  
 *  
 * @param $reference_type  
 * specifies the type of content that will be only be used to determine the revelenance of  
 * a certain piece of content  
 *  
 * @return  
 * $return_value[] is an array of objs that have two fields, content_id and average.  
 *  
 */  
104    
 function cre_similar($n, $nid, $target_type = 'node', $tag = 'vote', $reference_type = NULL) {  
   // TODO: allow for reference_type to be an array  
   $node = node_load($nid);  
   
   if ($reference_type) {  
     $sql_result = db_query("SELECT d.content_id2 as 'content_id', (d.sum / d.count) AS 'average', n.title  
       FROM {cre_similarity_matrix} d JOIN {node} n ON d.content_id2 = n.nid  
       WHERE d.content_id1 = %d AND d.content_id2 <> %d AND content_type1 = '%s' AND content_type2 = '%s'  
       ORDER BY (sum/count) DESC LIMIT %d", $nid, $nid, $reference_type, $target_type, $n);  
   }  
   else {  
     $sql_result = db_query("SELECT d.content_id2 as 'content_id', (d.sum / d.count) AS 'average', n.title  
       FROM {cre_similarity_matrix} d JOIN {node} n ON d.content_id2 = n.nid  
       WHERE d.content_id1 = %d AND d.content_id2 <> %d AND content_type2 = '%s' AND n.type = '%s'  
       ORDER BY (sum/count) DESC LIMIT %d", $nid, $nid, $target_type, $node->type, $n);  
   }  
   $return_value = array();  
   while ($recommend_obj = db_fetch_object($sql_result)) {  
     $return_value[] = $recommend_obj;  
   }  
   return $return_value;  
   
 }  
105    
106  /*  /*
107  * implementation of votingapi's hook_insert()  * implementation of votingapi's hook_insert()
# Line 227  function cre_similar($n, $nid, $target_t Line 109  function cre_similar($n, $nid, $target_t
109    
110  function cre_votingapi_insert(&$vote){  function cre_votingapi_insert(&$vote){
111    if ($vote->value_type != 'percent' && $vote->value_type != 'points') return;    if ($vote->value_type != 'percent' && $vote->value_type != 'points') return;
   
   cre_modify_avg_difference($vote->uid, $vote->content_id, $vote->content_type);  
112    
113      // just update the average table, the similarity matrix will be updated on cron
114    // update cre_average_vote table    // update cre_average_vote table
115    if (db_result(db_query("SELECT COUNT(*) from {cre_average_vote} where uid = %d", $vote->uid))) {    db_query("UPDATE {cre_average_vote} SET count = count + 1, sum = sum + %d WHERE uid = %d", $vote->value, $vote->uid);
116      db_query("UPDATE {cre_average_vote} SET count = count + 1, sum = sum + %d WHERE uid = %d", $vote->value, $vote->uid);    if (!db_affected_rows()) {
   }  
   else {  
117      db_query("INSERT INTO {cre_average_vote} (uid, sum,count) VALUES (%d, %d, %d)", $vote->uid, $vote->value, 1);      db_query("INSERT INTO {cre_average_vote} (uid, sum,count) VALUES (%d, %d, %d)", $vote->uid, $vote->value, 1);
118    }    }
119          return;          return;
120  }  }
121    
122  /*  /*
 * implementation of votingapi's hook_update  
 */  
 function cre_votingapi_update(&$vote, $new_value){  
   // When a vote is updated, adjust sum - not count - for all records in cre_similarity_matrix where content_id1 or content_id2 equals vote->content_id.  
   // The adjustment to sum is the rating difference.  
   
   $rating_difference = $new_value - $vote->value;  
   
   db_query("UPDATE {cre_similarity_matrix} SET sum = sum + %d WHERE (content_id1 = %d AND content_type1 = '%s') OR (content_id2 = %d AND content_type2 = '%s')", $rating_difference, $vote->content_id, $vote->content_type, $vote->content_id, $vote->content_type);  
   
   db_query("UPDATE {cre_average_vote} SET sum = sum + %d WHERE uid = %d", $rating_difference, $vote->uid);  
 }  
   
 /*  
123  * implementation of votingapi's hook_delete  * implementation of votingapi's hook_delete
124  */  */
125  function cre_votingapi_delete(&$vote){  function cre_votingapi_delete(&$vote){
# Line 279  function cre_votingapi_delete(&$vote){ Line 144  function cre_votingapi_delete(&$vote){
144  * private function to install the module over existing voting data  * private function to install the module over existing voting data
145  */  */
146  function _cre_load_all_diff_avg(){  function _cre_load_all_diff_avg(){
147    $starting_pos = variable_get('cre_starting_position', -1);    global $pos;
148      register_shutdown_function('cre_shutdown');
149      $starting_pos = variable_get('cre_starting_position', 0);
150    $vote_limit = variable_get('cre_max_install_votes', FALSE);    $vote_limit = variable_get('cre_max_install_votes', FALSE);
151    
152    // for every vote within range...^    // for every vote within range...^
153    $db_result = db_query("SELECT vote_id, uid, content_id, content_type FROM {votingapi_vote} WHERE vote_id < %d ORDER BY vote_id DESC LIMIT %d", $starting_pos, $vote_limit);    $db_result = db_query("SELECT vote_id, uid, content_id, content_type FROM {votingapi_vote} WHERE vote_id > %d ORDER BY vote_id ASC LIMIT %d", $starting_pos, $vote_limit);
154    while ($vote_obj = db_fetch_object($db_result)) {    while ($vote_obj = db_fetch_object($db_result)) {
155      // call function cre_modify_avg_difference($uid, $content_id)      // call function cre_modify_avg_difference($uid, $content_id)
156      cre_modify_avg_difference($vote_obj->uid, $vote_obj->content_id, $vote_obj->content_type);      cre_modify_avg_difference($vote_obj->uid, $vote_obj->content_id, $vote_obj->content_type);
157      $vid = $vote_obj->vote_id;      $pos = $vote_obj->vote_id;
158    }    }
   // save the starting position for next run  
   variable_set('cre_starting_position', $vid);  
159  }  }
160    
161  function cre_cron() {  function cre_shutdown() {
162    if (variable_get('cre_max_install_votes', 0)){    global $pos;
163      _cre_load_all_diff_avg();    // save the starting position for next run
164    }    variable_set('cre_starting_position', $pos);
165  }  }
166    
167  /*  function cre_cron() {
168  * This function will be responsible for installing the users' average vote    _cre_load_all_diff_avg();
 */  
   
 function _cre_user_average() {  
   $result = db_query("SELECT DISTINCT uid from {votingapi_vote}");  
   while ($user = db_fetch_object($result)) {  
     // get all 'percent' vote values made by this user  
     $total = db_result(db_query("SELECT SUM(value) from {votingapi_vote} WHERE uid = %d AND value_type = 'percent'", $user->uid));  
     $num_rows = db_num_rows(db_query("SELECT vote_id from {votingapi_vote} where uid = %d", $user->uid));  
   
     db_query("INSERT INTO {cre_average_vote} (uid, sum, count) VALUES (%d, %d, %d)", $user->uid, $total, $num_rows);  
   }  
169  }  }
170    
171  function cre_remove($content_id, $content_type) {  function cre_remove($content_id, $content_type) {
172    db_query("DELETE FROM {cre_similarity_matrix} WHERE content_id1 = %d AND content_type1 = '%s'", $content_id, $content_type);    db_query("DELETE FROM {cre_similarity_matrix} WHERE content_id1 = %d AND content_type1 = '%s'", $content_id, $content_type);
173    db_query("DELETE FROM {cre_similarity_matrix} WHERE content_id2 = %d AND content_type2 = '%s'", $content_id, $content_type);    db_query("DELETE FROM {cre_similarity_matrix} WHERE content_id2 = %d AND content_type2 = '%s'", $content_id, $content_type);
 }  
   
 function theme_cre_title_list($items = array()){  
   $output = array();  
   foreach ($items as $item){  
     $output[] = l($item->title, "node/$item->content_id", array('class' => 'node-' . $item->type));  
   }  
   return theme('item_list', $output);  
 }  
   
 function theme_cre_node_view($nid){  
   return node_view(node_load(array('nid' => $nid)), FALSE);  
174  }  }

Legend:
Removed from v.1.9.2.2.2.13  
changed lines
  Added in v.1.9.2.2.2.14

  ViewVC Help
Powered by ViewVC 1.1.2