Merge branch 'master' of github.com:beetletweezers/tweezers

Conflicts:
	core/views.py
rtd2
Eric Holscher 2010-08-15 03:40:46 +00:00
commit 45a523f143
18 changed files with 231 additions and 31 deletions

View File

@ -1,4 +1,5 @@
from django.conf import settings
from django.db.models import F
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template import RequestContext
@ -11,6 +12,7 @@ import os
from projects.models import Project
from projects.tasks import update_docs
from projects.utils import find_file
from watching.models import PageView
@csrf_view_exempt
@ -29,6 +31,11 @@ def serve_docs(request, username, project_slug, filename):
filename = filename.rstrip('/')
if not os.path.exists(os.path.join(proj.full_html_path, filename)):
return HttpResponse("These docs haven't been built yet :(")
if 'html' in filename:
pageview, created = PageView.objects.get_or_create(project=proj, url=filename)
if not created:
pageview.count = F('count') + 1
pageview.save()
return serve(request, filename, proj.full_html_path)
def render_header(request):

View File

@ -53,37 +53,16 @@ input[type="text"], input[type="password"] { width: 250px; height: 20px; margin-
select { display: block; max-height: 300px; width: 250px; margin-bottom: 10px; font: 16/20px "inconsolata-1", "inconsolata-2", 'bitstream vera sans mono', 'andale mono', 'lucida console', monospace; }
textarea { width: 435px; height: 150px; }
input[type="submit"], input[type="button"], button { font-family: "ff-meta-web-pro-1", "ff-meta-web-pro-2", Arial, "Helvetica Neue", sans-serif; color: #666; font-weight: bold; padding: 4px 10px; border: none; background: #e6e6e6 url(../images/gradient.png) repeat-x bottom left; margin: 30px 5px 20px 0; text-shadow: 0 1px 0 rgba(255, 255, 255, 1); border: 1px solid #bfbfbf; }
input[type="submit"]:hover, input[type="button"]:hover, button:hover { background: #8ECC4C url(../images/gradient.png) repeat-x bottom left; color: #fff; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); }
input[type="submit"]:hover, input[type="button"]:hover, button:hover { background-color: #8ECC4C; color: #fff; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); border-color: #8ECC4C; }
fieldset { border: 1px solid #bfbfbf; padding: 15px; -moz-border-radius: 3px; -webkit-border-radius: 3px; margin-bottom: 15px; }
input[type="hidden"] { display: none; }
input[type="checkbox"], input[type="radio"] { display: inline; }
label { display: block; margin-bottom: 4px; font-weight: bold; }
/* header */
#rtfd-header { height: 50px; min-width: 780px; background: #465158; overflow: hidden; text-align: left; border-bottom: 1px solid #ccc; }
/* header title */
.rtfd-header-title h1 { font-size: 20px; padding: 5px 20px; color: #fff; }
/* header search */
.rtfd-header-search { position: absolute; top: 10px; left: 230px; }
.rtfd-header-search input { padding: 0 5px; margin: 0; height: 25px; font-size: 14px; float: left; -moz-border-radius: 0; -webkit-border-radius: 0; border: none; }
.rtfd-header-search input[type="text"] { -moz-border-radius-topleft: 3px; -moz-border-radius-bottomleft: 3px; -webkit-border-top-left-radius: 3px; -webkit-border-bottom-left-radius: 3px; }
.rtfd-header-search input[type="submit"] { -moz-border-radius-topright: 3px; -moz-border-radius-bottomright: 3px; -webkit-border-top-right-radius: 3px; -webkit-border-bottom-right-radius: 3px; padding: 0 12px; }
/* header nav */
.rtfd-header-nav { position: absolute; top: 0; right: 10px; }
.rtfd-header-nav ul li { float: left; }
.rtfd-header-nav ul li a { display: block; text-decoration: none; background: url(../images/gradient.png) bottom left repeat-x #697983; padding: 15px; color: #fff; }
.rtfd-header-nav ul li a:hover { background-color: #8CA1AF; }
.rtfd-header-nav ul li.active a, .header-nav ul li.active a:hover { background-color: #BAC7CF; }
/* content */
#content { margin-top: 80px; }
#content { padding-top: 80px; }
/* module */
@ -127,6 +106,13 @@ label { display: block; margin-bottom: 4px; font-weight: bold; }
.pagination .current.page, .pagination .current.page:hover { color: #444; background: url("../images/gradient-light.png") repeat-x scroll left bottom #d9d9d9; }
/* footer */
#footer { margin: 100px 0; color: #aaa; }
#footer a { color: #aaa; }
#footer a:hover { color: #666; }
/* utils */
.clear { clear: both; }

24
media/css/header.css Normal file
View File

@ -0,0 +1,24 @@
body { padding-top: 50px; }
/* header */
#rtfd-header { position: absolute; top: 0; left: 0; width: 100%; font: 16px/20px "ff-meta-web-pro-1","ff-meta-web-pro-2", Arial, "Helvetica Neue", sans-serif; height: 50px; min-width: 780px; background: #465158; overflow: hidden; text-align: left; }
#rtfd-header ul { margin: 0; padding: 0; list-style: none; }
/* header title */
.rtfd-header-title h1 { font-size: 20px; padding: 5px 20px; color: #fff; }
/* header search */
.rtfd-header-search { position: absolute; top: 10px; left: 230px; }
.rtfd-header-search input { padding: 0 5px; margin: 0; height: 25px; font-size: 14px; float: left; -moz-border-radius: 0; -webkit-border-radius: 0; border: none; }
.rtfd-header-search input[type="text"] { -moz-border-radius-topleft: 3px; -moz-border-radius-bottomleft: 3px; -webkit-border-top-left-radius: 3px; -webkit-border-bottom-left-radius: 3px; width: 220px; }
.rtfd-header-search input[type="submit"] { font-family: "ff-meta-web-pro-1", "ff-meta-web-pro-2", Arial, "Helvetica Neue", sans-serif; -moz-border-radius-topright: 3px; -moz-border-radius-bottomright: 3px; -webkit-border-top-right-radius: 3px; -webkit-border-bottom-right-radius: 3px; padding: 0 12px; background: #e6e6e6 url(../images/gradient.png) repeat-x bottom left; font-weight: bold; color: #666; }
.rtfd-header-search input[type="submit"]:hover { text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); background-color: #8ECC4C; color: #fff; }
/* header nav */
.rtfd-header-nav { position: absolute; top: 0; right: 10px; }
.rtfd-header-nav ul li { float: left; }
.rtfd-header-nav ul li a { display: block; text-decoration: none; background: url(../images/gradient.png) bottom left repeat-x #697983; padding: 15px; color: #fff; }
.rtfd-header-nav ul li a:hover { background-color: #8CA1AF; }
.rtfd-header-nav ul li.active a, .header-nav ul li.active a:hover { background-color: #BAC7CF; }

View File

@ -15,3 +15,4 @@ django-extensions
-e git+http://github.com/ericflo/django-pagination.git#egg=pagination
-e hg+http://bitbucket.org/ubernostrum/django-profiles/#egg=profiles
-e git+http://github.com/nathanborror/django-registration.git#egg=django-registration
-e git+http://github.com/nathanborror/django-basic-apps.git#egg=django-basic-apps

View File

@ -36,7 +36,7 @@ class Project(models.Model):
return self.name
def get_absolute_url(self):
return reverse('projects_detail', args=[self.user.username, self.slug, ''])
return reverse('projects_detail', args=[self.user.username, self.slug])
def user_doc_path(self):
return os.path.join(settings.DOCROOT, self.user.username, self.slug)
@ -182,7 +182,6 @@ class File(models.Model):
def filename(self):
return os.path.join(
self.project.conf.path,
self.project.slug,
'%s.rst' % self.denormalized_path
)

View File

@ -6,7 +6,8 @@
Contents:
.. toctree::
:maxdepth: 2
:glob:
{{ project.slug }}/*
{% load projects_tags %}
{% for file in project|annotated_tree %}
{{ file.denormalized_path }}
{% endfor %}

View File

View File

@ -0,0 +1,19 @@
from django import template
register = template.Library()
@register.filter
def top_level_files(project):
return project.files.filter(parent__isnull=True)
@register.filter
def annotated_tree(project, max_depth=99):
annotated = []
def walk_tree(qs, depth=1):
for obj in qs:
obj.depth = depth
annotated.append(obj)
if depth < max_depth:
walk_tree(obj.children.order_by('ordering'), depth+1)
walk_tree(project.files.filter(parent__isnull=True).order_by('ordering'))
return annotated

View File

@ -17,6 +17,10 @@ urlpatterns = patterns('projects.views.public',
'project_index',
name='project_tag_detail',
),
url(r'^(?P<username>\w+)/(?P<project_slug>[-\w]+)/$',
'project_detail',
name='projects_detail'
),
url(r'^(?P<username>\w+)/$',
'project_index',
name='projects_user_list'

View File

@ -85,10 +85,12 @@ INSTALLED_APPS = (
'south',
'taggit',
'django_extensions',
'basic.flagging',
# our apps
'projects',
'core',
'watching',
)
@ -99,4 +101,3 @@ EMAIL_USE_TLS = True
EMAIL_HOST = 'golem'
EMAIL_HOST_USER = 'no-reply@readthedocs.com'
EMAIL_PORT = 25

View File

@ -11,6 +11,7 @@
<!-- css -->
<link rel="stylesheet" href="{{ MEDIA_URL }}css/core.css">
<link rel="stylesheet" href="{{ MEDIA_URL }}css/header.css">
<!-- js -->
@ -43,6 +44,20 @@
</div>
<!-- END content-->
<!-- BEGIN footer-->
<div id="footer">
<div class="wrapper">
<hr>
{% block footer-content %}
2010. Created by <a href="http://ericholscher.com/">Eric Holscher</a>, <a href="http://charlesleifer.com/">Charles Leifer</a>, and <a href="http://bobbygrace.info/">Bobby Grace</a> for the 2010 <a href="http://djangodash.com/">Django Dash</a>.
{% endblock %}
</div>
</div>
<!-- END footer-->
</body>
</html>

View File

@ -7,9 +7,9 @@ admin.autodiscover()
urlpatterns = patterns('',
url(r'^accounts/', include('registration.backends.default.urls')),
url(r'^dashboard/', include('projects.urls.private')),
url(r'^projects/(?P<username>\w+)/(?P<project_slug>[-\w]+)/(?P<filename>.*)$',
url(r'^projects/(?P<username>\w+)/(?P<project_slug>[-\w]+)/docs/(?P<filename>.*)$',
'core.views.serve_docs',
name='projects_detail'
name='docs_detail'
),
url(r'render_header/',
'core.views.render_header',

0
watching/__init__.py Normal file
View File

4
watching/admin.py Normal file
View File

@ -0,0 +1,4 @@
from watching.models import PageView
from django.contrib import admin
admin.site.register(PageView)

11
watching/models.py Normal file
View File

@ -0,0 +1,11 @@
from django.db import models
from projects.models import Project
class PageView(models.Model):
project = models.ForeignKey(Project, related_name='page_views')
url = models.CharField(max_length=255)
count = models.IntegerField(default=1)
def __unicode__(self):
return u"Page views for %s's url %s" % (self.project, self.url)

67
watching/tests.py Normal file
View File

@ -0,0 +1,67 @@
from django.conf import settings
from django.test import TestCase
from django.core.urlresolvers import reverse
import json
from projects.models import Conf
data = """
{
"before": "5aef35982fb2d34e9d9d4502f6ede1072793222d",
"repository": {
"url": "http://github.com/beetletweezers/tweezers",
"name": "github",
"description": "You're lookin' at it.",
"watchers": 5,
"forks": 2,
"private": 1,
"owner": {
"email": "chris@ozmm.org",
"name": "defunkt"
}
},
"commits": [
{
"id": "41a212ee83ca127e3c8cf465891ab7216a705f59",
"url": "http://github.com/defunkt/github/commit/41a212ee83ca127e3c8cf465891ab7216a705f59",
"author": {
"email": "chris@ozmm.org",
"name": "Chris Wanstrath"
},
"message": "okay i give in",
"timestamp": "2008-02-15T14:57:17-08:00",
"added": ["filepath.rb"]
},
{
"id": "de8251ff97ee194a289832576287d6f8ad74e3d0",
"url": "http://github.com/defunkt/github/commit/de8251ff97ee194a289832576287d6f8ad74e3d0",
"author": {
"email": "chris@ozmm.org",
"name": "Chris Wanstrath"
},
"message": "update pricing a tad",
"timestamp": "2008-02-15T14:36:34-08:00"
}
],
"after": "de8251ff97ee194a289832576287d6f8ad74e3d0",
"ref": "refs/heads/master"
}
"""
class Basic(TestCase):
fixtures=['eric', 'test_data']
def setUp(self):
settings.CELERY_ALWAYS_EAGER = True
def tearDown(self):
settings.CELERY_ALWAYS_EAGER = False
def test_github(self):
resp = self.client.post('/github', {'payload': data})
self.assertEqual(Conf.objects.count(), 1)
conf = Conf.objects.all()[0]
self.assertEqual(conf.theme, 'default')
self.assertTrue(conf.path is not None)

28
watching/urls.py Normal file
View File

@ -0,0 +1,28 @@
from django.conf.urls.defaults import *
urlpatterns = patterns('watching.views',
url(r'^$',
'project_index',
name='projects_list'
),
url(r'^tags/$',
'tag_index',
name='project_tag_list',
),
url(r'^search/',
'search',
name='search',
),
url(r'^tags/(?P<tag>\w+)/$',
'project_index',
name='project_tag_detail',
),
url(r'^projects/(?P<username>\w+)/(?P<project_slug>[-\w]+)/$',
'project_detail',
name='projects_detail'
),
url(r'^(?P<username>\w+)/$',
'project_index',
name='projects_user_list'
),
)

33
watching/views.py Normal file
View File

@ -0,0 +1,33 @@
from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.decorators.csrf import csrf_view_exempt
from django.views.static import serve
import json
from projects.models import Project
from projects.tasks import update_docs
from projects.utils import find_file
@csrf_view_exempt
def github_build(request):
obj = json.loads(request.POST['payload'])
name = obj['repository']['name']
url = obj['repository']['url']
project = Project.objects.get(repo=url)
update_docs.delay(pk=project.pk)
return HttpResponse('Build Started')
def serve_docs(request, username, project_slug, filename):
proj = Project.objects.get(slug=project_slug, user__username=username)
if not filename:
filename = "index.html"
filename = filename.rstrip('/')
return serve(request, filename, proj.full_html_path)
def render_header(request):
return render_to_response('core/header.html', {},
context_instance=RequestContext(request))