/Modifier.class.php
PHP | 1077 lines | 491 code | 155 blank | 431 comment | 113 complexity | 943e62f0165526cbf96b570d9c8932e4 MD5 | raw file
- <?php
- /**
- * Recommender Ranking Modifier Base Class
- *
- * The ModifierFactory implements a factory to create
- * concrete Modifier objects of various types.
- *
- * Each Modifier object subclasses the Modifier base class.
- *
- * Kristian Ljungkvist Created 08/20/07
- * Note -- Requires PHP 5
- *
- * Copyright 2007 Science Buddies. All Rights Reserved
- */
- class Modifier {
-
- /**
- * Constructor
- *
- *
- * @author Kristian Ljungkvist
- */
- function __construct() {
- }
-
- /**
- * process method
- *
- * Each Modifier must override this method.
- * @author Kristian Ljungkvist
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- return $ranking_list;
- }
- /**
- * array_move
- *
- * Moves an element of an associative array to a new location in that array.
- *
- * Kristian Ljungkvist new rewrite 04/23/2009.
- **/
- function array_move($array,$from,$key,$to) {
- // echo "array_move: $key from position $from to position $to<br/>";
- $org_count = count($array);
- if( ($from < 1) || ($to < 1)) {return $array;}
- if($from == $to) {return $array;}
- if(!$key) {return $array;}
- if($to > count($array)) {return $array;}
- $value_to_move = $array[$key];
- // 1. copy array minus item to move
- foreach($array as $cur_key => $val) {
- if($cur_key != $key) {
- $copied_array[$cur_key] = $val;
- }
- }
- // 2. copy up to 'to' position from that new array
- $index = 1;
- foreach($copied_array as $cur_key => $val) {
- if($index >= $to) {break;}
- $copied_array_2[$cur_key] = $val;
- $index++;
- }
- // 3. add item to move to 'to' position
- $copied_array_2[$key] = $value_to_move;
- // 4. copy anything left after 'to' in copied array
- $index = 1;
- foreach($copied_array as $cur_key => $val) {
- if($index < $to) {$index++; continue;}
- $copied_array_2[$cur_key] = $val;
- $index++;
- }
- // Check that the resulting array is of the same length as the original. If not, notify me and return the original.
- if(count($copied_array_2) != $org_count) {
- // Notify me by email if this happens-- KEL 09/24/08
- $msg = "org_count: $org_count\n";
- $msg .= "result_count: ".count($copied_array_2)."\n";
- $msg .= "Original array:\n";
- $msg .= print_r($array,true);
- $msg .= "Resulting array:\n";
- $msg .= print_r($copied_array_2,true);
- mail("kristian.ljungkvist@gmail.com","Modifier.class.php -- array_move -- Counts Differ",$msg);
- return $array; // return original, unmodified array in this case.
- }
- return($copied_array_2);
- }
- /**
- * array_move
- *
- * Moves an element of an associative array to a new location in that array.
- *
- * Kristian Ljungkvist 09/19/2007.
- **/
- function recent_defunct_array_move($array,$from,$key,$to) {
- $org_count = count($array);
- // echo "from:$from, key:$key, to:$to, count:".count($array)."\n";
- if($from == $to) {return $array;}
- if(!$key) {return $array;}
- if($to >= count($array)) {return $array;}
- // if($array[$from] != $key) {return $array;}
- $tmp_array = array();
-
- // get the item to be moved
-
- $contents = $array[$key];
-
- $index = 0;
- $moved = false;
- foreach($array as $cur_key =>$val) {
- if(!$cur_key) {continue;}
- if($cur_key == $key) {continue;}
- if(($index != $to) || $moved) {
-
- $tmp_array[$cur_key] = $val;
- } else {
- $tmp_array[$key] = $contents;
- $tmp_array[$cur_key] = $val;
- $moved = true;
- }
- $index++;
- }
- if(count($tmp_array) != $org_count) {
- // Notify me by email if this happens-- KEL 09/24/08
- $msg = "org_count: $org_count\n";
- $msg .= "result_count: ".count($tmp_array)."\n";
- $msg .= "Original array:\n";
- $msg .= print_r($array,true);
- $msg .= "Resulting array:\n";
- $msg .= print_r($tmp_array,true);
- mail("kristian.ljungkvist@gmail.com","Modifier.class.php -- array_move -- Counts Differ",$msg);
- return $array; // return original, unmodified array in this case.
- }
- return $tmp_array;
- }
- function defunct_array_move($array,$from,$key,$to) {
- // Do some sanity checks on the parameters passed in.
- // echo "array_move(array,$from,$key,$to)<br/>";
-
- if(!is_array($array)) {
- // echo "invalid array argument passed to array_move!";
- return $array;
- }
- if(!$key) {
- // echo "key argument is empty in array_move!";
- return $array;
- }
-
- if($from == $to) {return $array;}
-
- if($from > count($array) || $from < 0) {
- // echo "'from' argument ($from) passed to array_move out of bounds!";
- return $array;
- }
-
- if($to > count($array) || $to < 0) {
- // echo "'to' argument ($to) passed to array_move out of bounds!";
- return $array;
- }
- /*
- if(array_search($key,$array,1) != ($from)) {
- echo "value $key is not at index ".($from)." in original array! (it's at index:".array_search($key,$array,1).")<br/>\n";
- return($array);
- }
- */
- if($this->index_of_key($array,$key) != $from) {
- // echo "value $key is not at index ".($from)." in original array! (it's at index:".$this->index_of_key($array,$key).")<br/>\n";
- return($array);
- }
- // OK, things look reasonable. Now copy the item to be moved
-
- $contents = $array[$key];
-
- // First, remove the original instance
- $left = array_slice ($array, 0, $from-1);
- $right = array_slice ($array, $from);
- // Put the array back together, sans the removed item.
- $array = array_merge ($left, $right);
-
- // Now, insert the removed item in it's new position
- $left = array_slice ($array, 0, $to-1);
- $right = array_slice ($array, $to-1);
-
- $left[$key] = $contents;
- // Put the arrray back together again.
- $array = array_merge ($left,$right);
- // Now, test that the item was indeed inserted at the correct spot.
-
- if($this->index_of_key($array,$key) != ($to -1)) {
- // echo "array_move:: item $key was inserted into position ".$this->index_of_key($array,$key)." rather than ".($to -1)."! Error?<br/>\n";
- } else {
- // echo "array_move: Success!<br/>\n";
- }
- return $array;
- }
- /**
- * index_of_key
- *
- * Finds the index of a given key in an associative array., After much research, it doesn't appear that
- * PHP provides an easy way to get this value.
- *
- * Kristian Ljungkvist 09/19/2007.
- **/
-
- function index_of_key($array,$key) {
- $cur_i = 0;
- foreach($array as $SK => $MSD) {
- if(strpos($SK, '_') === 0) {
- continue;
- }
- if($SK == $key) {
- return $cur_i;
- }
- $cur_i++;
- }
- return $cur_i;
- }
- /**
- * key_at_index
- *
- * Finds the key of a given entry in an associative array., After much research, it doesn't appear that
- * PHP provides an easy way to get this value.
- *
- * Kristian Ljungkvist 09/19/2007.
- **/
- function key_at_index($array,$index) {
- $cur_index = 0;
- if(index > count($array)) {
- // echo "index passed to key_at_index out of bounds";
- return "Error";
- }
- $i=0;
- foreach($array as $SK => $MSD) {
- if(strpos($SK, '_') === 0) {
- continue;
- }
- if($i == $index) {
- return $SK;
- }
- $i++;
- }
- }
- }
- /**
- * Recommender Ranking Modifier Factory Class
- *
- * The ModifierFactory implements the factory design pattern
- * Creates concrete Modifier objects of various types.
- *
- * Kristian Ljungkvist Created 08/20/07
- * Note -- Requires PHP 5
- *
- * Copyright 2007 Science Buddies. All Rights Reserved
- */
- class ModifierFactory {
- function createModifier($modifier_name) {
- switch($modifier_name) {
- case 'NewProjectRandomizer':
- return new NewProjectRandomizerModifier;
- break;
- case 'ProjectRandomizer':
- return new ProjectRandomizerModifier;
- break;
- case 'Paginator':
- return new PaginatorModifier;
- break;
- case 'AreaAssignment':
- return new AreaAssignmentModifier;
- break;
- case 'DifficultyLevel':
- return new DifficultyLevelModifier;
- break;
- case 'TimeRequired':
- return new TimeRequiredModifier;
- break;
- case 'Deduper':
- return new DeduperModifier;
- break;
- case 'FirstPageNotRandom':
- return new FirstPageNotRandomModifier;
- break;
- case 'HoneyPot':
- return new HoneyPotModifier;
- break;
- case 'InterestArea':
- return new InterestAreaModifier;
- break;
- }
- //echo "Invalid Modifier name passed to createModifier Factory method!<br/>";
- return false;
- }
- }
- class PaginatorModifier extends Modifier {
-
- /**
- * process method
- *
- * Overrides the Modifier Base class process method.
- *
- * The Paginator grabs the correct chunk of the ranking list based on the current page
- * and the configuration variable MaxProjectsPerPage in the recommender_config table.
- *
- * @author Kristian Ljungkvist
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- //echo "In PaginatorModifier<br/>";
- $result_array = array();
- // Grab the current page and the MaxProjectsPerPage configuration setting
- $projects_per_page = $configuration['MaxProjectsPerPage'];
- $page = $state_array['p'];
-
- if(!$page) {
- $page = 1; // default to page # 1.
- }
-
- // Now, copy only the chunk of projects that correspond to the current page.
- $cur_index = 0;
- foreach($ranking_list as $SK => $MSD) {
- // escape the header fields that are prefixed with an underline, -- KEL 10/18/07
- if(strpos($SK, '_') === 0) {
- continue;
- }
- $cur_index++;
- if($cur_index > (($page -1 )* $projects_per_page) && $cur_index <= ($page * $projects_per_page)) {
- $result_array[$SK] = $MSD;
- }
- }
- return $result_array;
- }
- }
- class AreaAssignmentModifier extends Modifier {
- var $interest_area_array = array('phys_sci' => array('Aero','Astro','Chem','Music','Phys','PhysSci','AeroEng','ApMech','ChemE','CE','CompEng','Elec','EnvEng','MatSci','ME','Robotics','Engr','CompSci','FoodSci','Photo'),
- 'life_sci' => array('BioChem','Genom','MamBio','MicroBio','Pharm','PlantBio','Zoo','LifeSci','EnvSci','Sports'),
- 'earth_sci' => array('EnvSci','Geo','OceanSci','Weather','EarthSci'),
- 'env_sci' => array('EnvSci','Geo','OceanSci','Weather','EarthSci'),
- 'engr' => array('Aero','AeroEng','ApMech','ChemE','CE','CompEng','Elec','EnvEng','MatSci','ME','Robotics','Engr','CompSci','Photo')
- );
- /**
- * process method
- *
- * Overrides the Modifier Base class process method.
- *
- * The AreaAssignment modifier filters out project ideas from the ranking list that don't relate to the individual's
- * interest area assignment by their teacher.
- *
- * @author Kristian Ljungkvist
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- //echo "In AreaAssignmentModifier<br/>";
- $result_array = array();
- // Grab this user's AreaAssignment from the individual_profile.
- $area_assignment = $individual_profile['AreaAssignment'];
- // If the teacher didn't restrict the interest area, just do nothing.
- if(!$area_assignment || $area_assignment == 'none') {
- return $ranking_list;
- }
-
- // OK, we have an interest area restriction. Loop through the ranking list, filtering out any projects that do
- // not map to that interest area, based on a lookup in the interest_area array.
-
- foreach($ranking_list as $SK => $MSD) {
- // escape the header fields that are prefixed with an underline, -- KEL 10/18/07
- if(strpos($SK, '_') === 0) {
- continue;
- }
- // get the subarea from the file name
- preg_match('/^([^_]+)/', $SK, $match);
- $subarea = $match[0];
- if($sk_summary[$subarea]['SKs'][$SK]['Questions']['Type'] == 'Test') {
- $result_array[$SK] = $MSD; // Don't filter out "Test" type projects, since these are the neighborhood identifiers.
- continue;
- }
-
- // Check if this subarea is listed for the individual's area_assignment
- if(in_array($subarea,$this->interest_area_array[$area_assignment])) {
- $result_array[$SK] = $MSD;
- }
- }
- return $result_array;
- }
- }
- class InterestAreaModifier extends Modifier {
- /**
- * process method
- *
- * Overrides the Modifier Base class process method.
- *
- * The InterestArea modifier filters out project ideas from the ranking list that are not in the specified interest area.
- *
- * @author Kristian Ljungkvist 05/09/2008
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- //echo "In InterestAreaModifier<br/>";
- $result_array = array();
- $interest_area = $state_array['ia'];
- // If no interest area was specified, just do nothing.
- if(!$interest_area || $interest_area == 'none') {
- return $ranking_list;
- }
-
- // OK, we have an interest area restriction. Loop through the ranking list, filtering out any projects that are
- // not in that interest area.
-
- foreach($ranking_list as $SK => $MSD) {
- // escape the header fields that are prefixed with an underline, -- KEL 10/18/07
- if(strpos($SK, '_') === 0) {
- continue;
- }
- // get the subarea from the file name
- preg_match('/^([^_]+)/', $SK, $match);
- $subarea = $match[0];
-
- // Check if this subarea matches the current one
- if($subarea == $interest_area) {
- $result_array[$SK] = $MSD;
- }
- }
- return $result_array;
- }
- }
- class NewProjectRandomizerModifier extends Modifier {
-
- /**
- * process method
- *
- * Each Modifier must override this method.
- * @author Kristian Ljungkvist
- */
-
- /*
- New Project Idea Randomizer Modifier
- We want to perturb (up or down) the ranking for projects that have few measures of satisfaction and that are loosely related to the top ranked project.
- We define related projects by a lookup table that maps interest areas. For now, we'll use the same table as for
- the Area of Science Limitations.
- This randomizer only moves low-MOS projects that are in the same category (based on the above table) as the
- top-ranked project. This way, we avoid potentially promoting projects that are totally unrelated to the user's area of interest.
- Note also that this is separate from the global Area Of Science filtering that happens when the user has been given an area of science by his or her teacher.
- So, for those low-MOS projects within the right range of subareas:
- Let (MaturityDefinitionCutoff - nMOS) represent the percentage of times a project is randomized. Let's say nMOSCutoff is 100.
- So, a project with 0 MOS is randomized each time. A project with 50 MOS is randomized half the time, etc.
- This would essentially be a linear function. Each time a ranking is produced, those projects with
- nMOS < MaturityDefinitionCutoff are given the chance to be randomized. The randomizer "tosses a coin" based on
- nMos/MaturityDefinitionCutoff for each such project to decide if it should be randomized.
- As the Measures of Satisfaction increase, the project's ranking behavior becomes more stable. Once
- the project has 100 (or whatever cut-off we decide on) MOS, it is no longer randomized by this modifier, and is always ranked
- per it's profile as all other projects are. Note that a separate randomizer modifier might perturb the global ranking later.
- The project profile data includes the # of measures of satisfaction. This data would be computed by
- the batch process that updates the project profile data. By doing this, the recommender doesn't have to do expensive
- database lookups to properly randomize rankings.
- The range within which a project can be shuffled is determined by a field in the recommender_config table: NewProjectShuffleRange.
- The range is the original location plus/minus this constant. Any location within that range is equally likely.
- For example: Project A is originally ranked at slot #25. NewProjectShuffleRange is 10. The new location can be
- anywhere from 15 to 35.
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- //echo "In NewProjectRandomizerModifier<br/>";
-
- // First, determine if we're being called the first time this ranking has been processed.
-
- if($state_array['p'] > 1) {
- // Do nothing if this list has already been processed.
- return $ranking_list;
- }
-
- $mos_cutoff = $configuration['MaturityDefinitionCutoff'];
- $shuffle_range = $configuration['NewProjectShuffleRange'];
- $first_page_projects_not_random = $configuration['FirstPageProjectsNotRandom'];
- $projects_per_page = $configuration['MaxProjectsPerPage'];
- $only_seed_in_top_interest_area = $configuration['OnlySeedInTopInterestArea'];
- // Get the interest area for the top-ranked project.
- $top_project = key($ranking_list);
-
- preg_match('/^([^_]+)/', $top_project, $match);
- $top_project_interest_area = $match[0];
-
- //echo "top project interest area: $top_project_interest_area<BR/>";
- // As we traverse the assoc array, we keep an internal index or counter.
-
- // If we're at index = 20 and we need to move it to 15, we first remove the current item, and then insert it at index=15.
-
-
- $cur_index = 0;
- $reordered_ranking_list = $ranking_list; // Copy the original ranking list.
- foreach($ranking_list as $SK => $MSD) {
- // escape the header fields that are prefixed with an underline, -- KEL 10/18/07
- if(strpos($SK, '_') === 0) {
- continue;
- }
- // KEL 10/04/07 -- Skip any items on the first page if recommender_config.FirstPageProjectsNotRandom is 1.
- if($first_page_projects_not_random && ($cur_index < $projects_per_page)) {
- $cur_index++;
- continue;
- }
- // get the subarea from the file name
- preg_match('/^([^_]+)/', $SK, $match);
- $subarea = $match[0];
-
- // Check if this subarea matches that of the top+ranked project (if switch is set in config to do so.)
- if(!$only_seed_in_top_interest_area || ($subarea == $top_project_interest_area)) {
- // Check if the project is "immature".
- // This is defined as "Maturity < MaturityDefinitionCutoff
- //echo "$SK:Maturity = ".$sk_summary[$subarea]['SKs'][$SK]['Questions']['Maturity']."<br/>";
- if($sk_summary[$subarea]['SKs'][$SK]['Questions']['Maturity'] < $mos_cutoff) {
- // toss a coin (preportional to (MaturityDefinitionCutoff - nMOS) )to determine if this project should be shuffled this time.
- if(rand(0,$mos_cutoff) >= $sk_summary[$subarea]['SKs'][$SK]['Questions']['Maturity']) {
- // Shuffle this project in the ranking by a random offset in the range +/- shuffle_range.
-
-
- // Get the current position in the reordered ranking list
- $current_position = $this->index_of_key($reordered_ranking_list,$SK);
- $low_shuffle_range = $current_position - $shuffle_range;
- if($low_shuffle_range < 0) { $low_shuffle_range = 0;}
- // KEL 10/04/07 -- Make sure no randomized projects end up getting promoted to the first page if configured not to.
- if($first_page_projects_not_random && ($low_shuffle_range < $projects_per_page)) {
- $low_shuffle_range = $projects_per_page;
- }
- $high_shuffle_range = $current_position + $shuffle_range;
- if($high_shuffle_range > count($ranking_list)) {
- $high_shuffle_range = count($ranking_list);
- }
- $random_new_index = rand($low_shuffle_range,$high_shuffle_range);
- // move the entry to this new position.
- //echo "NewProjectRandomizer: moving $SK from position $current_position to position $random_new_index<br/>";
- $reordered_ranking_list = $this->array_move($reordered_ranking_list,$current_position,$SK,$random_new_index);
- }
- }
- }
- $cur_index++;
- }
- /*
- echo "Original ranking list:<br/>";
- print_r($ranking_list);
- echo "<hr/>";
- echo "Reordered ranking list:<br/>";
- print_r($reordered_ranking_list);
- echo "<hr/>";
- */
-
- return $reordered_ranking_list;
-
- }
- }
- class ProjectRandomizerModifier extends Modifier {
- /**
- * process method
- *
- * Each Modifier must override this method.
- * @author Kristian Ljungkvist
- */
- /**
- "Random" Randomizer Modifier
- This randomizer differs from NewProjectRandomizer in that it randomizes the list without regard to maturity. It also
- works differently in that it limits the number of items to perturb by the RandomProjectsRatio.
- The idea behind this modifier is to perturb the ranking of more mature projects as well so that the rankings don't
- ossify but have a chance to evolve.
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- //echo "In RandomizerModifier<br/>";
- // First, determine if we're being called the first time this ranking has been processed.
- if($state_array['p'] > 1) {
- // Do nothing if this list has already been processed.
- return $ranking_list;
- }
- $shuffle_range = $configuration['ProjectShuffleRange'];
- $first_page_projects_not_random = $configuration['FirstPageProjectsNotRandom'];
- $projects_per_page = $configuration['MaxProjectsPerPage'];
- $random_projects_ratio = $configuration['RandomProjectsRatio'];
- /**
- Algorithm:
- Find total # of items in ranking
- subtract one page worth (if FirstPageProjectsNotRandom is true)
- Now multiply this number by RandomProjectsRatio
- This is the number of items to randomize (randomized_items).
- loop for randomized_items iterations:
- find a random number between 1+PageLength and total lines in ranking
- Move it a random number of steps +/- $shuffle_range.
- end loop;
- */
- $length_of_ranking = count($ranking_list);
- $beginning_of_range_to_randomize = 0;
- if($first_page_projects_not_random) {
- $length_of_ranking -= $projects_per_page;
- $beginning_of_range_to_randomize = ($projects_per_page);
- }
-
- //echo "<hr/>ProjectRandomizer:length of ranking: $length_of_ranking, beginning_of_range_to_randomize: $beginning_of_range_to_randomize, projects per page: $projects_per_page<hr/>";
- $number_of_items_to_randomize = $random_projects_ratio * $length_of_ranking;
- $reordered_ranking_list = $ranking_list; // Copy the original ranking list.
-
- for($i=1;$i < $number_of_items_to_randomize;$i++) {
- $cur_index = rand($beginning_of_range_to_randomize,(count($reordered_ranking_list)));
- // Shuffle this project in the ranking by a random offset in the range +/- shuffle_range.
- $low_shuffle_range = $cur_index - $shuffle_range;
- if($low_shuffle_range < 0) { $low_shuffle_range = 0;}
- // KEL 10/04/07 -- Make sure no randomized projects end up getting promoted to the first page if configured not to.
- if($first_page_projects_not_random && ($low_shuffle_range < $projects_per_page)) {
- $low_shuffle_range = $projects_per_page;
- }
- $high_shuffle_range = $cur_index + $shuffle_range;
- if($high_shuffle_range > count($reordered_ranking_list)) {
- $high_shuffle_range = count($reordered_ranking_list);
- }
- $random_new_index = rand($low_shuffle_range,$high_shuffle_range);
- // Get the current position in the reordered ranking list
- // move the entry to this new position.
- $SK = Modifier::key_at_index($reordered_ranking_list,$cur_index);
- //echo "ProjectRandomizer: moving $SK from position $cur_index to position $random_new_index<br/>";
- $reordered_ranking_list = $this->array_move($reordered_ranking_list,$cur_index,$SK,$random_new_index);
- }
- return $reordered_ranking_list;
- }
- }
- class DifficultyLevelModifier extends Modifier {
-
- var $grade_to_difficulty_map = array('K' => array(1,2,3,4),
- '1' => array(1,2,3,4),
- '2' => array(1,2,3,4),
- '3' => array(1,2,3,4),
- '4' => array(1,2,3,4),
- '5' => array(1,2,3,4),
- '6' => array(4,5,6,7),
- '7' => array(4,5,6,7),
- '8' => array(4,5,6,7),
- '9' => array(6,7,8,9,10),
- '10' => array(6,7,8,9,10),
- '11' => array(6,7,8,9,10),
- '12' => array(6,7,8,9,10),
- 'Adult' => array(1,2,3,4,5,6,7,8,9,10)
- );
-
- /**
- * process method
- *
- * Overrides the Modifier Base class process method.
- *
- * The AreaAssignment modifier filters out project ideas from the ranking list that don't relate to the individual's
- * interest area assignment by their teacher.
- *
- * @author Kristian Ljungkvist
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- //echo "In DifficultyLevelModifier<br/>";
- $result_array = array();
- // Grab this user's GradeLevel or currently set difficultylevel override based on "show harder/easier" from the individual_profile.
- if($individual_profile['DifficultyLevel']) {
- $difficulty_level = $individual_profile['DifficultyLevel'];
- } else {
- $difficulty_level = $individual_profile['GradeLevel'];
- }
- //echo "DifficultyLevelModifier::difficulty_level = $difficulty_level<br/>";
- // If the user is an adult, just do nothing.
- if($difficulty_level == 'Adult' || !$difficulty_level) {
- return $ranking_list;
- }
-
- // OK, we have a difficulty level restriction. Loop through the ranking list, filtering out any projects that do
- // not map to the appropriate range, ased on a lookup in the grade_to_difficulty_map.
-
- foreach($ranking_list as $SK => $MSD) {
- // escape the header fields that are prefixed with an underline, -- KEL 10/18/07
- if(strpos($SK, '_') === 0) {
- continue;
- }
- // get the subarea from the file name
- preg_match('/^([^_]+)/', $SK, $match);
- $subarea = $match[0];
-
- // Get the DifficultyLevel_Low from the sk_profile.
- $project_difficulty_low = $sk_summary[$subarea]['SKs'][$SK]['Questions']['DifficultyLevel_Low'];
- $project_difficulty_high = $sk_summary[$subarea]['SKs'][$SK]['Questions']['DifficultyLevel_High'];
- //echo "project_difficulty[$SK] = $project_difficulty<br/>";
- // echo "project:$SK -- DifficultyLevel_Low = $project_difficulty_low, DifficultyLevel_High = $project_difficulty_high<br/>";
- if($project_difficulty_low == 1 && $project_difficulty_high == 10) {
- $result_array[$SK] = $MSD;
- continue;
- }
- // Check if this subarea is listed for the individual's area_assignment
- if((in_array($project_difficulty_low,$this->grade_to_difficulty_map[$difficulty_level]) || (in_array($project_difficulty_high,$this->grade_to_difficulty_map[$difficulty_level])))) {
- $result_array[$SK] = $MSD;
- }
- }
- return $result_array;
- }
- }
- class TimeRequiredModifier extends Modifier {
-
- var $time_required_map = array('tomorrow' => array('Very short'),
- 'week' => array('Very short','Short','Average'),
- 'several_weeks' => array('Very short','Short','Average','Long'),
- 'month' => array('Very short','Short','Average','Long','Very long'));
-
- /**
- * process method
- *
- * Overrides the Modifier Base class process method.
- *
- * TheTimeRequred modifier filters out project ideas from the ranking list that require more time than the user indicated he or she has.
- *
- * @author Kristian Ljungkvist
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- //echo "In TimeRequiredModifier<br/>";
- $result_array = array();
- // Grab this user's ProjectDuration
- $project_duration = $individual_profile['ProjectDuration'];
-
- // If time available is a month or more, return the full list.
- if($project_duration == 'month' || !$project_duration) {
- return $ranking_list;
- }
-
- // OK, we have a project duration restriction. Loop through the ranking list, filtering out any projects that do
- // not map to the appropriate range, ased on a lookup in the time_required_map.
-
- foreach($ranking_list as $SK => $MSD) {
- // escape the header fields that are prefixed with an underline, -- KEL 10/18/07
- if(strpos($SK, '_') === 0) {
- continue;
- }
- // get the subarea from the file name
- preg_match('/^([^_]+)/', $SK, $match);
- $subarea = $match[0];
-
- // Get the TimeRequired from the sk_profile.
- if($sk_summary[$subarea]['SKs'][$SK]['Questions']['Type'] == 'Test') {
- $result_array[$SK] = $MSD;
- continue;
- }
- $project_time = $sk_summary[$subarea]['SKs'][$SK]['Questions']['TimeRequired'];
- //echo "project_time[$SK] = $project_time<br/>";
-
- // Check if this subarea is listed for the individual's area_assignment
- foreach($this->time_required_map[$project_duration] as $cur_time) {
- //echo "Haystack:$project_time, Needle:$cur_time<br/>";
- if(stripos($project_time,$cur_time)!== false) {
- // echo "MATCH!<br/>";
- $result_array[$SK] = $MSD;
- break;
- }
- }
- }
- return $result_array;
- }
- }
- class DeduperModifier extends Modifier {
- var $groups_seen = array();
- /**
- * process method
- *
- * Overrides the Modifier Base class process method.
- *
- * Deduper modifier filters out project ideas with the same GroupID as one already shown.
- *
- * @author Kristian Ljungkvist
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- // echo "In DeduperModifier<br/>";
- // echo "ranking_list passed in:<br/>";
- // print_r($ranking_list);
- $result_array = array();
- // Loop through the ranking list, remembering each newly encountered GroupID. If an item has a non-null GroupID that we've
- // already seen, remove it from the ranking.
- foreach($ranking_list as $SK => $MSD) {
- // escape the header fields that are prefixed with an underline, -- KEL 10/18/07
- if(strpos($SK, '_') === 0) {
- continue;
- }
- // get the subarea from the file name
- preg_match('/^([^_]+)/', $SK, $match);
- $subarea = $match[0];
- // Get the GroupID from the sk_profile.
- $group_id = $sk_summary[$subarea]['SKs'][$SK]['Questions']['GroupID'];
- //echo "GroupID[$SK] = $group_id<br/>";
- if($group_id && $group_id != 'NULL' && $group_id != 'null') {
- //echo "GroupID[$SK] = $group_id<br/>";
- // Check if this GroupID has already been included
- if(in_array($group_id,$this->groups_seen)) {
- //echo "$group_id already seen. Skipping...<br/>";
- continue;
- }
- // OK, so we have a new group ID. SImply add it to the list.
- $this->groups_seen[] = $group_id;
-
- }
- $result_array[$SK] = $MSD;
- }
- // echo "<br/>groups seen: <br/>";
- // print_r($this->groups_seen);
- // echo "<br/>ranking list returned:<br/>";
- // print_r($result_array);
- return $result_array;
- }
- }
- class HoneyPotModifier extends Modifier {
- var $groups_seen = array();
- /**
- * process method
- *
- * Overrides the Modifier Base class process method.
- *
- * Honeypot modifier filters out honey pot projects. Will eventually trigger some awesome code.
- *
- * @author Kristian Ljungkvist
- */
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- //echo "In DeduperModifier<br/>";
- $result_array = array();
- // Loop through the ranking list, remembering each newly encountered GroupID. If an item has a non-null GroupID that we've
- // already seen, remove it from the ranking.
- foreach($ranking_list as $SK => $MSD) {
- // escape the header fields that are prefixed with an underline, -- KEL 10/18/07
- if(strpos($SK, '_') === 0) {
- continue;
- }
- // get the subarea from the file name
- preg_match('/^([^_]+)/', $SK, $match);
- $subarea = $match[0];
- // Check if this is a Honey pot.
- if($subarea == 'HoneyPot') {
- continue; // Skip it.
- }
- $result_array[$SK] = $MSD; // Add non-honeypots to our results.
- }
- return $result_array;
- }
- }
- class FirstPageNotRandomModifier extends Modifier {
- var $groups_seen = array();
- /**
- * process method
- *
- * Overrides the Modifier Base class process method.
- *
- * FirstPageNotRandom modifier ensures that all items on the first page are in order if the configuration flag is set.
- *
- * @author Kristian Ljungkvist
- */
- /*
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- $result_array = array();
- echo "<hr/>In FirstPageNotRandomModifier<hr/>";
- $shuffle_range = $configuration['ProjectShuffleRange'];
- $first_page_projects_not_random = $configuration['FirstPageProjectsNotRandom'];
- $projects_per_page = $configuration['MaxProjectsPerPage'];
- // First, check if FirstPageNotRandom flag is set. If not, do nothing.
- if(!$first_page_projects_not_random) {
- return $ranking_list;
- }
- // Loop through the items on the first page, as defined by $projects_per_page.
- // If an item has a higher MSD than the one following it, it was inserted randomly and needs to be moved.
- // If so, move the item to a spot in the range from $projects_per_page to $projects_per_page + $shuffle_range.
- $cur_index = 0;
- $prev_msd = 0;
- $prev_sk = '';
- $cur_msd = 0;
- $reordered_ranking_list = $ranking_list; // Copy the original ranking list.
- echo "<hr/>before:<hr/>";
- print_r(array_slice($reordered_ranking_list,0,10));
- foreach($ranking_list as $SK => $MSD) {
- // escape the header fields that are prefixed with an underline, -- KEL 10/18/07
- if(strpos($SK, '_') === 0) {
- continue;
- }
- if($cur_index > $projects_per_page) {
- //print_r($reordered_ranking_list);
- echo "<hr/>after:<hr/>";
- print_r(array_slice($reordered_ranking_list,0,10));
- return $reordered_ranking_list;
- }
- $cur_msd = $MSD;
- // echo "MSD[$cur_index]($SK):$cur_msd -- MSD[".($cur_index-1)."]($prev_sk):$prev_msd<br/>";
- if($cur_msd < $prev_msd) {
- //echo "FirstPageNotRandomModifier:: Item $SK is out of order on the first page. Moving it. <br/>";
- // previous item was out of order. Move it.
- $random_new_index = rand($projects_per_page,$projects_per_page + $shuffle_range);
- // Get the current position in the reordered ranking list
- $current_position = $this->index_of_key($reordered_ranking_list,$prev_sk);
- echo "FirstPageNotRandomModifier:: Moving Item $prev_sk from position ".($current_position)." in the reordered list to position $random_new_index. <br/>";
- echo "<hr/>before array_move:<hr/>";
- print_r(array_slice($reordered_ranking_list,0,10));
- $reordered_ranking_list = $this->array_move($reordered_ranking_list,($current_position+1),$prev_sk,$random_new_index);
- echo "<hr/>after array_move:<hr/>";
- print_r(array_slice($reordered_ranking_list,0,10));
- // print_r($reordered_ranking_list);
- }
- $prev_msd = $cur_msd;
- $prev_sk = $SK;
- $cur_index++;
- }
- return $reordered_ranking_list;
- }
- */
-
- function process($ranking_list,$individual_profile,$state_array,$configuration,$sk_summary)
- {
- $result_array = array();
- // echo "<hr/>In FirstPageNotRandomModifier<hr/>";
- $shuffle_range = $configuration['ProjectShuffleRange'];
- $first_page_projects_not_random = $configuration['FirstPageProjectsNotRandom'];
- $projects_per_page = $configuration['MaxProjectsPerPage'];
- // First, check if FirstPageNotRandom flag is set. If not, do nothing.
- if(!$first_page_projects_not_random) {
- return $ranking_list;
- }
-
- // echo "<hr/>before:<hr/>";
- // print_r(array_slice($ranking_list,0,10));
- // Copy the ranking list
-
- $copy_ranking_list = $ranking_list;
-
- // Sort the copy
- $copy_ranking_list = array_slice($copy_ranking_list,0,$projects_per_page);
- // echo "<hr/>FirstPageNotRandomNotifier::unsorted top-ten:<br/>";
- // $this->print_r_html(array_slice($copy_ranking_list,0,10));
-
- asort($copy_ranking_list);
-
- // For the top $projects_per_page items:
- // Move the SK into the corresponding slot in the reordered ranking.
-
- $copy_ranking_list = array_slice($copy_ranking_list,0,$projects_per_page);
- // echo "<hr/>FirstPageNotRandomNotifier::sorted top-ten:<br/>";
- // $this->print_r_html($copy_ranking_list);
-
-
- //$cur_index = 1;
- foreach($copy_ranking_list as $SK => $MSD) {
- // Find the original location of this SK in the ranking list.
-
- $current_position = $this->index_of_key($ranking_list,$SK);
- $location_in_sorted_list = $this->index_of_key($copy_ranking_list,$SK);
- $ranking_list = $this->array_move($ranking_list,($current_position),$SK,$location_in_sorted_list);
- // $this->print_r_html(array_slice($ranking_list,0,10));
- // $cur_index++;
- }
- // echo "<hr/>after:<hr/>";
- // $this->print_r_html(array_slice($ranking_list,0,10));
-
- return $ranking_list;
- }
-
- function print_r_html($data,$return_data=false)
- {
- $data = print_r($data,true);
- $data = str_replace( " "," ", $data);
- $data = str_replace( "\r\n","<br>\r\n", $data);
- $data = str_replace( "\r","<br>\r", $data);
- $data = str_replace( "\n","<br>\n", $data);
- if (!$return_data)
- echo $data;
- else
- return $data;
- }
- }
- ?>