Skip to content
This repository has been archived by the owner on Jul 25, 2018. It is now read-only.

Commit

Permalink
LOOKUP ALL THE PACKAGES
Browse files Browse the repository at this point in the history
  • Loading branch information
valpackett committed Apr 14, 2012
1 parent 37b73c8 commit 79d6d94
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 19 deletions.
9 changes: 5 additions & 4 deletions www/.aspen/exp.html
@@ -1,10 +1,11 @@
{% extends "layout.html" %}

{% block headl %}
<script src="/assets/modernizr.js"></script>
<script src="/assets/spin.min.js"></script>
<script src="/assets/ready.min.js"></script>
<script async>
<script src='/assets/modernizr.js'></script>
<script src='/assets/spin.min.js'></script>
<script src='/assets/ready.min.js'></script>
<script>
function FSpinner() { return new Spinner({color: '#aaa', speed: 1.7, length: 20, lines: 16, radius: 24, width: 7}) }
Modernizr.html5please = function(opts){ var passes = true; var features = opts.features.split(' '); var feat; for (var i = -1, len = features.length; ++i < len; ){ feat = features[i]; if (Modernizr[feat] === undefined) window.console && console.warn('Modernizr.' + feat + ' test not found'); if (Modernizr[feat] === false) passes = false; } if (passes){ opts.yep && opts.yep(); return passes; } Modernizr.html5please.cb = opts.nope; var script = document.createElement('script'); var ref = document.getElementsByTagName('script')[0]; var url = 'http://api.html5please.com/' + features.join('+') + '.json?callback=Modernizr.html5please.cb' + opts.options ? ('&' + opts.options) : ''; script.src = url; ref.parentNode.insertBefore(script, ref); return false; };

