commit
						934ea0a905
					
				
					 8 changed files with 7231 additions and 44 deletions
				
			
		| 
						 | 
					@ -43,6 +43,8 @@ class GitHub(object):
 | 
				
			||||||
        Returns an iterator over a resource, eg 'repos/divio/django-cms/watchers' that automatically handles
 | 
					        Returns an iterator over a resource, eg 'repos/divio/django-cms/watchers' that automatically handles
 | 
				
			||||||
        pagination.
 | 
					        pagination.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					        if params is None:
 | 
				
			||||||
 | 
					            params = {}
 | 
				
			||||||
        data, response = self._get(path, params)
 | 
					        data, response = self._get(path, params)
 | 
				
			||||||
        for thing in data:
 | 
					        for thing in data:
 | 
				
			||||||
            yield thing
 | 
					            yield thing
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								githubnetwork/middleware.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								githubnetwork/middleware.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					# -*- 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))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,13 @@
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
from collections import defaultdict
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib.auth.decorators import login_required
 | 
					from django.contrib.auth.decorators import login_required
 | 
				
			||||||
from django.http import HttpResponse, Http404, HttpResponseForbidden
 | 
					from django.http import HttpResponse, HttpResponseBadRequest
 | 
				
			||||||
from django.shortcuts import render_to_response, redirect
 | 
					from django.shortcuts import render_to_response
 | 
				
			||||||
from django.template.context import RequestContext
 | 
					from django.template.context import RequestContext
 | 
				
			||||||
 | 
					from django.utils import simplejson
 | 
				
			||||||
from django.utils.decorators import method_decorator
 | 
					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.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 ghapi import api
 | 
				
			||||||
from models import GHUser, Repo
 | 
					from models import GHUser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Graph(object):
 | 
					class Graph(object):
 | 
				
			||||||
| 
						 | 
					@ -84,5 +78,22 @@ class NetworkView(DetailView):
 | 
				
			||||||
        return self.render_to_response(context)
 | 
					        return self.render_to_response(context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @method_decorator(login_required)
 | 
					    @method_decorator(login_required)
 | 
				
			||||||
    def dispatch(self, *args. **kwargs):
 | 
					    def dispatch(self, *args, ** kwargs):
 | 
				
			||||||
        return super(ProtectedView, self).dispatch(*args, **kwargs)
 | 
					        return super(ProtectedView, self).dispatch(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@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)])
 | 
				
			||||||
 | 
					    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')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,7 @@ MIDDLEWARE_CLASSES = [
 | 
				
			||||||
    'django.contrib.auth.middleware.AuthenticationMiddleware',
 | 
					    'django.contrib.auth.middleware.AuthenticationMiddleware',
 | 
				
			||||||
    'django.contrib.messages.middleware.MessageMiddleware',
 | 
					    'django.contrib.messages.middleware.MessageMiddleware',
 | 
				
			||||||
    'ghapi.middleware.GithubAPIMiddleware',
 | 
					    'ghapi.middleware.GithubAPIMiddleware',
 | 
				
			||||||
 | 
					    'githubnetwork.middleware.GithubUserMiddleware',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEMPLATE_CONTEXT_PROCESSORS = [
 | 
					TEMPLATE_CONTEXT_PROCESSORS = [
 | 
				
			||||||
| 
						 | 
					@ -85,7 +86,7 @@ AUTHENTICATION_BACKENDS = [
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGIN_URL          = '/login/'
 | 
					LOGIN_URL          = '/login/'
 | 
				
			||||||
LOGIN_REDIRECT_URL = '/'
 | 
					LOGIN_REDIRECT_URL = '/me/'
 | 
				
			||||||
LOGIN_ERROR_URL    = '/login/failed/'
 | 
					LOGIN_ERROR_URL    = '/login/failed/'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										7034
									
								
								static/d3/d3.v2.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7034
									
								
								static/d3/d3.v2.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										4
									
								
								static/d3/d3.v2.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								static/d3/d3.v2.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										118
									
								
								templates/me.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								templates/me.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,118 @@
 | 
				
			||||||
 | 
					{% 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 %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								urls.py
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								urls.py
									
										
									
									
									
								
							| 
						 | 
					@ -14,6 +14,8 @@ urlpatterns = patterns('',
 | 
				
			||||||
    url(r'^followers/', views.graph_followers,
 | 
					    url(r'^followers/', views.graph_followers,
 | 
				
			||||||
        name='graph_followers'),
 | 
					        name='graph_followers'),
 | 
				
			||||||
    url(r'^login/$', views.login, name='login'),
 | 
					    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,
 | 
					    url(r'^repo/(?P<user>\w+)/(?P<repo>\w+)/', views.graph_repo,
 | 
				
			||||||
        name='graph_repo'),
 | 
					        name='graph_repo'),
 | 
				
			||||||
    url(r'^$', views.index, name='index'),
 | 
					    url(r'^$', views.index, name='index'),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue