Download & Extend

Web 2.0 enhancement to the "Related Modules" block

Project:Drupal.org customizations
Version:6.x-3.x-dev
Component:User interface
Category:feature request
Priority:normal
Assigned:danithaca
Status:needs work

Issue Summary

We've been working on the d.o. "Related Modules" block for 2 years now. We have evaluated 9 alternative algorithms. Result shows that improving the algorithms increases click-through rate from 0.1% to 1.5%. We summarized the results and wrote a paper for ACM Recommender System Conference'09. Early access of the paper can be found at http://mrzhou.cms.si.umich.edu/node/139.

One conclusion from our study was that automatic algorithms, no matter how we refine it, had certain limitations. To further improve the quality of "Related Modules" block, perhaps we have to use Web 2.0 techniques. That is, to provide a textbox (w/ auto-complete) on the module pages for the users to suggest related modules. And then, we aggregate the suggestions using some smart algorithms to display in the "Related Modules" block.

An alternative might be to have the module authors add related links on their module page. However, this has 2 problems: 1) the authors don't necessarily know all the related modules. 2) they might be reluctant to add links to substitute modules.

If the community generally thinks this is a good idea, I can work on a prototype.

Your comments are appreciated!

Comments

#1

Project:Drupal.org infrastructure» Drupal.org webmasters
Component:Drupal.org module» Redesign

I think this needs to go into the redesign queue.

#2

Daniel, I think you should go ahead with this. I don't think adding another block for adding module recommendations is that cumbersome. We can test on drupal.org and see if it adds a lot of value.

#3

Assigned to:Anonymous» danithaca
Status:active» needs work

Thanks! I'll go ahead and try to implement it :)

#4

This is now deployed on staging8.drupal.org. Let's get the module recommendation blocks and the ability to recommend modules that are related added to that site.

#5

Explanation of the algorithm from my advisor Paul Resnick:

"...[This is] an integrated version that automatically adjusts which recommendations it makes based on user clickthroughs, through a technique called a Multi-Armed Bandit learning algorithm. All of the matching algorithms (based on text similarity, co-mention in conversations, or co-installation) generate initial candidate sets of items to recommend, but the actual items to recommend will adjust over time based on clickthroughs. the adjustments can happen offline, in batches, so they don't affect page load performance."

#6

The code is checked in at https://svn.drupal.org/drupal/redesign_modules_sandbox/staging8/custom/

The same copy is posted here for easier review:

<?php
define
('PIVOTS_PID', 6001);

/**
* Implementation of hook_block()
*/
function pivots_project_block($op = 'list', $delta = 0, $edit = array()) {
  switch (
$op) {
    case
'list':
     
$blocks[0]['info'] = t('Pivots: related projects');
      return
$blocks;

    case
'view':
     
$block['subject'] = t('Related projects');
     
$block['content'] = pivots_project_output();
      return
$block;
  }
}

function
pivots_project_output() {
 
// skip nid=3060, the drupal project
 
if (($node = project_get_project_from_menu()) && ($node->nid != 3060)) {
   
$output = '';
   
$node_id = $node->nid;
   
$pivot_id = PIVOTS_PID;

   
$ga_event = "{$node->nid}";
   
$items = _pivots_project_generate_items($node_id, $pivot_id, variable_get('pivots_project_max_display', 5));
    if (!empty(
$items)) {
      foreach (
$items as $position => $item) {
       
$items[$position] = l($item['title'], "node/{$item['nid']}", array( 'attributes' => array(
               
"onClick" => "javascript:pageTracker._trackEvent('PivotsClick_${pivot_id}', '${node_id}_{$item['nid']}');",
               
"rel"     => "nofollow"
       
)));
       
$ga_event .= "_{$item['nid']}";
      }
     
$output = theme('item_list', $items, $title);
    }
    else {
     
$output = t("No related project yet. Please make suggestions. Thanks.");
    }
   
$GLOBALS['conf']['googleanalytics_codesnippet_after'] .= "pageTracker._trackEvent('PivotsPageview_{$pivot_id}', '{$ga_event}');";

   
$output .= _pivots_project_suggestbox($node->nid, $pivot_id);
    return
$output;
  }
}


function
_pivots_project_generate_items($node_id, $pivot_id, $limit) {

 
db_set_active('pivots'); // NOTE: here we activate the pivots database.

  // if there's database failure, we just pretend nothing happens whatsoever. pivots_project returns nothing in this case.
 
$matches = db_query("SELECT DISTINCT dst dest_id FROM {pivots_mab_match} WHERE pid=%d AND src=%d
        AND dst<>%d ORDER BY updated DESC, score DESC"
, $pivot_id, $node_id, $node_id);

 
db_set_active();  // NOTE: change back to use the default database

 
$count = 0;
 
$items = array();
  while ((
$match = db_fetch_array($matches)) && $count < $limit) {
   
$dest_id = $match['dest_id'];
   
$result = db_query("SELECT title FROM {node} WHERE nid=%d AND status=1", $dest_id);
   
// there might be cases that the node was deleted, or set to unpublished between pivots database refresh
    // so here we only count the valid node.
   
$title = db_result($result);
    if (
$title) {
     
$items[] = array('nid' => $dest_id, 'title' => $title, 'pid' => $pivot_id);
     
$count++;
    }
  }
  return
$items;
}