Modernizr.html5please({
Expand Down
27 changes: 14 additions & 13 deletions www/.aspen/layout.html
Expand Up @@ -4,41 +4,42 @@
<meta charset='utf-8'>
<title>{% block title %}{% end %} / .labs { float: both }</title>
<link rel='stylesheet' type='text/css' href='/assets/style.css'>
<script src="/assets/prefixfree.min.js"></script>
<script src='/assets/prefixfree.min.js'></script>
{% block head %}{% end %}{% block headl %}{% end %}
</head>
<body>
<header>
<h1><a href="/">.labs { float: both }</a></h1>
<h1><a href='/'>.labs { float: both }</a></h1>
</header>
<section role="main" id="content">
<section role='main' id='content'>
{% block content %}
{% end %}
</section>
<footer>
<p>
<a href="https://github.com/myfreeweb/labs/tree/master/www{{ request.path }}">Source code</a>
<a href='https://github.com/myfreeweb/labs/tree/master/www{{ request.path }}'>Source code</a>
</p>
<p>
By <a href="http://floatboth.com" rel="author">Greg V</a>
By <a href='http://floatboth.com' rel='author'>Greg V</a>
</p>
<p>
Using Python packages:
{% for dep in request.deps %}
<a href="http://pypi.python.org/pypi/{{ dep }}">{{ dep }}</a>
<a href='http://pypi.python.org/pypi/{{ dep }}'>{{ dep }}</a>
{% end %}
</p>
<p>
Using JavaScript libraries:
<a href="http://mbostock.github.com/d3/">D3.js</a>
<a href="http://leaverou.github.com/prefixfree/">-prefix-free</a>
<a href="http://modernizr.com">modernizr</a>
<a href="https://github.com/fgnass/spin.js">spin.js</a>
<a href="https://github.com/ded/domready">domready</a>
<a href="http://api.html5please.com/">html5please API</a>
<a href='http://mbostock.github.com/d3/'>D3.js</a>
<a href='http://leaverou.github.com/prefixfree/'>-prefix-free</a>
<a href='http://modernizr.com'>modernizr</a>
<a href='https://github.com/fgnass/spin.js'>spin.js</a>
<a href='https://github.com/ded/domready'>domready</a>
<a href='https://github.com/ded/reqwest'>reqwest</a>
<a href='http://api.html5please.com/'>html5please API</a>
</p>
<p>
Hosted on <a href="http://heroku.com">Heroku</a>
Hosted on <a href='http://heroku.com'>Heroku</a>
</p>
</footer>
<script>
Expand Down
7 changes: 7 additions & 0 deletions www/assets/reqwest.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion www/index.html
Expand Up @@ -9,7 +9,8 @@
<p>
Hi there. Feel free to try these cool experiments:
<ul>
<li><a href="/musiclyrics/">Music lyrics graph visualization</a></li>
<li><a href='/pkglookup/'>Package lookup (Ubuntu, AUR, Homebrew, FreeBSD, PyPI, RubyGems, etc.)</a></li>
<li><a href='/musiclyrics/'>Music lyrics graph visualization</a></li>
</ul>
</p>
</article>
Expand Down
2 changes: 1 addition & 1 deletion www/musiclyrics/index.html
Expand Up @@ -35,7 +35,7 @@
<script>
function start(user) {
var graph = document.getElementById('graph')
var spinner = new Spinner({color: '#aaa', speed: 1.7, length: 20, lines: 16, radius: 24, width: 7}).spin(graph)
var spinner = FSpinner().spin(graph)
d3.json(user + '.json', function(data) {
spinner.stop()
var w = graph.clientWidth, h = 600
Expand Down
76 changes: 76 additions & 0 deletions www/pkglookup/index.html
@@ -0,0 +1,76 @@


{% extends "exp.html" %}

{% block title %}Packages lookup{% end %}

{% block content %}
<p>
Looks up packages on PyPI, CPAN, RubyGems, NPM, Maven Central, Clojars,
GitHub, Bitbucket, Ubuntu, Arch User Repository, FreeBSD ports and Homebrew.
</p>
<p>
Results are cached for five hours.
</p>
<form id='theform'>
<input type='text' id='query'>
<button id='go' disabled>Go!</button>
</form>
<div id='spintarget'></div>
<ul id='results'>
</ul>
{% end %}

{% block head %}
<script src='/assets/reqwest.min.js'></script>
<style>
@media screen and (min-width: 700px) {
#results { column-count: 2; column-gap: 1em }
}
@media screen and (min-width: 1200px) {
#results { column-count: 3 }
}
#results { list-style-type: none }
#results a { color: inherit }
.pypi { color: #c4a000 }
.rubygems { color: #a40000 }
.freebsd { color: #ef2929 }
.maven { color: #3465a4 }
.aur { color: #729fcf }
.clojars { color: #4e9a06 }
.ubuntu { color: #8f5902 }
.npm { color: #75507b }
.homebrew { color: #ce5c00 }
.github, .bitbucket { color: #555753 }
</style>
{% end %}

{% block js %}
domready(function() {
function $() { return document.getElementById.apply(document, arguments) }
$('go').disabled = false
$('theform').onsubmit = function() {
var spinner = FSpinner().spin($('spintarget'))
reqwest({
url: '/pkglookup/lookup.json', data: {q: $('query').value},
type: 'json', method: 'get',
error: function(err) {
console.log(err)
spinner.stop()
$('results').innerHTML = '<li>FAIL, see console</li>'
},
success: function(resp) {
var acc = ''
resp.results.forEach(function(result) {
acc += '<li class='+ result.source.toLowerCase() +'>' +
'<a href="'+ result.url +'">'+ result.name +'</a> on '+ result.source +'</li>'
})
spinner.stop()
$('results').innerHTML = acc
$('query').value = ''
}
})
return false
}
})
{% end %}
162 changes: 162 additions & 0 deletions www/pkglookup/lookup.json
@@ -0,0 +1,162 @@
import json
import requests
import xmlrpclib
from Queue import Queue
from threading import Thread
from config import REDIS as red
from bs4 import BeautifulSoup as BS

last_ubuntu = 'precise'
lim = 7
cache_hours = 5
[red.delete(k) for k in red.keys('PKG*')] # Delete cached stuff when reloading the app

query = request.qs.one('q')
cachekey = 'PKG'+query
cached = red.get(cachekey)

if cached:
response.body = cached
else:
global results
global queue
results = []
queue = Queue()

def fetcher(fn):
def inner():
global results
global queue
query = queue.get()
results += fn(query)
queue.task_done()
return inner

@fetcher
def l_pypi(query):
resp = xmlrpclib.ServerProxy('http://pypi.python.org/pypi').search({'name': query})
return [{'name': entry['name'], 'source': 'PyPI',
'url': 'http://pypi.python.org/pypi/' + entry['name']}
for entry in resp[:lim]]

@fetcher
def l_cpan(query):
resp = requests.get('http://search.cpan.org/search',
params={'mode': 'module', 'format': 'xml', 'query': query})
if resp.status_code == 200:
return [{'name': entry.find('name').string, 'source': 'CPAN',
'url': ''.join(entry.find('link').strings)}
for entry in BS(resp.text, 'html.parser').find_all('module')[:lim]]
# html.parser parses <link>http://an.url</link> correctly
return []

@fetcher
def l_rubygems(query):
resp = requests.get('https://rubygems.org/api/v1/search.json',
params={'query': query})
if resp.status_code == 200:
return [{'name': entry['name'], 'source': 'RubyGems',
'url': entry['project_uri']}
for entry in json.loads(resp.text)[:lim]]
return []

@fetcher
def l_npm(query):
resp = requests.get('http://search.npmjs.org/_list/search/search',
params={'startkey': '"%s"' % query,
'endkey': '"%sZZZZZZZZZZZZZZZZZZZ"' % query, 'limit': lim})
if resp.status_code == 200:
return [{'name': entry['key'], 'source': 'npm',
'url': 'http://search.npmjs.org/#/' + entry['key']}
for entry in json.loads(resp.text)['rows']]
return []

@fetcher
def l_maven(query):
resp = requests.get('http://search.maven.org/solrsearch/select',
params={'wt': 'json', 'rows': lim, 'q': query})
if resp.status_code == 200:
return [{'name': entry['id'], 'source': 'Maven',
'url': 'http://search.maven.org/#artifactdetails|%s|%s|%s|jar'
% (entry['g'], entry['a'], entry['latestVersion'])}
for entry in json.loads(resp.text)['response']['docs']]
return []

@fetcher
def l_clojars(query):
resp = requests.get('http://clojars.org/search', params={'q': query})
if resp.status_code == 200:
return [{'name': entry.find('a').string, 'source': 'Clojars',
'url': 'http://clojars.org/' + entry.find('a').string}
for entry in BS(resp.text).find_all('li', 'search-results')[:lim]]
return []

@fetcher
def l_github(query):
resp = requests.get('http://github.com/api/v2/json/repos/search/'+query)
if resp.status_code == 200:
return [{'name': entry['name'], 'source': 'GitHub', 'url': entry['url']}
for entry in json.loads(resp.text)['repositories'][:lim]]
return []

@fetcher
def l_bitbucket(query):
resp = requests.get('https://api.bitbucket.org/1.0/repositories/', params={'name': query})
if resp.status_code == 200:
return [{'name': entry['name'], 'source': 'Bitbucket',
'url': entry['resource_uri'].replace('/api/1.0/repositories', 'https://bitbucket.org')}
for entry in json.loads(resp.text)['repositories'][:lim]]
return []

@fetcher
def l_ubuntu(query):
resp = requests.get('http://packages.ubuntu.com/search',
params={'suite': last_ubuntu, 'searchon': 'names', 'keywords': query})
if resp.status_code == 200:
return [{'name': entry.string.replace('Package ', ''), 'source': 'Ubuntu',
'url': 'http://packages.ubuntu.com/%s/%s' % \
(last_ubuntu, entry.string.replace('Package ', ''))}
for entry in BS(resp.text).find('div', id='psearchres').find_all('h3')[:lim]]
return []

@fetcher
def l_aur(query):
resp = requests.get('https://aur.archlinux.org/rpc.php',
params={'type': 'search', 'arg': query})
if resp.status_code == 200:
return [{'name': entry['Name'], 'source': 'AUR',
'url': 'http://aur.archlinux.org/packages.php?ID=' + entry['ID']}
for entry in json.loads(resp.text)['results'][:lim]]
return []

@fetcher
def l_freebsd(query):
resp = requests.get('http://www.freebsd.org/cgi/ports.cgi',
params={'stype': 'name', 'sektion': 'all', 'query': query})
if resp.status_code == 200:
acc = []
for category in BS(resp.text).find_all('dl'):
acc += [{'name': entry.find_all('a')[1].string, 'source': 'FreeBSD',
'url': entry.find_all('a')[1]['href']}
for entry in category.find_all('dt')[:lim]]
return acc
return []

@fetcher
def l_homebrew(query):
resp = requests.get('http://braumeister.org/search/'+query)
if resp.status_code == 200:
return [{'name': entry.find('a').string, 'source': 'Homebrew',
'url': 'http://braumeister.org' + entry.find('a')['href']}
for entry in BS(resp.text).find_all('div', 'formula')[:lim]]

for fn in [l_pypi, l_cpan, l_rubygems, l_npm, l_maven,
l_clojars, l_github, l_bitbucket, l_ubuntu,
l_aur, l_freebsd, l_homebrew]:
Thread(target=fn).start()
queue.put(query)

queue.join()
response.body = json.dumps({'results': results})
red.set(cachekey, response.body)
red.expire(cachekey, 60*60*cache_hours)

0 comments on commit 79d6d94

Please sign in to comment.