552 lines
13 KiB
PHP
552 lines
13 KiB
PHP
<?php
|
|
|
|
class ExtendedGitGraph {
|
|
|
|
const FILE_RAW_DATA = 'protected/data/git_graph_raw.dat';
|
|
const FILE_FINISHED_DATA = 'protected/data/git_graph_data.dat';
|
|
|
|
const API_AUTHORIZE = 'https://github.com/login/oauth/authorize?client_id=%s';
|
|
const API_TOKEN = 'https://github.com/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s';
|
|
|
|
const API_RATELIMIT = 'https://api.github.com/rate_limit';
|
|
const API_REPOSITORIESLIST = 'https://api.github.com/users/%s/repos?page=%d&per_page=100';
|
|
const API_SINGLEREPOSITORY = 'https://api.github.com/repos/%s';
|
|
|
|
const PROGRESS_SESSION_COOKIE = 'ajax_progress_egh_refresh';
|
|
|
|
const COMMITCOUNT_COLOR_UPPERLIMIT = 16;
|
|
|
|
private $username;
|
|
private $token;
|
|
private $tokenHeader;
|
|
|
|
private $repositories;
|
|
private $commits;
|
|
|
|
private $sec_username = [];
|
|
private $sec_repo = [];
|
|
|
|
private $commitmap = array();
|
|
private $startdate = null;
|
|
private $enddate = null;
|
|
|
|
public function __construct($usr_name) {
|
|
$this->username = $usr_name;
|
|
}
|
|
|
|
public function authenticate($auth_key, $client_id, $client_secret) {
|
|
$url = sprintf(self::API_TOKEN, $client_id, $client_secret, $auth_key);
|
|
$result = file_get_contents($url);
|
|
|
|
$result = str_replace('access_token=', '', $result);
|
|
$result = str_replace('&scope=&token_type=bearer', '', $result);
|
|
|
|
setToken($result);
|
|
}
|
|
|
|
public function setToken($token) {
|
|
$this->token = $token;
|
|
$this->tokenHeader = 'access_token=' . $token . '&token_type=bearer';
|
|
}
|
|
|
|
public function collect() {
|
|
set_time_limit(300); // 5min
|
|
|
|
$this->output_flushed_clear();
|
|
$this->output_flushed("Start progress");
|
|
|
|
$this->listRepositories();
|
|
$this->listAllCommits();
|
|
|
|
$this->save();
|
|
|
|
$this->output_flushed("Remaining Requests: " . $this->getRemainingRequests());
|
|
$this->output_flushed("Finished progress");
|
|
}
|
|
|
|
private function listRepositories()
|
|
{
|
|
$this->repositories = array();
|
|
|
|
$this->listRepositoriesForUser($this->username);
|
|
|
|
foreach ($this->sec_username as $un)
|
|
{
|
|
$this->listRepositoriesForUser($un);
|
|
}
|
|
|
|
foreach($this->sec_repo as $srep)
|
|
{
|
|
$this->listSingleRepo($srep);
|
|
}
|
|
}
|
|
|
|
private function listRepositoriesForUser($usr) {
|
|
$page = 1;
|
|
$url = sprintf(self::API_REPOSITORIESLIST . '&' . $this->tokenHeader, $usr, $page);
|
|
|
|
$result = $this->getJSON($url);
|
|
|
|
while (! empty($result)) {
|
|
foreach ($result as $result_repo) {
|
|
$this->repositories[] = $this->parseRepoJSON($result_repo);
|
|
|
|
$this->output_flushed("Found Repo: " . $result_repo->{'full_name'});
|
|
}
|
|
|
|
//##########
|
|
|
|
$url = sprintf(self::API_REPOSITORIESLIST . '&' . $this->tokenHeader, $usr, ++$page);
|
|
|
|
$result = $this->getJSON($url);
|
|
}
|
|
}
|
|
|
|
private function listSingleRepo($repo)
|
|
{
|
|
$url = sprintf(self::API_SINGLEREPOSITORY . '?' . $this->tokenHeader, $repo);
|
|
|
|
$result_repo = $this->getJSON($url);
|
|
|
|
if (! empty($result_repo))
|
|
{
|
|
$this->repositories[] = $this->parseRepoJSON($result_repo);
|
|
|
|
$this->output_flushed("Found Repo: " . $result_repo->{'full_name'});
|
|
}
|
|
}
|
|
|
|
private function getJSON($url) {
|
|
$options = array('http' => array('user_agent'=> $_SERVER['HTTP_USER_AGENT']));
|
|
$context = stream_context_create($options);
|
|
|
|
$response = @file_get_contents($url, false, $context);
|
|
|
|
if ($response === false)
|
|
{
|
|
$this->output_flushed("Error recieving json: '" . $url . "'");
|
|
return array();
|
|
}
|
|
|
|
return json_decode($response);
|
|
}
|
|
|
|
private function getRemainingRequests() {
|
|
$json = $this->getJSON(self::API_RATELIMIT . '?' . $this->tokenHeader);
|
|
|
|
return $json->{'resources'}->{'core'}->{'remaining'};
|
|
}
|
|
|
|
private function listAllCommits() {
|
|
$this->commits = array();
|
|
|
|
foreach($this->repositories as $repo) {
|
|
$this->listCommits($repo);
|
|
}
|
|
}
|
|
|
|
private function listCommits($repo) {
|
|
$page = 1;
|
|
$url = $repo['commits_url'] . '?per_page=100&page=' . $page . '&author=' .$this->username . '&' .$this->tokenHeader;
|
|
|
|
$result = $this->getJSON($url);
|
|
|
|
$commit_list = array();
|
|
|
|
while (! empty($result)) {
|
|
foreach ($result as $result_commit) {
|
|
$commit_list[] = $this->parseCommitJSON($repo, $result_commit);
|
|
}
|
|
|
|
$this->output_flushed("Found " . count($result) . " Commits from " . $repo['full_name']);
|
|
|
|
//##########
|
|
|
|
$url = $repo['commits_url'] . '?per_page=100&page=' . ++$page . '&author=' .$this->username . '&' .$this->tokenHeader;
|
|
|
|
$result = $this->getJSON($url);
|
|
}
|
|
|
|
$this->commits = array_merge($this->commits, $commit_list);
|
|
}
|
|
|
|
private function parseRepoJSON($json) {
|
|
return
|
|
[
|
|
'id' => $json->{'id'},
|
|
'name' => $json->{'name'},
|
|
'full_name' => $json->{'full_name'},
|
|
'owner' => $json->{'owner'}->{'login'},
|
|
'owner_id' => $json->{'owner'}->{'id'},
|
|
'owner_avatar-url' => $json->{'owner'}->{'avatar_url'},
|
|
'owner_gravatar-id' => $json->{'owner'}->{'gravatar_id'},
|
|
'url' => $json->{'html_url'},
|
|
'language' => $json->{'language'},
|
|
'url' => $json->{'html_url'},
|
|
'creation' => DateTime::createFromFormat(DateTime::ISO8601, $json->{'created_at'}),
|
|
'size' => $json->{'size'},
|
|
'default_branch' => $json->{'default_branch'},
|
|
'commits_url' => str_replace('{/sha}', '', $json->{'commits_url'}),
|
|
];
|
|
}
|
|
|
|
private function parseCommitJSON($repo, $json) {
|
|
return
|
|
[
|
|
'sha' => $json->{'sha'},
|
|
'author_name' => $json->{'commit'}->{'author'}->{'name'},
|
|
'author_mail' => $json->{'commit'}->{'author'}->{'email'},
|
|
'author_login' => $json->{'author'}->{'login'},
|
|
'author_id' => $json->{'author'}->{'id'},
|
|
'sha' => $json->{'sha'},
|
|
'message' => $json->{'commit'}->{'message'},
|
|
'repository' => $repo,
|
|
'date' => DateTime::createFromFormat(DateTime::ISO8601, $json->{'commit'}->{'author'}->{'date'}),
|
|
];
|
|
}
|
|
|
|
private function save() {
|
|
$this->output_flushed("Start saving data");
|
|
|
|
$save = serialize(
|
|
[
|
|
'repositories' => $this->repositories,
|
|
'commits' => $this->commits,
|
|
]);
|
|
|
|
file_put_contents(self::FILE_RAW_DATA, $save);
|
|
|
|
$this->output_flushed('Finished saving data');
|
|
}
|
|
|
|
public function output_flushed_clear()
|
|
{
|
|
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
|
|
|
|
$_SESSION[self::PROGRESS_SESSION_COOKIE] = '';
|
|
session_commit();
|
|
}
|
|
|
|
public function output_flushed($txt)
|
|
{
|
|
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
|
|
|
|
$_SESSION[self::PROGRESS_SESSION_COOKIE] .= '[' . date('H:i.s') . '] ' . $txt . "\r\n";
|
|
session_commit();
|
|
}
|
|
|
|
public function loadData() {
|
|
$data = unserialize(file_get_contents(self::FILE_RAW_DATA));
|
|
|
|
$this->repositories = $data['repositories'];
|
|
$this->commits = $data['commits'];
|
|
}
|
|
|
|
public function generate($year) {
|
|
$ymap = $this->generateYearMap($year); // unused on purpose (template.php needs it)
|
|
|
|
$ymapmax = min($this->getMaxCommitCount(), self::COMMITCOUNT_COLOR_UPPERLIMIT); // unused on purpose (template.php needs it)
|
|
|
|
ob_start();
|
|
include('template.php');
|
|
$returned = ob_get_contents();
|
|
ob_end_clean();
|
|
|
|
return $returned;
|
|
}
|
|
|
|
public function generateAndSave() {
|
|
$result = '';
|
|
|
|
foreach($this->getYears() as $year) {
|
|
$result.= $this->generate($year);
|
|
$result.= '<br />';
|
|
}
|
|
|
|
$this->generateCommitMap();
|
|
|
|
file_put_contents(self::FILE_FINISHED_DATA,
|
|
serialize(
|
|
[
|
|
'creation' => new DateTime(),
|
|
'total' => count($this->commits),
|
|
'repos' => count($this->repositories),
|
|
'streak' => $this->getLongestStreak(),
|
|
'streak_start' => $this->getLongestStreakStart(),
|
|
'streak_end' => $this->getLongestStreakEnd(),
|
|
'max_commits' => $this->getMaxCommits(),
|
|
'max_commits_date' => $this->getMaxCommitsDate(),
|
|
'avg_commits' => $this->getAvgCommits(),
|
|
'start' => $this->startdate,
|
|
'end' => $this->enddate,
|
|
'content' => $result,
|
|
]));
|
|
|
|
return $result;
|
|
}
|
|
|
|
private function generateCommitMap() {
|
|
$this->commitmap = array();
|
|
|
|
$this->startdate = $this->getStartDate();
|
|
$this->enddate = $this->getEndDate();
|
|
|
|
$date = clone $this->startdate;
|
|
while($date < $this->enddate) {
|
|
$this->commitmap[$date->format('Y-m-d')] = 0;
|
|
|
|
$date = $date->modify("+1 day");
|
|
}
|
|
|
|
foreach ($this->commits as $commit) {
|
|
if(array_key_exists($commit['date']->format('Y-m-d'), $this->commitmap))
|
|
$this->commitmap[$commit['date']->format('Y-m-d')]++;
|
|
}
|
|
}
|
|
|
|
private function getStartDate() {
|
|
$date = $this->commits[0]['date'];
|
|
|
|
foreach($this->commits as $commit) {
|
|
if ($commit['date'] < $date) {
|
|
$date = clone $commit['date'];
|
|
}
|
|
}
|
|
|
|
return new DateTime($date->format('Y-m-d'));
|
|
}
|
|
|
|
private function getEndDate() {
|
|
$date = $this->commits[0]['date'];
|
|
|
|
foreach($this->commits as $commit) {
|
|
if ($commit['date'] > $date) {
|
|
$date = clone $commit['date'];
|
|
}
|
|
}
|
|
|
|
return new DateTime($date->format('Y-m-d'));
|
|
}
|
|
|
|
private function getCommitsForDate($d) {
|
|
$v = $d->format('Y-m-d');
|
|
if (array_key_exists($v, $this->commitmap))
|
|
return $this->commitmap[$d->format('Y-m-d')];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private function getLongestStreak() {
|
|
/* @var $curr DateTime */
|
|
/* @var $end DateTime */
|
|
$curr = clone $this->startdate;
|
|
$end = clone $this->enddate;
|
|
|
|
$streak_curr = 0;
|
|
$streak_max = 0;
|
|
|
|
while ($curr <= $end) {
|
|
if ($this->getCommitsForDate($curr) > 0) {
|
|
$streak_curr++;
|
|
} else {
|
|
$streak_max = max($streak_max, $streak_curr);
|
|
$streak_curr = 0;
|
|
}
|
|
|
|
$curr = $curr->modify('+1 day');
|
|
}
|
|
|
|
$streak_max = max($streak_max, $streak_curr);
|
|
|
|
return $streak_max;
|
|
}
|
|
|
|
private function getLongestStreakStart() {
|
|
/* @var $curr DateTime */
|
|
/* @var $end DateTime */
|
|
$curr = clone $this->startdate;
|
|
$end = clone $this->enddate;
|
|
|
|
$streak_curr_start = clone $curr;
|
|
$streak_max_start = null;
|
|
|
|
$streak_curr = 0;
|
|
$streak_max = 0;
|
|
|
|
while ($curr <= $end) {
|
|
if ($this->getCommitsForDate($curr) > 0) {
|
|
$streak_curr++;
|
|
} else {
|
|
if ($streak_curr > $streak_max) {
|
|
$streak_max = $streak_curr;
|
|
$streak_max_start = clone $streak_curr_start;
|
|
}
|
|
|
|
$streak_curr = 0;
|
|
$streak_curr_start = clone $curr;
|
|
}
|
|
|
|
$curr = $curr->modify('+1 day');
|
|
}
|
|
|
|
if ($streak_curr > $streak_max) {
|
|
$streak_max_start = clone $streak_curr_start;
|
|
}
|
|
|
|
return $streak_max_start;
|
|
}
|
|
|
|
private function getLongestStreakEnd() {
|
|
/* @var $curr DateTime */
|
|
/* @var $end DateTime */
|
|
$curr = clone $this->startdate;
|
|
$end = clone $this->enddate;
|
|
|
|
$streak_max_end = null;
|
|
|
|
$streak_curr = 0;
|
|
$streak_max = 0;
|
|
|
|
while ($curr <= $end) {
|
|
if ($this->getCommitsForDate($curr) > 0) {
|
|
$streak_curr++;
|
|
} else {
|
|
if ($streak_curr > $streak_max) {
|
|
$streak_max = $streak_curr;
|
|
$streak_max_end = clone $curr;
|
|
}
|
|
|
|
$streak_curr = 0;
|
|
}
|
|
|
|
$curr = $curr->modify('+1 day');
|
|
}
|
|
|
|
if ($streak_curr > $streak_max) {
|
|
$streak_max_end = clone $curr;
|
|
}
|
|
|
|
return $streak_max_end;
|
|
}
|
|
|
|
private function getMaxCommits() {
|
|
/* @var $curr DateTime */
|
|
/* @var $end DateTime */
|
|
$curr = clone $this->startdate;
|
|
$end = clone $this->enddate;
|
|
|
|
$max = 0;
|
|
|
|
while ($curr <= $end) {
|
|
$max = max($max, $this->getCommitsForDate($curr));
|
|
|
|
$curr = $curr->modify('+1 day');
|
|
}
|
|
|
|
return $max;
|
|
}
|
|
|
|
private function getMaxCommitsDate() {
|
|
/* @var $curr DateTime */
|
|
/* @var $end DateTime */
|
|
$curr = clone $this->startdate;
|
|
$end = clone $this->enddate;
|
|
|
|
$max = 0;
|
|
$max_date = null;
|
|
|
|
while ($curr <= $end) {
|
|
$c = $this->getCommitsForDate($curr);
|
|
if ($c >= $max) {
|
|
$max = $c;
|
|
$max_date = clone $curr;
|
|
}
|
|
$max = max($max, $this->getCommitsForDate($curr));
|
|
|
|
$curr = $curr->modify('+1 day');
|
|
}
|
|
|
|
return $max_date;
|
|
}
|
|
|
|
private function getAvgCommits() {
|
|
/* @var $curr DateTime */
|
|
/* @var $end DateTime */
|
|
$curr = clone $this->startdate;
|
|
$end = clone $this->enddate;
|
|
|
|
$max = array();
|
|
|
|
while ($curr <= $end) {
|
|
$max[] = $this->getCommitsForDate($curr);
|
|
|
|
$curr = $curr->modify('+1 day');
|
|
}
|
|
|
|
$sum = array_sum($max);
|
|
$count = count($max);
|
|
|
|
return $sum / $count;
|
|
}
|
|
|
|
public function loadFinishedContent() {
|
|
$data = unserialize(file_get_contents(self::FILE_FINISHED_DATA));
|
|
return $data['content'];
|
|
}
|
|
|
|
public function loadFinishedData() {
|
|
$data = unserialize(file_get_contents(self::FILE_FINISHED_DATA));
|
|
return $data;
|
|
}
|
|
|
|
private function getMaxCommitCount() {
|
|
$max = 0;
|
|
|
|
foreach($this->getYears() as $year) {
|
|
$max = max($max, max($this->generateYearMap($year)));
|
|
}
|
|
|
|
return $max;
|
|
}
|
|
|
|
private function generateYearMap($year) {
|
|
$ymap = array();
|
|
|
|
$date = new DateTime($year . '-01-01');
|
|
while($date->format('Y') == $year) {
|
|
$ymap[$date->format('Y-m-d')] = 0;
|
|
|
|
$date = $date->modify("+1 day");
|
|
}
|
|
|
|
foreach ($this->commits as $commit) {
|
|
if(array_key_exists($commit['date']->format('Y-m-d'), $ymap))
|
|
$ymap[$commit['date']->format('Y-m-d')]++;
|
|
}
|
|
|
|
return $ymap;
|
|
}
|
|
|
|
public function getYears() {
|
|
$years = array();
|
|
|
|
foreach ($this->commits as $commit) {
|
|
if(! in_array($commit['date']->format('Y'), $years))
|
|
$years[] = $commit['date']->format('Y');
|
|
}
|
|
|
|
asort($years);
|
|
|
|
return $years;
|
|
}
|
|
|
|
public function addSecondaryUsername($sun)
|
|
{
|
|
$this->sec_username[] = $sun;
|
|
}
|
|
|
|
public function addSecondaryRepository($sre)
|
|
{
|
|
$this->sec_repo[] = $sre;
|
|
}
|
|
} |