Skip to content

Commit

Permalink
Fantasy Tracker v1.1 (#23)
Browse files Browse the repository at this point in the history
* Add token initialization

* #5: Refresh token causes program to return "Error" when it successfully refreshes (#12)

* Change naming scheme of variables and files -- add raspberry pi driver file (#14)

* Branch for raspberry pi automated data collection with cron

* Duplicate user agent

* Fix relative paths to be absolute so codebase can be run from any directory in filesystem (#15)

* Fix relative paths to be absolute so codebase can be used from any directory (#13)

* Fix constant directory names

* Rewrite algorithm to include all averages and generate a csv of suggestions (#16)

* (#3) Add mailer class to be able to email the user a generated report (#20)

* (#3) Add mailer class to be able to send a recipent an email of the report

* (#17) Add a script that can backup any directory to be run from cron (#21)

* Add update repo script to keep pi up to date with dev (#22)
  • Loading branch information
dkutin authored Mar 15, 2020
1 parent cb5be90 commit bc2201b
Show file tree
Hide file tree
Showing 16 changed files with 226 additions and 100 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ tmp/
tmp/auth/
tmp/data/
tmp/data/players/
test.php
constants.php
*.tar.gz
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/PHPMailer"]
path = lib/PHPMailer
url = https://github.com/PHPMailer/PHPMailer.git
133 changes: 78 additions & 55 deletions Analytics.php
Original file line number Diff line number Diff line change
@@ -1,67 +1,87 @@
<?php

include_once('constants.php');
include_once('ServiceCall.php');
include_once('helper.php');
include_once (__DIR__ . '/constants.php');
include_once (__DIR__ . '/ServiceCall.php');
include_once (__DIR__ . '/helper.php');

/**
* Class Analytics
*/
class Analytics
{
private $player_stats;
/**
* Analytics constructor.
*/
function __construct()
{
if (file_exists('tmp/data/' . LEAGUE_KEY . '_free_agents.json') && file_exists('tmp/data/' . LEAGUE_KEY . '_my_team.json')) {
// TODO: Remove team and free agent files from temp/ every time a new Analytics object is made ...
//deleteAllFromFolder('tmp/data/*.json');
$roster = getContents('tmp/data/' . LEAGUE_KEY . '_my_team.json');
$free_agents = getContents('tmp/data/' . LEAGUE_KEY . '_free_agents.json');
writeToFile(json_encode($roster['team']['roster']['players']), 'bin/team_' . TEAM_ID . '_roster.json');
writeToFile(json_encode($free_agents['league']['players']), 'bin/free_agents.json');
if (file_exists(TMP_DATA_DIR . LEAGUE_KEY . '_free_agents.json') && file_exists(TMP_DATA_DIR . LEAGUE_KEY . '_my_team.json')) {
$roster = getContents(TMP_DATA_DIR . LEAGUE_KEY . '_my_team.json');
$free_agents = getContents(TMP_DATA_DIR . LEAGUE_KEY . '_free_agents.json');
writeToFile(json_encode($roster['team']['roster']['players']), BIN_DIR . 'team_' . TEAM_ID . '_roster.json');
writeToFile(json_encode($free_agents['league']['players']), BIN_DIR . 'free_agents.json');
$this->player_stats = [
'Roster' => $this->createPlayerStats('Roster'),
'FA' => $this->createPlayerStats('FA'),
];
}
return $this;
}

/**
* @return string
* @return array
*/
function generateReport()
function generateSuggestion()
{
$averages['FA'] = $this->createPlayerAverages('FA');
$averages['Roster'] = $this->createPlayerAverages('Roster');
$player_weight = [];
$data = "";
$player_delta = [
'FA' => [
ONE_WEEK_AVG => $this->getPlayerDelta('FA', ONE_WEEK_AVG),
TWO_WEEK_AVG => $this->getPlayerDelta('FA', TWO_WEEK_AVG),
ONE_MO_AVG => $this->getPlayerDelta('FA', ONE_MO_AVG),
],
'Roster' => [
ONE_WEEK_AVG => $this->getPlayerDelta('Roster', ONE_WEEK_AVG),
TWO_WEEK_AVG => $this->getPlayerDelta('Roster', TWO_WEEK_AVG),
ONE_MO_AVG => $this->getPlayerDelta('Roster', ONE_MO_AVG),
],
];

$player_score = [];

foreach ($averages['Roster'] as $rplayer => $rplayer_avg) {
$free_agents = -1;
foreach ($averages['FA'] as $fplayer => $fplayer_avg) {
$free_agents++;
if ($fplayer_avg >= $rplayer_avg) {
$player_weight[$fplayer] = isset($player_weight[$fplayer]) ? $player_weight[$fplayer] + 1 : 1 - $free_agents;
$player_weight[$rplayer] = isset($player_weight[$rplayer]) ? $player_weight[$rplayer] - 1 : -1;
} else {
break;
// TODO: Maybe use central limit theorem here to better rank players
foreach ($player_delta as $type => $data) {
foreach ($data as $stat => $week_values) {
foreach ($week_values as $week => $player_averages) {
foreach ($player_averages as $player => $delta) {
if (empty($player_score[$type][$player])) {
$player_score[$type][$player] = $this->player_stats[$type][$week][$player];
}
switch ($stat) {
case ONE_WEEK_AVG:
$player_score[$type][$player] += 1 * $delta;
break;
case TWO_WEEK_AVG:
$player_score[$type][$player] += 1.5 * $delta;
break;
case ONE_MO_AVG:
$player_score[$type][$player] += 3 * $delta;
break;
}
}
}
}
}

foreach ($player_weight as $player => $weight) {
if (array_key_exists($player, $averages['Roster'])) {
print "You should consider dropping: ${player}. (${weight})" . PHP_EOL;
$data .= "You should consider dropping: ${player}. (${weight})" . PHP_EOL;
} else {
if ($weight > 0) {
print "You should consider picking up: ${player}. (${weight})" . PHP_EOL;
$data .= "You should consider picking up: ${player}. (${weight})" . PHP_EOL;
$player_suggestions = [];
foreach ($player_score['FA'] as $fa_player => $fa_score) {
foreach ($player_score['Roster'] as $r_player => $r_score) {
if ($fa_score > $r_score) {
$player_suggestions[$r_player][$fa_player] = $fa_score - $r_score;
}
}
}
writeToFile(json_encode($player_weight), 'bin/analysis_' . time() . '.json');
return $data;

return $player_suggestions;
}

/**
Expand All @@ -70,22 +90,22 @@ function generateReport()
* @return array
*/
function createPlayerStats($type, $player = '') {
global $scored_stats;
// If we've specified a player, get their weekly stats (if available)
if (empty($player)) {
$files = glob("tmp/data/players/${type}/*.json", GLOB_BRACE);
$files = glob(TMP_DATA_PLAYERS_DIR . "${type}/*.json", GLOB_BRACE);
} else {
$files = glob("tmp/data/players/${type}/player_${player}_week_*.json", GLOB_BRACE);
if (count($files) < 2) {
$files = glob(TMP_DATA_PLAYERS_DIR . "${type}/player_${player}_week_*.json", GLOB_BRACE);
if (count($files) == 0) {
print "Not enough data given for player ${player}!";
return FALSE;
return [];
}
}

$players = [];
foreach ($files as $file) {
$week = substr($file, -7, 2);
$data = getContents($file);
global $scored_stats;
$gp = $data['player']['player_stats']['stats']['stat']['0']['value'];
$averages = [];
if ($gp > 0) {
Expand All @@ -101,31 +121,34 @@ function createPlayerStats($type, $player = '') {
return $players;
}

function getSevenDayAverage($type, $player = '') {
$data = $this->createPlayerStats($type, $player);
function getPlayerDelta($type, $stat, $player = '') {
if (empty($this->player_stats[$type])) {
$this->player_stats[$type] = $this->createPlayerStats($type, $player);
}
$data = $this->player_stats[$type];
$stats = [];
foreach ($data as $week => $players) {
foreach ($players as $player => $value) {
if (!empty($data[$week-1][$player])) {
$stats[$week][$player] = $data[$week][$player] - $data[$week-1][$player];
if (!empty($data[$week-$stat][$player])) {
$stats[$week][$player] = $data[$week][$player] - $data[$week-$stat][$player];
}
}
}
return $stats;
}

// TODO: Maybe add another parameter to merge 2 functions ...
function getFourteenDayAverage($type, $player = '') {
$data = $this->createPlayerStats($type, $player);
$stats = [];
foreach ($data as $week => $players) {
foreach ($players as $player => $value) {
if (!empty($data[$week-2][$player])) {
$stats[$week][$player] = $data[$week][$player] - $data[$week-2][$player];
}
function generateReport() {
$suggestions = $this->generateSuggestion();
$data = '';
foreach ($suggestions as $r_player => $fa_players) {
arsort($fa_players);
$data .= "<div><p><b>$r_player can be replaced by: </b><br/>";
foreach ($fa_players as $fa_player => $diff) {
$data .= "<div>&emsp;&emsp;$fa_player: $diff </div><br/>";
}
$data .= "</p></div></br>";
}
return $stats;
writeToFile($data, BIN_DIR . 'analysis.csv');
return $data;
}

}
56 changes: 26 additions & 30 deletions FantasyAPI.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

include_once('constants.php');
include_once('helper.php');
include_once(__DIR__ . '/constants.php');
include_once(__DIR__ . '/helper.php');

/**
* Class FantasyAPI
Expand All @@ -23,7 +23,7 @@ class FantasyAPI
*/
function __construct()
{
$this->auth_json_file = 'tmp/auth/auth_credentials_' . CONSUMER_KEY . '.json';
$this->auth_json_file = TMP_AUTH_DIR . 'auth_credentials_' . CONSUMER_KEY . '.json';
if (file_exists($this->auth_json_file)) {
$this->credentials = json_decode(file_get_contents($this->auth_json_file), TRUE);
} else {
Expand All @@ -40,50 +40,50 @@ function __construct()
*/
function makeAPIRequest($url)
{
$curl = curl_init();
curl_setopt_array($curl, array(
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $url,
CURLOPT_HTTPHEADER => array('authorization: Bearer ' . $this->credentials['access_token'],
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language: en-US,en;q=0.5',
'Cache-Control: no-cache',
'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:28.0) Gecko/20100101 Firefox/28.0'),
),
));
$resp = curl_exec($curl);
if (strpos($resp, "token_expired")) {
$resp = curl_exec($ch);
curl_close($ch);
if (strpos($resp, "token_expired") !== FALSE) {
if ($this->refreshToken()) {
$this->makeAPIRequest($url);
return $this->makeAPIRequest($url);
} else {
print 'Trouble Getting Refresh Token...';

}
} else if (strpos($resp, "error")) {
} else if (strpos($resp, "error") !== FALSE) {
print "Error in making the API Request";
} else if (strpos($resp, "Request denied") !== FALSE) {
print "Request denied: " . $url;
} else {
$xml = simplexml_load_string($resp, "SimpleXMLElement", LIBXML_NOCDATA);
$json = json_encode($xml);
curl_close($curl);
return json_decode($json, TRUE);
}
curl_close($curl);
return FALSE;
}

/**
* @return bool|mixed
* @return bool
*/
function refreshToken()
{
// If our auth file doesn't exist, make one
if (!file_exists($this->auth_json_file)) {
return $this->initializeToken();
$this->initializeToken();
}

// If our token has not expired yet, return the existing auth
if ($this->credentials['expiry_time'] > time()) {
return json_decode($this->auth_json_file, TRUE);
return TRUE;
}

$ch = curl_init();
Expand All @@ -99,25 +99,21 @@ function refreshToken()
CURLOPT_HTTPHEADER => array(
'Authorization: Basic ' . base64_encode(CONSUMER_KEY . ":" . CONSUMER_SECRET),
'Content-Type: application/x-www-form-urlencoded',
'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'),
),
CURLOPT_POSTFIELDS => http_build_query($post_values)
));
$resp = curl_exec($ch);
if (strpos($resp, "error") || empty($resp)) {
curl_close($ch);
curl_close($ch);
if (strpos($resp, "error") !== FALSE || empty($resp)) {
print "Error getting Refresh Token";
return FALSE;
}
curl_close($ch);
writeToFile($resp, $this->auth_json_file);
$this->credentials = json_decode($resp, TRUE);
$this->credentials['expiry_time'] = time() + 3600;
print_r($resp);
return json_decode($resp, TRUE);
return TRUE;
}

/**
* @return bool|mixed
*/
function initializeToken()
{
$auth_code = readline('Go to: https://api.login.yahoo.com/oauth2/request_auth?client_id=' . CONSUMER_KEY . '&redirect_uri=oob&response_type=code&language=en-us and copy the code: ');
Expand All @@ -136,19 +132,19 @@ function initializeToken()
CURLOPT_HTTPHEADER => array(
'Authorization: Basic ' . base64_encode(CONSUMER_KEY . ":" . CONSUMER_SECRET),
'Content-Type: application/x-www-form-urlencoded',
'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'),
),
CURLOPT_POSTFIELDS => http_build_query($post_values)
));
$resp = curl_exec($ch);
if (empty($resp) || strpos($resp, 'error')) {
curl_close($ch);
curl_close($ch);
if (strpos($resp, 'error') !== FALSE || empty($resp)) {
print 'Error Initializing Token';
return FALSE;
}
curl_close($ch);
writeToFile($resp, $this->auth_json_file);
$this->credentials = json_decode($resp, TRUE);
$this->credentials['expiry_time'] = time() + 3600;
return json_decode($resp, TRUE);
return TRUE;
}
}

Loading

0 comments on commit bc2201b

Please sign in to comment.