Compare commits

..

No commits in common. "feature/ui" and "master" have entirely different histories.

14 changed files with 51 additions and 7324 deletions

View file

@ -43,8 +43,6 @@ class GitHub(object):
Returns an iterator over a resource, eg 'repos/divio/django-cms/watchers' that automatically handles
pagination.
"""
if params is None:
params = {}
data, response = self._get(path, params)
for thing in data:
yield thing

View file

@ -1,15 +0,0 @@
# -*- coding: utf-8 -*-
from django.utils.functional import SimpleLazyObject
from githubnetwork.models import GHUser
def get_github_user(request):
if not request.user.is_authenticated():
return None
return GHUser.objects.get(user=request.user)
class GithubUserMiddleware(object):
def process_request(self, request):
request.gh_user = SimpleLazyObject(lambda: get_github_user(request))

View file

@ -1,13 +1,19 @@
# -*- coding: utf-8 -*-
from collections import defaultdict
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import render_to_response
from django.http import HttpResponse, Http404, HttpResponseForbidden
from django.shortcuts import render_to_response, redirect
from django.template.context import RequestContext
from django.utils import simplejson
from django.utils.decorators import method_decorator
from django.views.generic.base import TemplateView, TemplateResponseMixin
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, ModelFormMixin, FormView
from django.views.generic.list import ListView
from django.contrib.auth.models import User
from ghapi import api
from models import GHUser
from models import GHUser, Repo
class Graph(object):
@ -15,10 +21,10 @@ class Graph(object):
self.nodes = set()
self.edges = {}
self.distances = {}
def add_node(self, value):
self.nodes.add(value)
def add_edge(self, from_node, to_node, distance):
self._add_edge(from_node, to_node, distance)
self._add_edge(to_node, from_node, distance)
@ -35,77 +41,48 @@ class NetworkView(DetailView):
return self.user
def get_user_network(self):
user = self.get_user()
graph = []
user = self.get_user()
graph = []
def get_repo_network(self):
user = self.get_user()
graph = []
for repo in api.get_iter('user/%s/repos' % user):
# TODO: (Lynn) this is messy - clean up
links = {}
repo_info = api.get('repos/%s/%s' % (user, repo))
parent = repo_info['parent']['owner']['login']
child = repo_info['owner']['login']
watchers = repo_info['parent']['watchers']
network = repo_info['network_count']
date_updated_parent = repo_info['parent']['updated_at']
date_updated_child = repo_info['updated_at']
links['source'] = parent
links['target'] = child
links['weight'] = {'watchers': watchers, 'network': network,
'date_updated_parent': date_updated_parent,
'date_updated_child': date_updated_child}
graph.append(links)
return graph
user = self.get_user()
graph = []
for repo in api.get_iter('user/%s/repos' % user):
# TODO: (Lynn) this is messy - clean up
links = {}
repo_info = api.get('repos/%s/%s' % (user, repo))
parent = repo_info['parent']['owner']['login']
child = repo_info['owner']['login']
watchers = repo_info['parent']['watchers']
network = repo_info['network_count']
date_updated_parent = repo_info['parent']['updated_at']
date_updated_child = repo_info['updated_at']
links['source'] = parent
links['target'] = child
links['weight'] = {'watchers': watchers, 'network' : network,
'date_updated_parent' : date_updated_parent,
'date_updated_child' : date_updated_child }
graph.append(links)
return graph
def get_queryset(self):
self.get_user()
if repos:
# do repo-y things
self.get_repo_network()
else:
# do user-y things
self.get_user_network()
self.get_user()
if repos:
# do repo-y things
self.get_repo_network()
else:
# do user-y things
self.get_user_network()
def get_context_data(self, **kwargs):
# TODO: (Lynn) figure out what's needed for context data
context_object_name = self.get_context_object_name(queryset)
def get(self):
self.object_list = self.get_queryset()
self.object_list = self.get_queryset()
context = self.get_context_data(object_list=self.object_list)
return self.render_to_response(context)
@method_decorator(login_required)
def dispatch(self, *args, ** kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
def _sorted_repos(request):
'''Get a list of repos for the currently authorized user, sort it, and
return it.'''
repos = [r for r in request.github.get_iter('users/%s/repos' %
request.user.username)]
repos.sort(key=lambda x: x['name'])
return repos
@login_required
def me(request):
context = RequestContext(request)
context['followers'] = simplejson.dumps(
[{'name': unicode(follower), 'avatar': follower.avatar_url}
for follower in GHUser.objects.filter(following=request.gh_user)])
context['repos'] = _sorted_repos(request)
return render_to_response('me.html', context)
@login_required
def get_user_followers(request):
name = request.GET.get('user', None)
if not name:
raise HttpResponseBadRequest()
names = simplejson.dumps([user['login']
for user in request.github.get_iter('users/%s/followers' % name)])
return HttpResponse(names, content_type='application/json')
def dispatch(self, *args. **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)

View file

@ -43,7 +43,6 @@ MIDDLEWARE_CLASSES = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'ghapi.middleware.GithubAPIMiddleware',
'githubnetwork.middleware.GithubUserMiddleware',
]
TEMPLATE_CONTEXT_PROCESSORS = [
@ -86,7 +85,7 @@ AUTHENTICATION_BACKENDS = [
]
LOGIN_URL = '/login/'
LOGIN_REDIRECT_URL = '/me/'
LOGIN_REDIRECT_URL = '/'
LOGIN_ERROR_URL = '/login/failed/'

7034
static/d3/d3.v2.js vendored

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,24 +0,0 @@
{# vim: set ft=htmldjango #}
{% extends "base.html" %}
{% block head_css %}
<style text="text/css">
h1.placeholder {
margin-top: 80px;
text-align: center;
color: #ccc;
}
</style>
{% endblock %}
{% block container %}
{% include "navbar.html" %}
<div class="container-fluid">
{% block graph %}<h1 class="placeholder">PUT A GRAPH HERE</h1>{% endblock %}
</div>
{% endblock container %}
{% block body_js %}
<script src="{{ STATIC_URL }}js/jquery.js"></script>
<script src="{{ STATIC_URL }}bootstrap/js/bootstrap-dropdown.js"></script>
{% endblock %}

View file

@ -1,2 +0,0 @@
{# vim: set ft=htmldjango #}
{% extends "graph_base.html" %}

View file

@ -1,6 +0,0 @@
{# vim: set ft=htmldjango #}
{% extends "graph_base.html" %}
{% block graph %}
<h1 class="placeholder">PUT A GRAPH OF<br/>{{ username }}'s<br/>{{ repo }} REPO<br/>HERE</h1>
{% endblock %}

View file

@ -1,118 +0,0 @@
{% extends "graph_base.html" %}
{% block graph %}
<h1>People following you</h1>
<div id='chart'> </div>
<style>
circle.node {
stroke: #fff;
stroke-width: 1.5px;
}
line.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<script src="{{ STATIC_URL }}d3/d3.v2.js"></script>
<script src="{{ STATIC_URL }}/js/jquery.js"></script>
<script>
$(document).ready(function(){
var followers = {{ followers|safe }};
var map = {};
var w = 960,
h = 960;
var color = d3.scale.category20();
var force = d3.layout.force()
.gravity(.05)
.distance(250)
.charge(-500)
.size([w, h]);
var nodes = force.nodes(),
links = force.links();
var vis = d3.select("#chart").append("svg:svg")
.attr("width", w)
.attr("height", h);
force.on("tick", function() {
vis.selectAll("g.node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
vis.selectAll("line.link")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
});
function restart() {
var link = vis.selectAll("line.link")
.data(links, function(d) { return d.source.id + "-" + d.target.id; });
link.enter().insert("svg:line", "g.node")
.attr("class", "link");
link.exit().remove();
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id;});
var nodeEnter = node.enter().append("svg:g").attr('class', 'node').call(force.drag);
nodeEnter.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", function(d){return d.avatar;})
.attr("x", "-16px")
.attr("y", "-16px")
.style("border-radius", "16px;")
.attr("width", "32px")
.attr("height", "32px");
nodeEnter.append("title")
.text(function(d) { return d.name });
node.exit().remove();
force.start();
}
// Add three nodes and three links.
function init() {
var center = {"name": "{{ request.gh_user.gh_login }}", "avatar": "{{ request.gh_user.avatar_url }}"};
nodes.push(center);
for (var i = 0; i<followers.length;i++){
nodes.push(followers[i]);
links.push({source:center, target:followers[i]});
map[followers[i].name] = followers[i];
}
restart();
}
function addLink(link) {
links.push(link);
restart();
}
restart();
init();
function loadFollowerFollowers(follower){
$.getJSON('{% url get_user_followers %}?user=' + follower.name, function(data){
for (var i = 0; i<data.length; i++){
var target = map[data[i]];
if (target){
addLink({source:follower, target:target})
}
}
});
// addLink({source: followers[15], target: followers[5]});
}
for (var i = 0; i<followers.length; i++){
loadFollowerFollowers(followers[i]);
}
});
</script>
{% endblock %}

View file

@ -1,29 +0,0 @@
{# vim: set ft=htmldjango #}
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand" href="">Cool name</a>
<ul class="nav">
<li><a href="{% url me %}">Me</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">My Repos</a>
<ul class="dropdown-menu">
{% for r in repos %}
<li><a href="#">{{ r.name }}</a></li>
{% endfor %}
</ul>
</li>
</ul>
<ul class="nav pull-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
{{ request.user.username }}
</a>
<ul class="dropdown-menu">
<li><a href="#">Logout</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>

View file

@ -11,13 +11,7 @@ admin.autodiscover()
urlpatterns = patterns('',
url(r'^%s(?P<path>.*)$' % re.escape(settings.STATIC_URL.lstrip('/')), 'django.contrib.staticfiles.views.serve', {'insecure': True}),
url(r'^admin/', include(admin.site.urls)),
url(r'^followers/', views.graph_followers,
name='graph_followers'),
url(r'^login/$', views.login, name='login'),
url(r'^me/$', 'githubnetwork.views.me', name='me'),
url(r'^~followers/$', 'githubnetwork.views.get_user_followers', name='get_user_followers'),
url(r'^repo/(?P<user>\w+)/(?P<repo>\w+)/', views.graph_repo,
name='graph_repo'),
url(r'^$', views.index, name='index'),
url(r'', include('social_auth.urls')),
)

View file

@ -1,5 +1,4 @@
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.shortcuts import redirect, render_to_response
from django.template import RequestContext
@ -8,12 +7,12 @@ def index(request):
'''Index page. Everyone starts here. If the user is logged in (that is, they
have a session id) return the follower_graph view. Otherwise, render the
index page.'''
if request.user.is_authenticated():
return redirect('graph_followers')
if request.session.get('sessionid', False):
return follower_graph(request)
# Set a test cookie. When the user clicks the 'Login' button, test and make
# sure this cookie was set properly.
request.session.set_test_cookie()
return render_to_response('index.html', RequestContext(request))
return render_to_response('login.html', RequestContext(request))
def login(request):
@ -22,21 +21,13 @@ def login(request):
# Make sure the user can accept cookies.
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
return redirect('socialauth_begin', backend='github')
return redirect('/login/github/')
else:
# During development, I've landed here a lot, despite having cookies
# enabled. So, set the test cookie so that trying to login from here
# actually works.
request.session.set_test_cookie()
# Render an error -- fix your damn cookies!
return render_to_response('index.html',
return render_to_response('login.html',
{ 'error': "Fix your damn cookies!" })
@login_required
def graph_repo(request, user=None, repo=None):
return render_to_response('graph_repo.html', {
'graph_user': user,
'graph_repo': repo,
'repos': _sorted_repos(request)
}, RequestContext(request))
def follower_graph(request):
return 'Hello!'