Assigning users to a proper league based on performance

80 views Asked by At

I'm getting hung up on the logic needed in php to assign my users to a "league" based on their performance. Basically, once a month I will have a cron job run which will fetch a list of my users and their data averaged like so

SELECT user_id, AVG(activity_value) AS `average` 
FROM usermeta 
WHERE activity_date >= DATE_ADD(CURDATE(), INTERVAL -7 DAY) 
GROUP BY user_id 
ORDER BY average DESC

This returns a list of my users and their average which will determine their placement in the leagues (best user at the top).

I have a leagues table which simply stores the league id and its name like so

league_id   league_name  
1           Gold
2           Silver
3           Bronze

Then I have a relation table that ties the user and league tables together which (should) look like so once I get the logic pinned down

user_id   league_id  
2         1
3         1
1         2
6         2
5         3
4         3

etc...

Where i'm getting hung up is the logic needed to insert the user to the correct league. There is the possibility that there can be more leagues too. So for instance, if there are 36 users and 4 leagues, each league should have 9 users in it.

My cron job so far is running this on execution (just to give you an idea of my progress)

protected function execute() {
    $leagues = $this->app['leagues'];
    $userAvgs = $leagues->getAllUserAvgs(); // holds results from query shown above

    foreach($userAvgs as $user) {
        $league = new League(array(
            'user_id'  => $user['user_id'],
            'league_id' => THE_CORRECT_LEAGUE_HERE
        ));
        $leagues->save($league);
    }
}

On each loop of the foreach it should store to the relation table the user and their proper league, but i'm just lost on how to run through it and determine the right league I need to insert for the users league_id. I guess I need to have the correct user number per league which could be along the lines of

$usersPerLeague = count($userAvgs)/count($leagues->getAllLeagues()); 
// getAllLeagues method returns a list of my leagues obviously

If anyone can guide me in the right direction on this process, i'd much appreciate it.

3

There are 3 answers

0
Erick Briseño On BEST ANSWER

This is an alternative solution

$all_leagues= $leagues->getAllLeagues();
$all_users = $users->getAllUserAvgs();
$num_leagues = count($all_leagues);
$num_users = count($all_users);
$usersPerLeague = $num_users / $num_leagues;


$a=0;
$b=0;
foreach($all_users as $user) {
    $a++;
    if($a % $usersPerLeague == 0){
       $b++;
    }
    $league = new League(array(
        'user_id'  => $user['user_id'],
        'league_id' => $all_leagues[$b]
    ));
    $leagues->save($league);
}

in the sql order you can add the user_id order

ORDER BY average,user_id DESC
0
dagfr On

Your user are ordered by average, so just perform

$usersPerLeague = count($userAvgs)/count($leagues->getAllLeagues()); 
$current_league =1;
$i=0;
foreach($userAvgs as $user) {
    $i++:
    if($i%9==0) $current_league++;
    $league = new League(array(
        'user_id'  => $user['user_id'],
        'league_id' => $current_league
    ));
    $leagues->save($league);
}

Alternative solution :

Add a rank to your request like this :

 $sql = SELECT user_id, AVG(activity_value) AS `average`, @rownum := @rownum + 1 AS rank
 FROM usermeta ,(SELECT @rownum := 0) r
 WHERE activity_date >= DATE_ADD(CURDATE(), INTERVAL -7 DAY) 
 GROUP BY user_id 
 ORDER BY average DESC

if rank < usersPerLeague => league 1 if rank < 2*usersPerLeague => league 2 etc.

0
Jompper On
$leagues = array_chunk($players, $playerCount/$leagueCount);

// loop thru leagues are from 0 to leaguecount - 1
foreach($leagues as $leagueNum => $players){
    foreach($players as $player){
        // use league + 1 to make leagues from 1 to league count
        $league = new League(array(
            'user_id'  => $player['user_id'],
            'league_id' => $leagueNum +1
        ));
        $leagues->save($league);
    }
}