function
_pivots_project_suggestbox($node_id, $pivot_id) {
 
$form = array();

 
$form['suggest'] = array(
   
'#type' => 'button',
   
'#value' => t('Make a suggestion!'),
   
'#weight' => 1,
   
'#id' => 'pivots_project_suggestbutton'
 
);

 
$node = node_load($node_id);
 
drupal_add_js("
      $(document).ready(function() {
        $('#pivots_project_suggestbutton').click(function() {
          var pattern = /^http[:][/][/]drupal[.]org[/]project[/]\w+$/;
          var suggestion = ''; //$('#pivots_project_suggestbox').val();
          var promption = 'Please suggest a related project to -{$node->title}- using its URL such as <a href="
http://drupal.org/project/xxx" title="http://drupal.org/project/xxx" rel="nofollow">http://drupal.org/project/xxx</a>. Thank you.';
         
var error_promption = 'Your suggested URL does not look like <a href="http://drupal.org/project/xxx" title="http://drupal.org/project/xxx" rel="nofollow">http://drupal.org/project/xxx</a>. Please try again, or press Cancel.';
          var
success_promption = 'Thank you for suggesting a related project. It may or may not display soon according to the competition algorithm.';
          while (!
suggestion.match(pattern)) {
           
suggestion = prompt(promption);
            if (!
suggestion) break;
            else
promption = error_promption;
          }
          if (
suggestion) {
           
alert(success_promption);
           
pageTracker._trackEvent('PivotsSuggest_${pivot_id}', '${node_id}_'+suggestion);
          }
        });
      });
   
", 'inline');

  return drupal_render_form('pivots_project_suggestbox', $form);
}
?>

#7

A working demo can be found at http://staging8.drupal.org/project/cck

#8

Please provide a patch so I can see the changes.

#9

Status:needs work» needs review

patch attached. thanks!

AttachmentSize
pivots_block.module.patch 5.29 KB

#10

I'd have liked to read the paper, but the PDF does not seem to be downloadable from the URL given:

http://mrzhou.cms.si.umich.edu/files/sites/mrzhou.cms.si.umich.edu/files...

(interesting spam you have over there, BTW: some of it looks actually manual)

#11

Any movement on this? Having the broken block on there is just taking up space...

Michelle

#12

@Michelle: Thanks for asking. May I ask a favor from you to review the code for me? I'll reciprocate if ever you'll need a code review. thanks!! --daniel

#13

I don't really know anything about reviewing code... I looked at the patch and didn't see anything that jumped out at me. Not sure what to say about it...

Michelle

#14

Project:Drupal.org webmasters» Drupal.org customizations
Version:<none>» 6.x-2.x-dev
Component:Redesign» User interface

This looks like a patch to drupalorg.

#15

Status:needs review» needs work

This only sends data to Google Analytics. They do provide an API to get data back out, but it would be good to have a copy recorded directly in our local database, in a new table.

#16

Version:6.x-2.x-dev» 6.x-3.x-dev
Status:needs work» needs review

@drumm: I have uploaded a new patch with minor changes on the Google Analytics code. Yes. You are right. The patch only sends data to GA, but then we have a Python script (mab4do.py in attachment) that retrieves the GA data, saves the data in a local database hosted on d.o., and then updates the "related projects" block based on the GA clicks/suggestions data. The Python script would be running on scratchvm.drupal.org once a day. The algorithm we use is based on a particular version of the multi-armed bandit algorithm. It is documented in the paper Optimal learning and experimentation in bandit problems.

I have fully tested the patch on our local d.o. development environment. If the code review is fine, I can do "git apply patch" to the drupalorg module myself. Thanks.

AttachmentSize
pivots_block.patch 2.59 KB
mab4do.py_.zip 4.95 KB

#17

Status:needs review» needs work

- The JS should be in an external file, rather than being even more inline. This should allow \/ rather than [/] for escaping the /.
- The URL patterns should check for https

#18

Status:needs work» needs review

Thanks drumm! New patch attached:

- made JS into an external file; used \/ instead of [/]
- URL patterns check for https
- made some code refactoring.

AttachmentSize
pivots_block.patch 6.44 KB

#19

Looking better.

- In JS, use a Drupal behavior, http://drupal.org/node/304258. This will make sure the JS runs only once.
- The ul[id^="pivots-block-list"] selector might be slow. Can an exact class or id be used?
- onclick events should be defined in the .js file and attached with jQuery.

#20

Status:needs review» needs work

#21

Status:needs work» needs review

Thanks for the feedback drumm. New patch attached, and is tested successfully on my local d.o. dev.

-- Used Drupal.behaviors in JS
-- Instead of using [id^="pivots-block-list"], now it uses exact class/id match.
-- onclick events are now defined in JS and attached with JQuery.

If the patch looks fine, can you let me check in the patch myself? I'm waiting for #658218: get access to stagingvm in order to run the python update script that goes together with the patch. Thanks.

AttachmentSize
pivots_block.patch 7.17 KB

#22

Status:needs review» needs work

The PHP looks like an improvement. $ga_data = "$node_id"; seems a bit redundant and I'd like to see 'variables' . $concatenated . 'like_this', but it is still improved enough.

I did run the JS through JSLint, and got to the attached JS, which I have not tested at all.

AttachmentSize
pivots_block.js_.txt 1.3 KB

#23

Status:needs work» needs review

Thanks drumm for the suggestion and JSlint code. New patch attached:

-- changed the redundant $ga_data = "$node_id"; to be $ga_data = $node_id;
-- replaced original JS file with the JSLint output.

I have fully tested the patch on my local d.o. dev environment.

AttachmentSize
pivots_block.patch 7.74 KB

#24

Status:needs review» fixed

Committed and will probably deploy this afternoon.

#25

Deployed.

#26

Status:fixed» needs work

This got rid of the existing suggestions, and threw a JS error on splitting undefined. I had to roll it back.

I'll need to see this working on a dev site, http://drupal.org/node/1018084, with the block cache turned on, before considering again.

#27

Got it. Sorry drumm for the trouble. I've tested it on my local d.o. dev server; don't know why it throws JS error on d.o....
I'm travelling now. Will resume in 10 days.

nobody click here