roadmap v1

This commit is contained in:
Jeremy Kauffman 2016-08-18 14:11:06 -04:00
parent a21e9b56b5
commit c940a21ea6
17 changed files with 213 additions and 147 deletions

View file

@ -86,9 +86,9 @@ class Controller
case '/dl/lbry_setup.sh':
return static::redirect('/get', 301);
case '/get/lbry.dmg':
return static::redirect(DownloadActions::getDownloadUrl(DownloadActions::OS_OSX) ?: '/get');
return static::redirect(Github::getDownloadUrl(Os::OS_OSX) ?: '/get');
case '/get/lbry.deb':
return static::redirect(DownloadActions::getDownloadUrl(DownloadActions::OS_LINUX) ?: '/get');
return static::redirect(Github::getDownloadUrl(Os::OS_LINUX) ?: '/get');
case '/art':
return static::redirect('/what', 301);
case '/why':

View file

@ -15,10 +15,7 @@ class ContentActions extends Actions
public static function executeHome()
{
return ['page/home', [
'totalUSD' => CreditApi::getTotalDollarSales(),
'totalPeople' => CreditApi::getTotalPeople()
]];
return ['page/home'];
}
public static function executeFaq()
@ -114,7 +111,8 @@ class ContentActions extends Actions
public static function executeRoadmap()
{
return ['content/roadmap', [
'tasks' => Asana::listRoadmapTasks()
'tasks' => Asana::listRoadmapTasks(),
'changesets' => Github::listRoadmapChangesets()
]];
}

View file

@ -2,23 +2,6 @@
class DownloadActions extends Actions
{
const OS_ANDROID = 'android',
OS_IOS = 'ios',
OS_LINUX = 'linux',
OS_OSX = 'osx',
OS_WINDOWS = 'windows';
public static function getOses()
{
return [
static::OS_WINDOWS => ['/windows', 'Windows', 'icon-windows', '_windows'],
static::OS_OSX => ['/osx', 'OS X', 'icon-apple', '_osx'],
static::OS_LINUX => ['/linux', 'Linux', 'icon-linux', '_linux'],
static::OS_ANDROID => ['/android', 'Android', 'icon-android', '_android'],
static::OS_IOS => ['/ios', 'iOS', 'icon-mobile', '_ios']
];
}
public static function executeGet()
{
$email = static::param('e');
@ -52,7 +35,7 @@ class DownloadActions extends Actions
return ['download/get', ['os' => static::guessOs()]];
}
$osChoices = static::getOses();
$osChoices = Os::getAll();
$os = static::guessOs();
if ($os && isset($osChoices[$os]))
@ -64,7 +47,7 @@ class DownloadActions extends Actions
'osIcon' => $osIcon,
'prefineryUser' => $user ?: [],
'downloadHtml' => View::exists('download/' . $partial) ?
View::render('download/' . $partial, ['downloadUrl' => static::getDownloadUrl($os)]) :
View::render('download/' . $partial, ['downloadUrl' => Github::getDownloadUrl($os)]) :
false
]];
}
@ -123,8 +106,8 @@ class DownloadActions extends Actions
public static function prepareListPartial(array $vars)
{
return $vars + ['osChoices' => isset($vars['excludeOs']) ?
array_diff_key(static::getOses(), [$vars['excludeOs'] => null]) :
static::getOses()
array_diff_key(Os::getAll(), [$vars['excludeOs'] => null]) :
Os::getAll()
];
}
@ -165,7 +148,7 @@ class DownloadActions extends Actions
{
//if exact OS is requested, use that
$uri = strtok($_SERVER['REQUEST_URI'], '?');
foreach (static::getOses() as $os => $osChoice)
foreach (Os::getAll() as $os => $osChoice)
{
if ($osChoice[0] == $uri)
{
@ -182,67 +165,16 @@ class DownloadActions extends Actions
$ua = $_SERVER['HTTP_USER_AGENT'];
if (stripos($ua, 'OS X') !== false)
{
return strpos($ua, 'iPhone') !== false || stripos($ua, 'iPad') !== false ? static::OS_IOS : static::OS_OSX;
return strpos($ua, 'iPhone') !== false || stripos($ua, 'iPad') !== false ? Os::OS_IOS : Os::OS_OSX;
}
if (stripos($ua, 'Linux') !== false || strpos($ua, 'X11') !== false)
{
return strpos($ua, 'Android') !== false ? static::OS_ANDROID : static::OS_LINUX;
return strpos($ua, 'Android') !== false ? Os::OS_ANDROID : Os::OS_LINUX;
}
if (stripos($ua, 'Windows') !== false)
{
return static::OS_WINDOWS;
return Os::OS_WINDOWS;
}
return null;
}
public static function getDownloadUrl($os, $useCache = true)
{
if (!in_array($os, array_keys(static::getOses())))
{
throw new DomainException('Unknown OS');
}
$apc = $useCache && extension_loaded('apc') && ini_get('apc.enabled');
$key = 'lbry_release_data2';
$releaseData = null;
if ($apc)
{
$releaseData = apc_fetch($key);
}
if (!$releaseData)
{
try
{
$releaseData =
json_decode(Curl::get('https://api.github.com/repos/lbryio/lbry/releases/latest', [], ['user_agent' => 'LBRY']), true);
if ($apc)
{
apc_store($key, $releaseData, 600); // cache for 10 min
}
}
catch (Exception $e)
{
}
}
if (!$releaseData)
{
return null;
}
foreach ($releaseData['assets'] as $asset)
{
if (
($os == static::OS_LINUX && in_array($asset['content_type'], ['application/x-debian-package', 'application/x-deb'])) ||
($os == static::OS_OSX && in_array($asset['content_type'], ['application/x-diskcopy', 'application/x-apple-diskimage']))
)
{
return $asset['browser_download_url'];
}
}
return null;
}
}

View file

@ -31,7 +31,7 @@ class Curl
'cache' => false,
'headers' => [],
'verify' => true,
'timeout' => 5,
'timeout' => 10, //5 was timing out on /roadmap for Asana API
'follow_redirects' => true,
'user_agent' => null,
'proxy' => null,

29
lib/tools/Os.class.php Normal file
View file

@ -0,0 +1,29 @@
<?php
/**
* Created by PhpStorm.
* User: kauffj
* Date: 8/16/16
* Time: 8:18 PM
*/
class Os
{
const OS_ANDROID = 'android',
OS_IOS = 'ios',
OS_LINUX = 'linux',
OS_OSX = 'osx',
OS_WINDOWS = 'windows';
public static function getAll()
{
//url, English name, icon class, partial name
//yes, this is probably a bad pattern
return [
Os::OS_WINDOWS => ['/windows', 'Windows', 'icon-windows', '_windows'],
Os::OS_OSX => ['/osx', 'OS X', 'icon-apple', '_osx'],
Os::OS_LINUX => ['/linux', 'Linux', 'icon-linux', '_linux'],
Os::OS_ANDROID => ['/android', 'Android', 'icon-android', '_android'],
Os::OS_IOS => ['/ios', 'iOS', 'icon-mobile', '_ios']
];
}
}

View file

@ -1,34 +0,0 @@
<?php
/**
* Description of CreditApi
*
* @author jeremy
*/
class CreditApi
{
public static function getCurrentTestCreditReward()
{
return 1000;
}
public static function getTotalDollarSales()
{
return 22585;
}
public static function getTotalPeople()
{
$rawJSON = @file_get_contents('https://spreadsheets.google.com/feeds/cells/1iOC1o5jq_4ySwRzsy2tZPPltw6Tbky2e3lDFdsWV8dU/okf1n52/public/full/R1C1?alt=json');
$json = $rawJSON ? json_decode($rawJSON, true) : [];
return isset($json['entry']) && isset($json['entry']['content']) && is_numeric($json['entry']['content']['$t'] ) ?
$json['entry']['content']['$t'] :
6687; //fallback #
}
public static function getCreditsPerDollar($days)
{
//naive algo = decrease 0.5% per day
return 200 * max(0, 100 - $days / 2) / 100;
}
}

View file

@ -53,13 +53,13 @@ class Asana
*/
// return static::get('/projects');
$projects = [
158602294500138 => 'LBRY Browser',
158602294500137 => 'LBRYnet',
161514803479899 => 'Blockchain and Wallets',
158829550589337 => 'Reporting and Analytics',
136290697597644 => 'Integration and Building',
158602294500249 => 'Documentation',
158602294500214 => 'Other'
158602294500138 => ['LBRY Browser', 'https://github.com/lbryio/lbry-web-ui'],
158602294500137 => ['LBRYnet', 'https://github.com/lbryio/lbry'],
161514803479899 => ['Blockchain and Wallets', 'https://github.com/lbryio/lbrycrd'],
158829550589337 => ['Reporting and Analytics', null],
136290697597644 => ['Integration and Building', null],
158602294500249 => ['Documentation', null],
158602294500214 => ['Other', null]
];
$tasks = [
@ -68,8 +68,9 @@ class Asana
];
$categories = array_keys($tasks);
foreach($projects as $projectId => $projectName)
foreach($projects as $projectId => $projectTuple)
{
list($projectName, $projectUrl) = $projectTuple;
$projectTasks = static::get('/tasks?' . http_build_query(['completed_since' => 'now', 'project' => $projectId]));
$key = null;
foreach ($projectTasks as $task)
@ -85,20 +86,30 @@ class Asana
$fullTask = static::get('/tasks/' . $task['id']);
$tasks[$key][] = array_intersect_key($fullTask, ['name' => null, 'due_on' => null]) + [
'project' => $projectName,
'url' => $projectUrl,
'assignee' => $fullTask['assignee'] ? ucwords($fullTask['assignee']['name']) : ''
];
}
}
}
foreach($tasks as &$taskSet)
{
usort($taskSet, function($tA, $tB) {
if ($tA['due_on'] xor $tB['due_on'])
{
return $tA['due_on'] ? -1 : 1;
}
return $tA['due_on'] < $tB['due_on'] ? -1 : 1;
});
}
return $tasks;
}
protected static function get($endpoint, array $data = [])
{
$apiKey = '0/c85cfce3591c2a3e214408cfba7cc44c';
// $apiKey = Config::get('prefinery_key');
// curl -H "Authorization: Bearer ACCESS_TOKEN" https://app.asana.com/api/1.0/users/me
$apiKey = Config::get('asana_key');
$options = static::$curlOptions + [
'headers' => ['Authorization: Bearer ' . $apiKey]

View file

@ -0,0 +1,71 @@
<?php
class Github
{
public static function getDownloadUrl($os, $useCache = true)
{
if (!in_array($os, array_keys(Os::getAll())))
{
throw new DomainException('Unknown OS');
}
try
{
$releaseData = static::get('/repos/lbryio/lbry/releases/latest');
foreach ($releaseData['assets'] as $asset)
{
if (
($os == Os::OS_LINUX && in_array($asset['content_type'], ['application/x-debian-package', 'application/x-deb'])) ||
($os == Os::OS_OSX && in_array($asset['content_type'], ['application/x-diskcopy', 'application/x-apple-diskimage']))
)
{
return $asset['browser_download_url'];
}
}
}
catch (Exception $e)
{
}
return null;
}
public static function get($endpoint)
{
return Curl::get('https://api.github.com' . $endpoint, [], ['user_agent' => 'LBRY', 'json_response' => true, 'cache' => false]);
}
public static function listRoadmapChangesets()
{
$sets = [];
$page = 1;
$allReleases = [];
do
{
$releases = static::get('/repos/lbryio/lbry/releases?page=' . $page);
$page++;
$allReleases = array_merge($allReleases, $releases);
} while (count($releases) >= 30);
usort($allReleases, function($rA, $rB) {
if ($rA['prerelease'] xor $rB['prerelease'])
{
return $rA['prerelease'] ? -1 : 1;
}
return $rA['tag_name'] < $rB['tag_name'];
});
foreach($allReleases as $release)
{
// static::get('/repos/lbryio/lbry/releases')
$sets[$release['tag_name']] = array_intersect_key($release, ['prerelease' => null]) + [
'published_at' => date('Y-m-d', strtotime($release['published_at'])),
'body' => ParsedownExtra::instance()->text($release['body'])
];
}
return $sets;
}
}

View file

@ -3,27 +3,77 @@
<?php echo View::render('nav/_header', ['isDark' => false]) ?>
<main>
<div class="content content-light">
<div class="content content-light spacer2">
<h1>{{roadmap.title}}</h1>
<div class="help">
The LBRY roadmap pulls in information dynamically from our internal project management system and public
<a href="https://github.com/lbryio" class="link-primary">GitHub</a> page.
<div class="help spacer2">
Recent, ongoing and upcoming changes to LBRY.
</div>
<h2>Scheduled Changes</h2>
<?php foreach($tasks as $category => $categoryTasks): ?>
<section>
<h3><?php echo ucfirst($category) ?></h3>
<table class="content full-table">
<?php foreach($categoryTasks as $task): ?>
<tr>
<td><?php echo $task['name'] ?></td>
<td style="width: 20%"><?php echo $task['project'] ?></td>
<td style="width: 15%"><?php echo $task['due_on'] ?></td>
<td style="width: 20%"><?php echo $task['assignee'] ?></td>
<section class="spacer2">
<h2>Recent Changes</h2>
<table class="content full-table" id="changeset-table">
<?php $setCount = 0 ?>
<thead>
<th>Release</th>
<th>Date</th>
<th>Notes</th>
</thead>
<?php foreach($changesets as $version => $changeset): ?>
<tr <?php echo ++$setCount > 5 ? 'style="display: none"' : '' ?>>
<th style="width: 15%">
<?php echo $version ?>
<?php if ($changeset['prerelease']): ?>
<span class="badge badge-info">prerelease</span>
<?php endif ?>
</th>
<td style="width: 15%" class="center"><?php echo $changeset['published_at'] ?></td>
<td><?php echo $changeset['body'] ?></td>
</tr>
<?php if ($version == 'v0.2.2'): ?>
<tr style="display: none">
<th>v0.1-v0.2.2</th>
<td></td>
<td>These releases were not tagged and noted properly. We were too busy creating awesome!</td>
</tr>
<?php endif ?>
<?php endforeach ?>
</table>
<a href="javascript:;" class="link-primary" id="show-all-changesets">show all changes</a>
<?php js_start() ?>
$('#show-all-changesets').click(function() {
$(this).hide();
$('#changeset-table').find('tr').show();
});
<?php js_end() ?>
</section>
<?php foreach($tasks as $category => $categoryTasks): ?>
<section class="spacer2">
<h2><?php echo ucfirst($category) ?> Changes</h2>
<table class="content full-table">
<thead>
<th>Item</th>
<th>Date</th>
<th>Component</th>
<th>Owner</th>
</thead>
<?php foreach($categoryTasks as $task): ?>
<tr>
<td><?php echo $task['name'] ?></td>
<td style="width: 15%"><?php echo $task['due_on'] ?></td>
<td style="width: 20%">
<?php if ($task['url']): ?>
<a href="<?php echo $task['url'] ?>" class="link-primary"><?php echo $task['project'] ?></a>
<?php else: ?>
<?php echo $task['project'] ?>
<?php endif ?>
</td>
<td style="width: 20%">
<?php echo $task['assignee'] ?: '<em>unassigned</em>' ?>
</td>
</tr>
<?php endforeach ?>
</table>
</section>
<?php endforeach ?>
</div>
</main>
</main>
<?php echo View::render('nav/_footer') ?>

View file

@ -1,8 +1,8 @@
<div class="notice notice-info">
<p>{{download.unavailable}}</p>
<?php if ($os == DownloadActions::OS_OSX): ?>
<?php if ($os == Os::OS_OSX): ?>
<p>{{download.osx}}</p>
<?php elseif ($os == DownloadActions::OS_WINDOWS): ?>
<?php elseif ($os == Os::OS_WINDOWS): ?>
<p>{{download.windows}}</p>
<?php endif ?>
</div>

View file

@ -18,4 +18,8 @@ $badge-height: $spacing-vertical * 3/4;
{
background-color: $color-primary;
}
&.badge-info
{
background-color: $color-info-bg;
}
}

View file

@ -138,6 +138,10 @@
vertical-align: text-bottom;
}
}
td.center
{
vertical-align: middle;
}
tr.thead:not(:first-child) th
{
border-top: 1px solid #e2e2e2;

View file

@ -7,6 +7,7 @@ $color-light-alt: hsl(hue($color-primary), 15, 85);
$color-text-dark: #000;
$color-money: #216C2A;
$color-meta-light: #505050;
$color-info-bg: #3a779d;
$font-size: 16px;