Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

so much flake8

  • Loading branch information...
commit 1c0dfc5efa18dc1d0abec7cb0b04cff1ae9a0a1e 1 parent 825324c
@wraithan wraithan authored
Showing with 1,097 additions and 783 deletions.
  1. +15 −9 deploy/fab/fabfile.py
  2. +5 −1 deploy/fabfile.py
  3. +3 −3 docs/_ext/djangodocs.py
  4. +15 −12 docs/conf.py
  5. +13 −4 fabfile-development.py
  6. +28 −6 fabfile.py
  7. +1 −0  readthedocs/bookmarks/admin.py
  8. +6 −3 readthedocs/bookmarks/models.py
  9. +10 −8 readthedocs/bookmarks/urls.py
  10. +4 −0 readthedocs/bookmarks/views.py
  11. +1 −0  readthedocs/core/admin.py
  12. +1 −0  readthedocs/core/context_processors.py
  13. +12 −12 readthedocs/core/djangome_urls.py
  14. +6 −6 readthedocs/core/forms.py
  15. +2 −0  readthedocs/core/hacks.py
  16. +7 −11 readthedocs/core/management/commands/build_files.py
  17. +0 −2  readthedocs/core/management/commands/import_intersphinx.py
  18. +9 −10 readthedocs/core/management/commands/sync_builds.py
  19. +19 −23 readthedocs/core/management/commands/update_repos.py
  20. +6 −7 readthedocs/core/management/commands/update_versions.py
  21. +8 −5 readthedocs/core/middleware.py
  22. +12 −5 readthedocs/core/models.py
  23. +2 −1  readthedocs/core/search_sites.py
  24. +17 −14 readthedocs/core/subdomain_urls.py
  25. +8 −5 readthedocs/core/templatetags/core_tags.py
  26. +11 −6 readthedocs/core/underscore_middleware.py
  27. +9 −4 readthedocs/core/utils.py
  28. +93 −59 readthedocs/core/views.py
  29. +31 −18 readthedocs/djangome/views.py
  30. +5 −1 readthedocs/projects/admin.py
  31. +1 −1  readthedocs/projects/exceptions.py
  32. +2 −0  readthedocs/projects/feeds.py
  33. +7 −3 readthedocs/projects/filters.py
  34. +37 −15 readthedocs/projects/forms.py
  35. +152 −124 readthedocs/projects/models.py
  36. +12 −5 readthedocs/projects/search_indexes.py
  37. +117 −70 readthedocs/projects/tasks.py
  38. +4 −1 readthedocs/projects/templatetags/projects_tags.py
  39. +45 −44 readthedocs/projects/urls/private.py
  40. +23 −22 readthedocs/projects/urls/public.py
  41. +24 −10 readthedocs/projects/utils.py
  42. +52 −18 readthedocs/projects/views/private.py
  43. +19 −12 readthedocs/projects/views/public.py
  44. +15 −13 readthedocs/rtd_tests/fixtures/sample_repo/source/conf.py
  45. +11 −11 readthedocs/rtd_tests/tests/__init__.py
  46. +1 −0  readthedocs/rtd_tests/tests/base.py
  47. +49 −50 readthedocs/rtd_tests/tests/test_api.py
  48. +2 −2 readthedocs/rtd_tests/tests/test_backend.py
  49. +1 −0  readthedocs/rtd_tests/tests/test_hacks.py
  50. +10 −11 readthedocs/rtd_tests/tests/test_middlware.py
  51. +13 −10 readthedocs/rtd_tests/tests/test_post_commit_hooks.py
  52. +55 −56 readthedocs/rtd_tests/tests/test_privacy.py
  53. +29 −16 readthedocs/rtd_tests/tests/test_redirects.py
  54. +1 −0  readthedocs/rtd_tests/tests/test_urls.py
  55. +15 −19 readthedocs/rtd_tests/tests/view_tests.py
  56. +1 −1  readthedocs/settings/base.py
  57. +1 −1  readthedocs/tastyapi/__init__.py
  58. +8 −3 readthedocs/tastyapi/client.py
  59. +31 −30 readthedocs/urls.py
View
24 deploy/fab/fabfile.py
@@ -1,12 +1,13 @@
import os
-from fabric.api import *
+from fabric.api import cd, env, put, run, sudo
import fabtools
cwd = os.getcwd()
all_users = ['docs', 'builder']
required_dirs = ['checkouts', 'etc', 'run', 'log']
+
def all():
install_packages('build')
install_packages('web')
@@ -15,12 +16,14 @@ def all():
checkout('docs')
setup_env('docs')
+
def build():
install_packages('build')
users('docs')
checkout('docs')
setup_env('docs')
+
def web():
install_packages('web')
users('docs')
@@ -42,9 +45,9 @@ def install_packages(type):
sudo(
'apt-get install -y git-core python-dev '
'postgresql-client libpq-dev subversion graphviz '
- 'curl sqlite libxml2-dev libxslt-dev vim g++ python-numpy python-scipy '
- 'build-essential texlive-full libevent-dev libmysqlclient-dev '
- 'python-m2crypto'
+ 'curl sqlite libxml2-dev libxslt-dev vim g++ python-numpy '
+ 'python-scipy build-essential texlive-full libevent-dev '
+ 'libmysqlclient-dev python-m2crypto'
)
sudo('pip install -U mercurial')
if type == 'db':
@@ -53,7 +56,6 @@ def install_packages(type):
sudo('apt-get install -y nginx')
-
def users(user=None):
if user:
users = [user]
@@ -66,7 +68,8 @@ def users(user=None):
if not fabtools.files.is_file('%s/.ssh/authorized_keys' % home):
sudo('mkdir -p %s/.ssh' % home)
- put('keys/*.pub', '%s/.ssh/authorized_keys' % home, mode=700, use_sudo=True)
+ put('keys/*.pub', '%s/.ssh/authorized_keys' % home, mode=700,
+ use_sudo=True)
sudo('chown -R %s:%s %s' % (user, user, home))
sudo('chmod -R 700 %s' % home)
sudo('mkdir -p /var/build')
@@ -90,7 +93,8 @@ def checkout(user=None):
run('git clone git://github.com/rtfd/readthedocs.org.git')
if not fabtools.files.is_file('%s/bin/python' % home):
run('virtualenv %s' % home)
- run('%s/bin/pip install -U -r %s/checkouts/readthedocs.org/deploy_requirements.txt' % (home, home))
+ run(('%s/bin/pip install -U -r %s/checkouts/readthedocs.org/'
+ 'deploy_requirements.txt') % (home, home))
def setup_env(user=None):
@@ -102,10 +106,11 @@ def setup_env(user=None):
env.user = user
home = '/home/%s' % user
put('files/bash_profile', '%s/.bash_profile' % home)
- put('files/%s_supervisord.conf' % user, '%s/etc/supervisord.conf' % home)
- #put('files/%s_local_settings.py' % user, '%s/checkouts/readthedocs.org/readthedocs/settings/local_settings.py' % home)
+ put('files/%s_supervisord.conf' % user,
+ '%s/etc/supervisord.conf' % home)
run('%s/bin/pip install -U supervisor ipython gunicorn' % home)
+
def fix_perms(user=None):
if user:
users = [user]
@@ -116,6 +121,7 @@ def fix_perms(user=None):
home = '/home/%s' % user
sudo('chown -R %s:%s %s' % (user, user, home))
+
def setup_db():
env.user = "docs"
home = '/home/%s' % env.user
View
6 deploy/fabfile.py
@@ -11,7 +11,8 @@
def install_chef():
sudo('apt-get update', pty=True)
- sudo('apt-get install -y git-core libopenssl-ruby rubygems ruby ruby-dev', pty=True)
+ sudo('apt-get install -y git-core libopenssl-ruby rubygems ruby ruby-dev',
+ pty=True)
#sudo('gem install chef --no-ri --no-rdoc', pty=True)
sudo('gem install chef --no-ri --no-rdoc --version=0.10.2', pty=True)
@@ -19,15 +20,18 @@ def install_chef():
def sync_config():
local('rsync -av . %s@%s:/etc/chef' % (env.user, env.hosts[0]))
+
def update():
sync_config()
sudo('cd /etc/chef && %s' % env.chef_executable, pty=True)
+
def reload():
"Reload the server."
env.user = "docs"
run("kill -HUP `cat %s/gunicorn.pid`" % env.rundir, pty=True)
+
def restart():
"Restart (or just start) the server"
sudo('restart readthedocs-gunicorn', pty=True)
View
6 docs/_ext/djangodocs.py
@@ -1,6 +1,6 @@
def setup(app):
app.add_crossref_type(
- directivename = "setting",
- rolename = "setting",
- indextemplate = "pair: %s; setting",
+ directivename="setting",
+ rolename="setting",
+ indextemplate="pair: %s; setting",
)
View
27 docs/conf.py
@@ -1,18 +1,21 @@
# -*- coding: utf-8 -*-
#
-import sys, os
+import os
+import sys
+
sys.path.insert(0, os.path.abspath('../readthedocs'))
import settings.sqlite
from django.core.management import setup_environ
setup_environ(settings.sqlite)
+
sys.path.append(os.path.abspath('_ext'))
extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.intersphinx',
- 'sphinx_http_domain',
- 'djangodocs',
- ]
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.intersphinx',
+ 'sphinx_http_domain',
+ 'djangodocs',
+]
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
@@ -24,10 +27,10 @@
default_role = 'obj'
pygments_style = 'sphinx'
intersphinx_mapping = {
- 'python': ('http://python.readthedocs.org/en/latest/', None),
- 'django': ('http://django.readthedocs.org/en/latest/', None),
- 'sphinx': ('http://sphinx.readthedocs.org/en/latest/', None),
- }
+ 'python': ('http://python.readthedocs.org/en/latest/', None),
+ 'django': ('http://django.readthedocs.org/en/latest/', None),
+ 'sphinx': ('http://sphinx.readthedocs.org/en/latest/', None),
+}
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if on_rtd:
html_theme = 'default'
@@ -36,8 +39,8 @@
html_static_path = ['_static']
htmlhelp_basename = 'ReadTheDocsdoc'
latex_documents = [
- ('index', 'ReadTheDocs.tex', u'Read The Docs Documentation',
- u'Eric Holscher, Charlie Leifer, Bobby Grace', 'manual'),
+ ('index', 'ReadTheDocs.tex', u'Read The Docs Documentation',
+ u'Eric Holscher, Charlie Leifer, Bobby Grace', 'manual'),
]
man_pages = [
('index', 'read-the-docs', u'Read The Docs Documentation',
View
17 fabfile-development.py
@@ -1,4 +1,4 @@
-from fabric.api import *
+from fabric.api import cd, env, prefix, run, sudo, task
# Fill out USER and HOSTS configuration before running
env.user = ''
@@ -7,37 +7,45 @@
env.code_dir = '/home/%s/rtd/checkouts/readthedocs.org' % (env.user)
env.virtualenv = '/home/%s/rtd' % (env.user)
+
def install_prerequisites():
"""Install prerequisites."""
- sudo("apt-get -y install python-dev python-pip git redis-server texlive texlive-latex-extra")
+ sudo("apt-get -y install python-dev python-pip git redis-server texlive "
+ "texlive-latex-extra")
sudo("pip install virtualenv")
+
def create_virtualenv():
"""Create virtualenv."""
run("virtualenv --no-site-packages --distribute rtd")
+
def clone_repository():
"""Clone readthedocs repo"""
run("mkdir %s/checkouts" % (env.virtualenv))
with cd("%s/checkouts" % env.virtualenv):
run("git clone http://github.com/rtfd/readthedocs.org.git")
+
def pip_requirements():
"""Install pip requirements"""
with cd(env.code_dir):
with prefix("source %s/bin/activate" % (env.virtualenv)):
run("pip install -r pip_requirements.txt")
+
def build_db():
"""Build database"""
with prefix("source %s/bin/activate" % (env.virtualenv)):
run("%s/readthedocs/manage.py syncdb" % (env.code_dir))
+
def migrate_db():
"""Migrate database"""
with prefix("source %s/bin/activate" % (env.virtualenv)):
run("%s/readthedocs/manage.py migrate" % (env.code_dir))
+
def load_testprojects():
"""Load test data and update repos"""
with prefix("source %s/bin/activate" % (env.virtualenv)):
@@ -56,11 +64,12 @@ def install():
migrate_db()
load_testprojects()
+
@task
def clean():
"""Clean up everything to start over"""
sudo("rm -rf %s" % (env.virtualenv))
sudo("pip uninstall virtualenv")
- sudo("apt-get -y purge python-dev python-pip git redis-server texlive texlive-latex-extra")
+ sudo("apt-get -y purge python-dev python-pip git redis-server texlive "
+ "texlive-latex-extra")
sudo("apt-get -y autoremove --purge")
-
View
34 fabfile.py
@@ -1,23 +1,28 @@
-from fabric.api import *
+from fabric.api import cd, env, lcd, local, hosts, prompt, run
from fabric.decorators import runs_once
import time
env.runtime = 'production'
-env.hosts = ['chimera.readthedocs.com', 'bigbuild.readthedocs.com', 'asgard.readthedocs.com']
+env.hosts = ['chimera.readthedocs.com',
+ 'bigbuild.readthedocs.com',
+ 'asgard.readthedocs.com']
env.user = 'docs'
env.code_dir = '/home/docs/checkouts/readthedocs.org'
env.virtualenv = '/home/docs/'
env.rundir = '/home/docs/run'
+
@hosts(['chimera.readthedocs.com', 'asgard.readthedocs.com'])
def remove_project(project):
run('rm -rf %s/user_builds/%s' % (env.code_dir, project))
+
@hosts(['asgard.readthedocs.com'])
def nginx_logs():
env.user = "root"
run("tail -f /var/log/nginx/*.log")
+
@hosts(['localhost'])
def i18n():
with lcd('readthedocs'):
@@ -27,6 +32,7 @@ def i18n():
local('tx push -s')
local('./manage.py compilemessages')
+
def push():
"Push new code, but don't restart/reload."
local('git push origin master')
@@ -34,9 +40,12 @@ def push():
run('git fetch')
run('git reset --hard origin/master')
+
def update_requirements():
"Update requirements in the virtualenv."
- run("%s/bin/pip install -i http://simple.crate.io/ -r %s/deploy_requirements.txt" % (env.virtualenv, env.code_dir))
+ run(("%s/bin/pip install -i http://simple.crate.io/ -r "
+ "%s/deploy_requirements.txt") % (env.virtualenv, env.code_dir))
+
@hosts(['chimera.readthedocs.com'])
def migrate(project=None):
@@ -45,11 +54,13 @@ def migrate(project=None):
else:
run('django-admin.py migrate')
+
@hosts(['chimera.readthedocs.com', 'asgard.readthedocs.com'])
def static():
"Restart (or just start) the server"
run('django-admin.py collectstatic --noinput')
+
@hosts(['chimera.readthedocs.com', 'asgard.readthedocs.com'])
def restart():
"Restart (or just start) the server"
@@ -58,29 +69,34 @@ def restart():
#so it has time to reload
time.sleep(3)
+
@hosts(['chimera.readthedocs.com', 'asgard.readthedocs.com'])
def reload():
"Reload (or just start) the server"
run("supervisorctl update")
+
@hosts(['bigbuild.readthedocs.com'])
def celery():
"Restart (or just start) the server"
run("supervisorctl restart celery")
+
def pull():
"Pull new code"
with cd(env.code_dir):
run('git pull origin master')
+
@runs_once
def spider():
local('patu.py -d1 readthedocs.org')
+
def _aws_wrapper(f, *args, **kwargs):
"get AWS credentials if not defined"
#these are normally defined in ~/.fabricrc
- @hosts('run_once') #so fab doesn't go crazy
+ @hosts('run_once') # so fab doesn't go crazy
def wrapped(*args, **kwargs):
from boto.cloudfront.exception import CloudFrontServerError
from boto.cloudfront import CloudFrontConnection
@@ -96,6 +112,7 @@ def wrapped(*args, **kwargs):
print "Error: \n", e.error_message
return wrapped
+
@_aws_wrapper
def to_cdn(c, slug):
"Create a new Distribution object on CloudFront"
@@ -110,10 +127,11 @@ def to_cdn(c, slug):
enabled=True,
comment='Slug: ' + slug,
cnames=[slug + '.readthedocs.org']
- )
+ )
print "Created: " + d.domain_name + " for " + slug
list_cdn()
+
@_aws_wrapper
def list_cdn(c):
"List Distributions on CloudFront"
@@ -123,6 +141,7 @@ def list_cdn(c):
d.status[:4], d.origin.dns_name,
d.domain_name)
+
@_aws_wrapper
def disable_cdn(c, *args):
"Sets a Distribution entry to disabled. Required before deletion."
@@ -135,12 +154,13 @@ def disable_cdn(c, *args):
#fix is to comment out lines 347-352 in cloudfront/distribution.py
distro.get_distribution().disable()
+
@_aws_wrapper
def delete_cdn(c):
"Deletes all Distributions in the 'Disabled' state."
distributions = c.get_all_distributions()
for distro in distributions:
- if not distro.enabled and distro.status=="Deployed":
+ if not distro.enabled and distro.status == "Deployed":
print "Deleting", distro.origin.dns_name
distro.get_distribution().delete()
@@ -156,10 +176,12 @@ def full_deploy():
#restart()
#celery()
+
@hosts(['chimera.readthedocs.com'])
def uptime():
run('uptime')
+
@hosts(['chimera.readthedocs.com'])
def update_index():
run('django-admin.py update_index')
View
1  readthedocs/bookmarks/admin.py
@@ -4,6 +4,7 @@
from django.contrib import admin
from bookmarks.models import Bookmark
+
class BookmarkAdmin(admin.ModelAdmin):
list_display = ('project', 'date', 'url')
View
9 readthedocs/bookmarks/models.py
@@ -4,9 +4,12 @@
from projects.models import Project
+
class Bookmark(models.Model):
- project = models.ForeignKey(Project, verbose_name=_('Project'), related_name='bookmarks', null=True)
- user = models.ForeignKey(User, verbose_name=_('User'), related_name='bookmarks')
+ project = models.ForeignKey(Project, verbose_name=_('Project'),
+ related_name='bookmarks', null=True)
+ user = models.ForeignKey(User, verbose_name=_('User'),
+ related_name='bookmarks')
date = models.DateTimeField(_('Date'), auto_now_add=True)
url = models.CharField(_('URL'), max_length=255)
desc = models.TextField(_('Description'), null=True)
@@ -15,7 +18,7 @@ class Meta:
ordering = ['-date']
def __unicode__(self):
- return ugettext(u"Bookmark %(url)s for %(user)s (%(pk)s)") %{
+ return ugettext(u"Bookmark %(url)s for %(user)s (%(pk)s)") % {
'url': self.url,
'user': self.user,
'pk': self.pk,
View
18 readthedocs/bookmarks/urls.py
@@ -1,16 +1,18 @@
-from django.conf.urls.defaults import *
+from django.conf.urls.defaults import patterns, url
-urlpatterns = patterns('bookmarks.views',
+
+urlpatterns = patterns(
+ # base view, flake8 complains if it is on the previous line.
+ 'bookmarks.views',
url(r'^$',
'bookmark_list',
- name='bookmarks_list'
- ),
+ name='bookmarks_list'),
+
url(r'^add/(?P<url>.*)/$',
'bookmark_add',
- name='bookmarks_add'
- ),
+ name='bookmarks_add'),
+
url(r'^remove/(?P<url>.*)/$',
'bookmark_remove',
- name='bookmarks_remove'
- ),
+ name='bookmarks_remove'),
)
View
4 readthedocs/bookmarks/views.py
@@ -6,6 +6,7 @@
from bookmarks.models import Bookmark
+
def bookmark_list(request, queryset=Bookmark.objects.all()):
return object_list(
request,
@@ -13,6 +14,7 @@ def bookmark_list(request, queryset=Bookmark.objects.all()):
template_object_name='bookmark',
)
+
@login_required
def user_bookmark_list(request):
"""Show a list of the current user's bookmarks.
@@ -21,6 +23,7 @@ def user_bookmark_list(request):
queryset = queryset.filter(user=request.user)
return bookmark_list(request, queryset=queryset)
+
@login_required
def bookmark_add(request, url):
"""Add a new bookmark for the current user to ``url``.
@@ -31,6 +34,7 @@ def bookmark_add(request, url):
return HttpResponse(payload, mimetype='text/javascript')
+
@login_required
def bookmark_remove(request, url):
"""Remove the current user's bookmark to ``url``.
View
1  readthedocs/core/admin.py
@@ -5,6 +5,7 @@
from core.models import UserProfile
+
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('user', 'homepage')
search_fields = ('user__username', 'homepage')
View
1  readthedocs/core/context_processors.py
@@ -1,5 +1,6 @@
from django.conf import settings
+
def readthedocs_processor(request):
exports = {
'PRODUCTION_DOMAIN': getattr(settings, 'PRODUCTION_DOMAIN', None),
View
24 readthedocs/core/djangome_urls.py
@@ -1,29 +1,29 @@
-from django.conf.urls.defaults import *
+from django.conf.urls.defaults import patterns, url
from urls import urlpatterns as main_patterns
ALL_VERSIONS_RE = '(?P<version>.+)'
-urlpatterns = patterns('',
+urlpatterns = patterns(
+ '', # base view, flake8 complains if it is on the previous line.
url('^$',
'djangome.views.redirect_home',
- {'version': 'latest'},
- ),
+ {'version': 'latest'}),
+
url('^(?P<term>[\w\-\.]+)$',
'djangome.views.redirect_to_term',
- {'version': 'latest'},
- ),
+ {'version': 'latest'}),
+
url('^(?P<term>[\w\-\.]+)/stats$',
'djangome.views.show_term',
- {'version': 'latest'},
- ),
+ {'version': 'latest'}),
+
url('^%s/(?P<term>[\w\-\.]+)$' % ALL_VERSIONS_RE,
'djangome.views.redirect_to_term',
- name = 'redirect_to_term'
- ),
+ name='redirect_to_term'),
+
url('^%s/(?P<term>[\w\-\.]+)/stats$' % ALL_VERSIONS_RE,
'djangome.views.show_term',
- name = 'show_term'
- ),
+ name='show_term'),
)
urlpatterns += main_patterns
View
12 readthedocs/core/forms.py
@@ -1,6 +1,6 @@
import logging
from haystack.forms import SearchForm
-from haystack.query import SearchQuerySet
+from haystack.query import SearchQuerySet
from django import forms
from django.forms.fields import CharField
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
+
class UserProfileForm(forms.ModelForm):
first_name = CharField(label=_('First name'), required=False)
last_name = CharField(label=_('Last name'), required=False)
@@ -36,7 +37,7 @@ def save(self, *args, **kwargs):
user.last_name = last_name
user.save()
return profile
-
+
class FacetField(forms.MultipleChoiceField):
'''
@@ -47,7 +48,7 @@ def valid_value(self, value):
'''
Although this is a choice field, no choices need to be supplied.
Instead, we just validate that the value is in the correct format
- for facet filtering (facet_name:value)
+ for facet filtering (facet_name:value)
'''
if ":" not in value:
return False
@@ -82,16 +83,15 @@ def clean_selected_facets(self):
clean = SearchQuerySet().query.clean
for facet in facets:
field, value = facet.split(":", 1)
- if not value: # Ignore empty values
+ if not value: # Ignore empty values
continue
value = clean(value)
cleaned_facets.append(u'%s:"%s"' % (field, value))
return cleaned_facets
-
+
def search(self):
sqs = super(FacetedSearchForm, self).search()
for facet in self.cleaned_data['selected_facets']:
sqs = sqs.narrow(facet)
self.searchqueryset = sqs
return sqs
-
View
2  readthedocs/core/hacks.py
@@ -1,6 +1,7 @@
import imp
import sys
+
class ErrorlessImport(object):
def find_module(self, name, path):
try:
@@ -33,6 +34,7 @@ def patch_meta_path():
FreeLoader._class = ErrorlessImport()
sys.meta_path += [FreeLoader._class]
+
def unpatch_meta_path():
sys.meta_path.remove(FreeLoader._class)
#sys.meta_path = []
View
18 readthedocs/core/management/commands/build_files.py
@@ -4,29 +4,25 @@
from django.core.management.base import BaseCommand
from django.conf import settings
-
from projects import tasks
from builds.models import Version
log = logging.getLogger(__name__)
+
class Command(BaseCommand):
- help = '''\
-Delete and re-create ImportedFile objects for all latest Versions, such
-that they can be added to the search index. This is accomplished by walking the
-filesystem for each project.
-'''
+ help = ('Delete and re-create ImportedFile objects for all latest '
+ 'Versions, such that they can be added to the search index. This '
+ 'is accomplished by walking the filesystem for each project.')
option_list = BaseCommand.option_list + (
make_option('-p',
- dest='project',
- default='',
- help='Project to index'
- ),
+ dest='project',
+ default='',
+ help='Project to index'),
)
-
def handle(self, *args, **options):
'''
Build/index all versions or a single project's version
View
2  readthedocs/core/management/commands/import_intersphinx.py
@@ -8,5 +8,3 @@ class Command(BaseCommand):
def handle(self, *args, **options):
for version in Version.objects.filter(slug="latest"):
update_intersphinx(version.pk)
-
-
View
19 readthedocs/core/management/commands/sync_builds.py
@@ -7,6 +7,7 @@
log = logging.getLogger(__name__)
+
class Command(BaseCommand):
"""Custom management command to rebuild documentation for all projects on
the site. Invoked via ``./manage.py update_repos``.
@@ -14,17 +15,15 @@ class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('-V',
- dest='version',
- default=None,
- help='Build a version, or all versions'
- ),
+ dest='version',
+ default=None,
+ help='Build a version, or all versions'),
make_option('-c',
- action='store_true',
- dest='checkout',
- default=False,
- help='sync checkouts'
- ),
- )
+ action='store_true',
+ dest='checkout',
+ default=False,
+ help='sync checkouts'),
+ )
def handle(self, *args, **options):
version = options['version']
View
42 readthedocs/core/management/commands/update_repos.py
@@ -1,13 +1,14 @@
import logging
+from optparse import make_option
from django.core.management.base import BaseCommand
-from optparse import make_option
from projects import tasks
from projects.models import Project
from builds.models import Version
log = logging.getLogger(__name__)
+
class Command(BaseCommand):
"""Custom management command to rebuild documentation for all projects on
the site. Invoked via ``./manage.py update_repos``.
@@ -15,29 +16,25 @@ class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('-p',
- action='store_true',
- dest='pdf',
- default=False,
- help='Make a pdf'
- ),
+ action='store_true',
+ dest='pdf',
+ default=False,
+ help='Make a pdf'),
make_option('-r',
- action='store_true',
- dest='record',
- default=False,
- help='Make a Build'
- ),
+ action='store_true',
+ dest='record',
+ default=False,
+ help='Make a Build'),
make_option('-f',
- action='store_true',
- dest='force',
- default=False,
- help='Force a build in sphinx'
- ),
+ action='store_true',
+ dest='force',
+ default=False,
+ help='Force a build in sphinx'),
make_option('-V',
- dest='version',
- default=None,
- help='Build a version, or all versions'
- )
- )
+ dest='version',
+ default=None,
+ help='Build a version, or all versions')
+ )
def handle(self, *args, **options):
make_pdf = options['pdf']
@@ -82,8 +79,7 @@ def handle(self, *args, **options):
tasks.update_docs_pull(pdf=make_pdf,
record=record,
force=force)
-
+
@property
def help(self):
return Command.__doc__
-
View
13 readthedocs/core/management/commands/update_versions.py
@@ -4,21 +4,20 @@
from projects.tasks import update_docs
-
class Command(BaseCommand):
"""Custom management command to rebuild documentation for all projects on
the site. Invoked via ``./manage.py update_repos``.
"""
option_list = BaseCommand.option_list + (
make_option('-p',
- action='store_true',
- dest='pdf',
- default=False,
- help='Make a pdf'
- ),
+ action='store_true',
+ dest='pdf',
+ default=False,
+ help='Make a pdf'),
)
def handle(self, *args, **options):
make_pdf = options['pdf']
for version in Version.objects.filter(active=True, built=False):
- update_docs(version.project_id, pdf=make_pdf, record=False, version_pk=version.pk)
+ update_docs(version.project_id, pdf=make_pdf, record=False,
+ version_pk=version.pk)
View
13 readthedocs/core/middleware.py
@@ -5,6 +5,7 @@
import redis
+
class SubdomainMiddleware(object):
def process_request(self, request):
if settings.DEBUG:
@@ -20,20 +21,22 @@ def process_request(self, request):
if len(domain_parts) == 3:
subdomain = domain_parts[0]
# Serve subdomains
- if not (subdomain.lower() == 'www') and not (subdomain.lower() == 'ssl') and 'readthedocs.org' in host:
+ is_www = subdomain.lower() == 'www'
+ is_ssl = subdomain.lower() == 'ssl'
+ if not is_www and not is_ssl and 'readthedocs.org' in host:
request.subdomain = True
request.slug = subdomain
request.urlconf = 'core.subdomain_urls'
return None
# Serve rtfd.org
- elif not (subdomain.lower() == 'www') and not (subdomain.lower() == 'ssl') and 'rtfd.org' in host:
+ elif not is_www and not is_ssl and 'rtfd.org' in host:
request.slug = subdomain
request.urlconf = 'core.djangome_urls'
return None
# Serve CNAMEs
- if 'readthedocs.org' not in host \
- and 'localhost' not in host \
- and 'testserver' not in host:
+ if 'readthedocs.org' not in host and \
+ 'localhost' not in host and \
+ 'testserver' not in host:
request.cname = True
try:
slug = cache.get(host)
View
17 readthedocs/core/models.py
@@ -7,24 +7,31 @@
STANDARD_EMAIL = "anonymous@readthedocs.org"
+
class UserProfile (models.Model):
"""Additional information about a User.
"""
- user = models.ForeignKey(User, verbose_name=_('User'), unique=True, related_name='profile')
+ user = models.ForeignKey(User, verbose_name=_('User'), unique=True,
+ related_name='profile')
whitelisted = models.BooleanField(_('Whitelisted'))
homepage = models.CharField(_('Homepage'), max_length=100, blank=True)
- allow_email = models.BooleanField(_('Allow email'), help_text=_('Show your email on VCS contributions.'), default=True)
+ allow_email = models.BooleanField(_('Allow email'),
+ help_text=_('Show your email on VCS '
+ 'contributions.'),
+ default=True)
def __unicode__(self):
- return ugettext("%(username)s's profile") % {'username': self.user.username}
+ return (ugettext("%(username)s's profile")
+ % {'username': self.user.username})
def get_form(self):
from .forms import UserProfileForm
-
return UserProfileForm(instance=self)
def get_absolute_url(self):
- return ('profiles_profile_detail', (), {'username': self.user.username})
+ return ('profiles_profile_detail', (),
+ {'username': self.user.username})
+
get_absolute_url = models.permalink(get_absolute_url)
def get_contribution_details(self):
View
3  readthedocs/core/search_sites.py
@@ -1,3 +1,4 @@
import haystack
-haystack.autodiscover()
+
+haystack.autodiscover()
View
31 readthedocs/core/subdomain_urls.py
@@ -2,34 +2,37 @@
from urls import urlpatterns as main_patterns
-urlpatterns = patterns('',
- url(r'^projects/(?P<project_slug>[\w.-]+)/(?P<lang_slug>\w{2})/(?P<version_slug>[\w.-]+)/(?P<filename>.*)$',
+
+urlpatterns = patterns(
+ '', # base view, flake8 complains if it is on the previous line.
+ url((r'^projects/(?P<project_slug>[\w.-]+)/(?P<lang_slug>\w{2})/'
+ r'(?P<version_slug>[\w.-]+)/(?P<filename>.*)$'),
'core.views.subproject_serve_docs',
- name='subproject_docs_detail'
- ),
+ name='subproject_docs_detail'),
+
url(r'^projects/(?P<project_slug>[\w.-]+)',
'core.views.subproject_serve_docs',
- name='subproject_docs_detail'
- ),
+ name='subproject_docs_detail'),
+
url(r'^(?P<lang_slug>\w{2})/(?P<version_slug>[\w.-]+)/(?P<filename>.*)$',
'core.views.serve_docs',
- name='docs_detail'
- ),
+ name='docs_detail'),
+
url(r'^(?P<lang_slug>\w{2})/(?P<version_slug>.*)/$',
'core.views.serve_docs',
{'filename': 'index.html'},
- name='docs_detail'
- ),
+ name='docs_detail'),
+
url(r'^page/(?P<filename>.*)$',
'core.views.subdomain_handler',
{'version_slug': None,
'lang_slug': None},
- name='docs_detail'
- ),
+ name='docs_detail'),
+
url(r'^(?P<version_slug>.*)/$',
'core.views.subdomain_handler',
- name='version_subdomain_handler'
- ),
+ name='version_subdomain_handler'),
+
url(r'^$', 'core.views.subdomain_handler'),
)
View
13 readthedocs/core/templatetags/core_tags.py
@@ -5,14 +5,17 @@
register = template.Library()
+
@register.filter
def gravatar(email, size=48):
- """
- hacked from djangosnippets.org, but basically given an email address
- render an img tag with the hashed up bits needed for leetness omgwtfstillreading
+ """hacked from djangosnippets.org, but basically given an email address
+ render an img tag with the hashed up bits needed for leetness
+ omgwtfstillreading
+
"""
url = "http://www.gravatar.com/avatar.php?%s" % urllib.urlencode({
- 'gravatar_id': hashlib.md5(email).hexdigest(),
+ 'gravatar_id': hashlib.md5(email).hexdigest(),
'size': str(size)
})
- return '<img src="%s" width="%s" height="%s" alt="gravatar" class="gravatar" border="0" />' % (url, size, size)
+ return ('<img src="%s" width="%s" height="%s" alt="gravatar" '
+ 'class="gravatar" border="0" />' % (url, size, size))
View
17 readthedocs/core/underscore_middleware.py
@@ -1,3 +1,5 @@
+# We aren't serving - domains properly ATM, so turn this off.
+"""
from django.conf import settings
from django.http import HttpResponsePermanentRedirect, SuspiciousOperation
from django.utils.http import urlquote
@@ -5,23 +7,26 @@
import re
from django import http
-host_validation_re = re.compile(r"^([a-z0-9_.-]+|\[[a-f0-9]*:[a-f0-9:]+\])(:\d+)?$")
-#host_validation_re = re.compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9:]+\])(:\d+)?$")
+host_validation_re = re.compile((r"^([a-z0-9_.-]+|\[[a-f0-9]*:[a-f0-9:]+\])'
+ r'(:\d+)?$"))
+#host_validation_re = re.compile((r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9:]+\])'
+ r'(:\d+)?$"))
http.host_validation_re = host_validation_re
class UnderscoreMiddleware(object):
def process_request(self, request):
# Handle redirect of domains with _ in them.
host = request.get_host()
- # We aren't serving - domains properly ATM, so turn this off.
- """
if '_' in host:
host = host.replace('_', '-')
new_uri = '%s://%s%s%s' % (
request.is_secure() and 'https' or 'http',
host,
urlquote(request.path),
- (request.method == 'GET' and len(request.GET) > 0) and '?%s' % request.GET.urlencode() or ''
+ (request.method == 'GET'
+ and len(request.GET) > 0)
+ and '?%s' % request.GET.urlencode()
+ or ''
)
return HttpResponsePermanentRedirect(new_uri)
- """
+"""
View
13 readthedocs/core/utils.py
@@ -1,12 +1,14 @@
-import logging
import getpass
+import logging
import os
+
from django.conf import settings
log = logging.getLogger(__name__)
SYNC_USER = getattr(settings, 'SYNC_USER', getpass.getuser())
+
def copy_to_app_servers(full_build_path, target, mkdir=True):
"""
A helper to copy a directory across app servers
@@ -19,8 +21,8 @@ def copy_to_app_servers(full_build_path, target, mkdir=True):
log.error("COPY ERROR to app servers:")
log.error(mkdir_cmd)
- sync_cmd = ("rsync -e 'ssh -T' -av --delete %s/ %s@%s:%s" %
- (full_build_path, SYNC_USER, server, target))
+ sync_cmd = ("rsync -e 'ssh -T' -av --delete %s/ %s@%s:%s"
+ % (full_build_path, SYNC_USER, server, target))
ret = os.system(sync_cmd)
if ret != 0:
log.error("COPY ERROR to app servers.")
@@ -40,7 +42,10 @@ def copy_file_to_app_servers(from_file, to_file):
log.error("COPY ERROR to app servers.")
log.error(mkdir_cmd)
- sync_cmd = ("rsync -e 'ssh -T' -av --delete %s %s@%s:%s" % (from_file, SYNC_USER, server, to_file))
+ sync_cmd = ("rsync -e 'ssh -T' -av --delete %s %s@%s:%s" % (from_file,
+ SYNC_USER,
+ server,
+ to_file))
ret = os.system(sync_cmd)
if ret != 0:
log.error("COPY ERROR to app servers.")
View
152 readthedocs/core/views.py
@@ -1,5 +1,6 @@
"""Core views, including the main homepage, post-commit build hook,
documentation and header rendering, and server errors.
+
"""
from django.core.urlresolvers import NoReverseMatch, reverse
@@ -32,27 +33,31 @@
log = logging.getLogger(__name__)
+
def homepage(request):
- #latest_projects = Project.objects.filter(builds__isnull=False).annotate(max_date=Max('builds__date')).order_by('-max_date')[:10]
- latest = Project.objects.public(request.user).order_by('-modified_date')[:10]
+ latest = (Project.objects.public(request.user)
+ .order_by('-modified_date')[:10])
featured = Project.objects.filter(featured=True)
return render_to_response('homepage.html',
{'project_list': latest,
- 'featured_list': featured,
- #'updated_list': updated
- },
- context_instance=RequestContext(request))
+ 'featured_list': featured},
+ context_instance=RequestContext(request))
+
def random_page(request, project=None):
+ imp_file = ImportedFile.objects.order_by('?')
if project:
- return HttpResponseRedirect(ImportedFile.objects.filter(project__slug=project).order_by('?')[0].get_absolute_url())
- return HttpResponseRedirect(ImportedFile.objects.order_by('?')[0].get_absolute_url())
+ return HttpResponseRedirect((imp_file.filter(project__slug=project)[0]
+ .get_absolute_url()))
+ return HttpResponseRedirect(imp_file[0].get_absolute_url())
+
def queue_depth(request):
r = redis.Redis(**settings.REDIS)
return HttpResponse(r.llen('celery'))
+
def live_builds(request):
builds = Build.objects.filter(state='building')[:5]
WEBSOCKET_HOST = getattr(settings, 'WEBSOCKET_HOST', 'localhost:8088')
@@ -64,25 +69,26 @@ def live_builds(request):
{'builds': builds,
'build_percent': percent,
'WEBSOCKET_HOST': WEBSOCKET_HOST},
- context_instance=RequestContext(request))
+ context_instance=RequestContext(request))
+
@csrf_view_exempt
def wipe_version(request, project_slug, version_slug):
- version = get_object_or_404(Version, project__slug=project_slug, slug=version_slug)
+ version = get_object_or_404(Version, project__slug=project_slug,
+ slug=version_slug)
if request.user not in version.project.users.all():
raise Http404("You must own this project to wipe it.")
del_dir = version.project.checkout_path(version.slug)
if del_dir:
remove_dir.delay(del_dir)
- return render_to_response('wipe_version.html', {
- 'del_dir': del_dir,
- 'deleted': True,
- },
- context_instance=RequestContext(request))
- return render_to_response('wipe_version.html', {
- 'del_dir': del_dir,
- },
- context_instance=RequestContext(request))
+ return render_to_response('wipe_version.html',
+ {'del_dir': del_dir,
+ 'deleted': True},
+ context_instance=RequestContext(request))
+ return render_to_response('wipe_version.html',
+ {'del_dir': del_dir},
+ context_instance=RequestContext(request))
+
@csrf_view_exempt
def github_build(request):
@@ -103,28 +109,37 @@ def github_build(request):
for project in projects:
version = project.version_from_branch_name(branch)
if version:
- log.info("(Github Build) Processing %s:%s" % (project.slug, version.slug))
- default = project.default_branch or project.vcs_repo().fallback_branch
+ log.info(("(Github Build) Processing %s:%s"
+ % (project.slug, version.slug)))
+ default = project.default_branch or (project.vcs_repo()
+ .fallback_branch)
if branch == default:
- #Shortcircuit versions that are default
- #These will build at "latest", and thus won't be active
+ # Short circuit versions that are default
+ # These will build at "latest", and thus won't be
+ # active
version = project.versions.get(slug='latest')
version_pk = version.pk
version_slug = version.slug
- log.info("(Github Build) Building %s:%s" % (project.slug, version.slug))
+ log.info(("(Github Build) Building %s:%s"
+ % (project.slug, version.slug)))
elif version in project.versions.exclude(active=True):
- log.info("(Github Build) Not building %s" % version.slug)
- return HttpResponseNotFound('Not Building: %s' % branch)
+ log.info(("(Github Build) Not building %s"
+ % version.slug))
+ return HttpResponseNotFound(('Not Building: %s'
+ % branch))
else:
version_pk = version.pk
version_slug = version.slug
- log.info("(Github Build) Building %s:%s" % (project.slug, version.slug))
+ log.info(("(Github Build) Building %s:%s"
+ % (project.slug, version.slug)))
else:
version_slug = 'latest'
branch = 'latest'
- log.info("(Github Build) Building %s:latest" % project.slug)
- #version_pk being None means it will use "latest"
- update_docs.delay(pk=project.pk, version_pk=version_pk, force=True)
+ log.info(("(Github Build) Building %s:latest"
+ % project.slug))
+ # version_pk being None means it will use "latest"
+ update_docs.delay(pk=project.pk, version_pk=version_pk,
+ force=True)
return HttpResponse('Build Started: %s' % version_slug)
except Exception, e:
log.error("(Github Build) Failed: %s:%s" % (name, e))
@@ -158,6 +173,7 @@ def github_build(request):
else:
return redirect('builds_project_list', project.slug)
+
@csrf_view_exempt
def bitbucket_build(request):
if request.method == 'POST':
@@ -176,6 +192,7 @@ def bitbucket_build(request):
else:
return redirect('builds_project_list', project.slug)
+
@csrf_view_exempt
def generic_build(request, pk):
project = Project.objects.get(pk=pk)
@@ -188,22 +205,24 @@ def generic_build(request, pk):
update_docs.delay(pk=pk, version_pk=version.pk, force=True)
else:
update_docs.delay(pk=pk, force=True)
- #return HttpResponse('Build Started')
+ # return HttpResponse('Build Started')
return redirect('builds_project_list', project.slug)
return redirect('builds_project_list', project.slug)
+
def subdomain_handler(request, lang_slug=None, version_slug=None, filename=''):
- """
- This provides the fall-back routing for subdomain requests.
+ """This provides the fall-back routing for subdomain requests.
+
+ This was made primarily to redirect old subdomain's to their version'd
+ brothers.
- This was made primarily to redirect old subdomain's to their version'd brothers.
"""
project = get_object_or_404(Project, slug=request.slug)
# Don't add index.html for htmldir.
if not filename and project.documentation_type != 'sphinx_htmldir':
filename = "index.html"
if version_slug is None:
- #Handle / on subdomain.
+ # Handle / on subdomain.
default_version = project.get_default_version()
url = reverse(serve_docs, kwargs={
'version_slug': default_version,
@@ -212,12 +231,13 @@ def subdomain_handler(request, lang_slug=None, version_slug=None, filename=''):
})
return HttpResponseRedirect(url)
if version_slug and lang_slug is None:
- #Handle /version/ on subdomain.
+ # Handle /version/ on subdomain.
aliases = project.aliases.filter(from_slug=version_slug)
- #Handle Aliases.
+ # Handle Aliases.
if aliases.count():
if aliases[0].largest:
- highest_ver = highest_version(project.versions.filter(slug__contains=version_slug, active=True))
+ highest_ver = highest_version(project.versions.filter(
+ slug__contains=version_slug, active=True))
version_slug = highest_ver[0].slug
else:
version_slug = aliases[0].to_slug
@@ -244,11 +264,13 @@ def subdomain_handler(request, lang_slug=None, version_slug=None, filename=''):
filename=filename)
-def subproject_serve_docs(request, project_slug, lang_slug=None, version_slug=None, filename=''):
+def subproject_serve_docs(request, project_slug, lang_slug=None,
+ version_slug=None, filename=''):
parent_slug = request.slug
proj = get_object_or_404(Project, slug=project_slug)
- subproject_qs = ProjectRelationship.objects.filter(parent__slug=parent_slug, child__slug=project_slug)
- if lang_slug == None or version_slug == None:
+ subproject_qs = ProjectRelationship.objects.filter(
+ parent__slug=parent_slug, child__slug=project_slug)
+ if lang_slug is None or version_slug is None:
# Handle /
version_slug = proj.get_default_version()
url = reverse('subproject_docs_detail', kwargs={
@@ -260,11 +282,14 @@ def subproject_serve_docs(request, project_slug, lang_slug=None, version_slug=No
return HttpResponseRedirect(url)
if subproject_qs.exists():
- return serve_docs(request, lang_slug, version_slug, filename, project_slug)
+ return serve_docs(request, lang_slug, version_slug, filename,
+ project_slug)
else:
- log.info('Subproject lookup failed: %s:%s' % (project_slug, parent_slug))
+ log.info('Subproject lookup failed: %s:%s' % (project_slug,
+ parent_slug))
raise Http404("Subproject does not exist")
+
def serve_docs(request, lang_slug, version_slug, filename, project_slug=None):
if not project_slug:
project_slug = request.slug
@@ -281,7 +306,8 @@ def serve_docs(request, lang_slug, version_slug, filename, project_slug=None):
})
return HttpResponseRedirect(url)
- ver = get_object_or_404(Version, project__slug=project_slug, slug=version_slug)
+ ver = get_object_or_404(Version, project__slug=project_slug,
+ slug=version_slug)
# Auth checks
if ver not in proj.versions.public(request.user, proj):
res = HttpResponse("You don't have access to this version.")
@@ -292,8 +318,13 @@ def serve_docs(request, lang_slug, version_slug, filename, project_slug=None):
if not filename:
filename = "index.html"
- #This is required because we're forming the filenames outselves instead of letting the web server do it.
- elif proj.documentation_type == 'sphinx_htmldir' and "_static" not in filename and "_images" not in filename and "html" not in filename and not "inv" in filename:
+ # This is required because we're forming the filenames outselves instead of
+ # letting the web server do it.
+ elif (proj.documentation_type == 'sphinx_htmldir'
+ and "_static" not in filename
+ and "_images" not in filename
+ and "html" not in filename
+ and not "inv" in filename):
filename += "index.html"
else:
filename = filename.rstrip('/')
@@ -315,10 +346,9 @@ def serve_docs(request, lang_slug, version_slug, filename, project_slug=None):
response["Content-Encoding"] = encoding
try:
response['X-Accel-Redirect'] = os.path.join('/user_builds',
- proj.slug,
- 'rtd-builds',
- version_slug,
- filename)
+ proj.slug,
+ 'rtd-builds',
+ version_slug, filename)
except UnicodeEncodeError:
raise Http404
@@ -326,33 +356,35 @@ def serve_docs(request, lang_slug, version_slug, filename, project_slug=None):
else:
return serve(request, filename, basepath)
+
def server_error(request, template_name='500.html'):
"""
A simple 500 handler so we get media
"""
r = render_to_response(template_name,
- context_instance = RequestContext(request)
- )
+ context_instance=RequestContext(request))
r.status_code = 500
return r
+
def server_error_404(request, template_name='404.html'):
"""
A simple 500 handler so we get media
"""
- r = render_to_response(template_name,
- context_instance = RequestContext(request)
- )
+ r = render_to_response(template_name,
+ context_instance=RequestContext(request))
r.status_code = 404
return r
+
def divide_by_zero(request):
return 1 / 0
+
def morelikethis(request, project_slug, filename):
project = get_object_or_404(Project, slug=project_slug)
file = get_object_or_404(ImportedFile, project=project, path=filename)
- #sqs = SearchQuerySet().filter(project=project).more_like_this(file)[:5]
+ # sqs = SearchQuerySet().filter(project=project).more_like_this(file)[:5]
sqs = SearchQuerySet().more_like_this(file)[:5]
if len(sqs):
output = [(obj.title, obj.absolute_url) for obj in sqs]
@@ -376,13 +408,15 @@ class SearchView(TemplateView):
def get_context_data(self, request, **kwargs):
context = super(SearchView, self).get_context_data(**kwargs)
context['request'] = self.request
- context['facets'] = self.results.facet_counts() # causes solr request #1
+ # causes solr request #1
+ context['facets'] = self.results.facet_counts()
context['form'] = self.form
context['query'] = self.query
- context['selected_facets'] = '&'.join(self.selected_facets) if self.selected_facets else ''
+ context['selected_facets'] = ('&'.join(self.selected_facets)
+ if self.selected_facets else '')
context['selected_facets_list'] = self.selected_facets_list
context['results'] = self.results
- context['count'] = len(self.results) # causes solr request #2
+ context['count'] = len(self.results) # causes solr request #2
return context
def get(self, request, **kwargs):
View
49 readthedocs/djangome/views.py
@@ -12,6 +12,7 @@
r = redis.Redis(**settings.REDIS)
+
class RedirectForm(forms.Form):
_domain = 'readthedocs.org'
@@ -21,11 +22,15 @@ class RedirectForm(forms.Form):
def clean_url(self):
url = urlparse.urlparse(self.cleaned_data['url'])
if self._domain not in url.netloc:
- raise forms.ValidationError(_('Please enter a valid URL on %s') % self._domain)
+ raise forms.ValidationError(_('Please enter a valid URL on %s') %
+ self._domain)
return self.cleaned_data['url']
+
def redirect_home(request, version):
- return http.HttpResponseRedirect('http://%s.readthedocs.org' % request.slug)
+ return http.HttpResponseRedirect('http://%s.readthedocs.org'
+ % request.slug)
+
def redirect_to_term(request, version, term):
form = RedirectForm(request.GET or None)
@@ -37,10 +42,13 @@ def redirect_to_term(request, version, term):
# make sure this service can't be used for spam.
if 'url' in request.GET:
if form.is_valid():
- # Make sure the new URL is in the set of URLs and increment its score.
+ # Make sure the new URL is in the set of URLs and increment its
+ # score.
url = form.cleaned_data['url']
- r.sadd('redirects:v4:%s:%s:%s:%s' % (lang, version, project, term), url)
- r.incr('redirects:v4:%s:%s:%s:%s:%s' % (lang, version, project, term, url))
+ r.sadd('redirects:v4:%s:%s:%s:%s' % (lang, version, project, term),
+ url)
+ r.incr('redirects:v4:%s:%s:%s:%s:%s' % (lang, version, project,
+ term, url))
return redirect(request.GET.get('return_to', url))
urls = get_urls(lang, project, version, term)
@@ -48,19 +56,20 @@ def redirect_to_term(request, version, term):
scoregroups = group_urls(urls)
# The first group is the URLs with the highest score.
- score, winners = scoregroups.next()
+ _, winners = scoregroups.next()
- # If there's only a single winning URL, we're done. Count the redirect and
- # then issue it.
+ # If there's only a single winning URL, we're done. Count the redirect
+ # and then issue it.
if len(winners) == 1:
url = winners[0]
- r.incr('redirects:v4:%s:%s:%s:%s:%s' % (lang, version, project, term, url))
+ r.incr('redirects:v4:%s:%s:%s:%s:%s' % (lang, version, project,
+ term, url))
return redirect(url)
- # Otherwise we need to display a list of all choices. We'll present this into
- # two buckets: the tied URLs with the high score (which is the list of winners
- # we've already gotten) and the tied URLs with a lower score. This second
- # bucket might be empty.
+ # Otherwise we need to display a list of all choices. We'll present
+ # this into two buckets: the tied URLs with the high score (which is
+ # the list of winners we've already gotten) and the tied URLs with a
+ # lower score. This second bucket might be empty.
losers = [losing_group for score, losing_group in scoregroups]
else:
@@ -74,6 +83,7 @@ def redirect_to_term(request, version, term):
'version': version,
})
+
def show_term(request, version, term):
return render(request, 'djangome/show.html', {
'djangome_term': term,
@@ -82,6 +92,7 @@ def show_term(request, version, term):
'can_edit': request.COOKIES.get('sekrit') == settings.SECRET_KEY,
})
+
def get_urls(lang, project, version, term):
"""
Gets the set of URLs for <term> in <version>.
@@ -92,15 +103,16 @@ def get_urls(lang, project, version, term):
# redirects:v1:term:url, then get each score along with each URL.
# This returns a list [score, url, score, url, ...]
urls = r.sort('redirects:v4:%s:%s:%s:%s' % (lang, version, project, term),
- by = 'redirects:v4:%s:%s:%s:%s:*' % (lang, version,
- project, term),
- get = ('redirects:v4:%s:%s:%s:%s:*' % (lang, version,
- project, term), '#'),
- desc = True)
+ by='redirects:v4:%s:%s:%s:%s:*' % (lang, version,
+ project, term),
+ get=('redirects:v4:%s:%s:%s:%s:*' % (lang, version,
+ project, term), '#'),
+ desc=True)
# Convert that to a list of tuples [(score, url), (score, url), ...]
return zip(urls[::2], urls[1::2])
+
def group_urls(urls):
"""
Given a list of (score, url) tuples, group them into buckets by score.
@@ -110,6 +122,7 @@ def group_urls(urls):
for (score, group) in itertools.groupby(urls, operator.itemgetter(0)):
yield (score, [url for score, url in group])
+
def firstof(list):
for i in list:
if i:
View
6 readthedocs/projects/admin.py
@@ -4,13 +4,16 @@
from builds.models import Version
from django.contrib import admin
-from projects.models import Project, ImportedFile, ProjectRelationship, EmailHook, WebHook
+from projects.models import (Project, ImportedFile, ProjectRelationship,
+ EmailHook, WebHook)
from guardian.admin import GuardedModelAdmin
+
class ProjectRelationshipInline(admin.TabularInline):
model = ProjectRelationship
fk_name = 'parent'
+
class VersionInline(admin.TabularInline):
model = Version
@@ -29,6 +32,7 @@ class ImportedFileAdmin(admin.ModelAdmin):
list_display = ('name', 'version')
list_filter = ('project',)
+
admin.site.register(Project, ProjectAdmin)
admin.site.register(ImportedFile, ImportedFileAdmin)
admin.site.register(EmailHook)
View
2  readthedocs/projects/exceptions.py
@@ -1,3 +1,3 @@
class ProjectImportError (Exception):
"""Failure to import a project from a repository."""
- pass
+ pass
View
2  readthedocs/projects/feeds.py
@@ -2,6 +2,7 @@
from projects.models import Project
+
class LatestProjectsFeed(Feed):
title = "Recently updated documentation"
link = "http://readthedocs.org"
@@ -16,6 +17,7 @@ def item_title(self, item):
def item_description(self, item):
return item.get_latest_build()
+
class NewProjectsFeed(Feed):
title = "Newest documentation"
link = "http://readthedocs.org"
View
10 readthedocs/projects/filters.py
@@ -11,10 +11,14 @@
REPO_CHOICES = ANY_REPO + constants.REPO_CHOICES
+
class ProjectFilter(django_filters.FilterSet):
- name = django_filters.CharFilter(label=_("Name"), name='name', lookup_type='icontains')
- pub_date = django_filters.DateRangeFilter(label=_("Created Date"), name="pub_date")
- repo = django_filters.CharFilter(label=_("Repository URL"), name='repo', lookup_type='icontains')
+ name = django_filters.CharFilter(label=_("Name"), name='name',
+ lookup_type='icontains')
+ pub_date = django_filters.DateRangeFilter(label=_("Created Date"),
+ name="pub_date")
+ repo = django_filters.CharFilter(label=_("Repository URL"), name='repo',
+ lookup_type='icontains')
repo_type = django_filters.ChoiceFilter(
label=_("Repository"),
name='repo',
View
52 readthedocs/projects/forms.py
@@ -19,18 +19,23 @@ def clean_name(self):
if not self.instance.pk:
potential_slug = slugify(name)
if Project.objects.filter(slug=potential_slug).count():
- raise forms.ValidationError(_('A project with that name exists already!'))
+ raise forms.ValidationError(
+ _('A project with that name exists already!')
+ )
return name
class ImportProjectForm(ProjectForm):
repo = forms.CharField(required=True,
- help_text=_(u'URL for your code (hg or git). Ex. http://github.com/ericholscher/django-kong.git'))
+ help_text=_(u'URL for your code (hg or git). Ex. '
+ u'http://github.com/ericholscher/django'
+ u'-kong.git'))
python_interpreter = forms.ChoiceField(
choices=constants.PYTHON_CHOICES, initial='python',
- help_text=_("(Beta) The Python interpreter used to create the virtual environment."))
+ help_text=_("(Beta) The Python interpreter used to create the virtual "
+ "environment."))
class Meta:
model = Project
@@ -38,7 +43,8 @@ class Meta:
# Important
'name', 'repo', 'repo_type', 'description', 'language',
# Not as important
- 'project_url', 'tags', 'default_branch', 'default_version', 'conf_py_file',
+ 'project_url', 'tags', 'default_branch', 'default_version',
+ 'conf_py_file',
# Privacy
'privacy_level', 'version_privacy_level',
# Python specific
@@ -50,16 +56,21 @@ class Meta:
def clean_repo(self):
repo = self.cleaned_data.get('repo', '').strip()
+ pvt_repos = getattr(settings, 'ALLOW_PRIVATE_REPOS', False)
if '&&' in repo or '|' in repo:
raise forms.ValidationError(_(u'Invalid character in repo name'))
- elif '@' in repo and not getattr(settings, 'ALLOW_PRIVATE_REPOS', False):
- raise forms.ValidationError(_(u'It looks like you entered a private repo - please use the public (http:// or git://) clone url'))
+ elif '@' in repo and not pvt_repos:
+ raise forms.ValidationError(
+ _(u'It looks like you entered a private repo - please use the '
+ u'public (http:// or git://) clone url'))
return repo
def clean_conf_py_file(self):
file = self.cleaned_data.get('conf_py_file', '').strip()
if file and not 'conf.py' in file:
- raise forms.ValidationError(_('Your configuration file is invalid, make sure it contains conf.py in it.'))
+ raise forms.ValidationError(
+ _('Your configuration file is invalid, make sure it contains '
+ 'conf.py in it.'))
return file
def save(self, *args, **kwargs):
@@ -104,14 +115,19 @@ def save(self):
def save_version(self, version):
new_value = self.cleaned_data.get('version-%s' % version.slug, None)
- privacy_level = self.cleaned_data.get('privacy-%s' % version.slug, None)
- if (new_value is None or new_value == version.active) and (privacy_level is None or privacy_level == version.privacy_level):
+ privacy_level = self.cleaned_data.get('privacy-%s' % version.slug,
+ None)
+ if ((new_value is None or
+ new_value == version.active)
+ and (privacy_level is None or
+ privacy_level == version.privacy_level)):
return
version.active = new_value
version.privacy_level = privacy_level
version.save()
if version.active and not version.built and not version.uploaded:
- update_docs.delay(self.project.pk, record=True, version_pk=version.pk)
+ update_docs.delay(self.project.pk, record=True,
+ version_pk=version.pk)
def build_versions_form(project):
@@ -147,7 +163,8 @@ def build_versions_form(project):
class BaseUploadHTMLForm(forms.Form):
content = forms.FileField(label=_("Zip file of HTML"))
- overwrite = forms.BooleanField(required=False, label=_("Overwrite existing HTML?"))
+ overwrite = forms.BooleanField(required=False,
+ label=_("Overwrite existing HTML?"))
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
@@ -193,7 +210,8 @@ def clean_subproject(self):
subproject_name = self.cleaned_data['subproject']
subproject_qs = Project.objects.filter(name=subproject_name)
if not subproject_qs.exists():
- raise forms.ValidationError(_("Project %(name)s does not exist") % {'name': subproject_name})
+ raise forms.ValidationError((_("Project %(name)s does not exist")
+ % {'name': subproject_name}))
self.subproject = subproject_qs[0]
return subproject_name
@@ -201,6 +219,7 @@ def save(self):
relationship = self.parent.add_subproject(self.subproject)
return relationship
+
class UserForm(forms.Form):
user = forms.CharField()
@@ -221,6 +240,7 @@ def save(self):
self.project.users.add(self.user)
return self.user
+
class EmailHookForm(forms.Form):
email = forms.EmailField()
@@ -229,13 +249,15 @@ def __init__(self, *args, **kwargs):
super(EmailHookForm, self).__init__(*args, **kwargs)
def clean_email(self):
- self.email = EmailHook.objects.get_or_create(email=self.cleaned_data['email'], project=self.project)[0]
+ self.email = EmailHook.objects.get_or_create(
+ email=self.cleaned_data['email'], project=self.project)[0]
return self.email
def save(self):
self.project.emailhook_notifications.add(self.email)
return self.project
+
class TranslationForm(forms.Form):
project = forms.CharField()
@@ -247,11 +269,11 @@ def clean_project(self):
subproject_name = self.cleaned_data['project']
subproject_qs = Project.objects.filter(name=subproject_name)
if not subproject_qs.exists():
- raise forms.ValidationError(_("Project %(name)s does not exist") % {'name': subproject_name})
+ raise forms.ValidationError((_("Project %(name)s does not exist")
+ % {'name': subproject_name}))
self.subproject = subproject_qs[0]
return subproject_name
def save(self):
project = self.parent.translations.add(self.subproject)
return project
-
View
276 readthedocs/projects/models.py
@@ -25,6 +25,7 @@
log = logging.getLogger(__name__)
+
class ProjectManager(models.Manager):
def _filter_queryset(self, user, privacy_level):
if isinstance(privacy_level, basestring):
@@ -58,7 +59,9 @@ def protected(self, user=None, *args, **kwargs):
"""
Query for projects, privacy_level != private, and skip = False
"""
- queryset = self._filter_queryset(user, privacy_level=(constants.PUBLIC, constants.PROTECTED))
+ queryset = self._filter_queryset(user,
+ privacy_level=(constants.PUBLIC,
+ constants.PROTECTED))
return queryset.filter(*args, **kwargs)
def private(self, user=None, *args, **kwargs):
@@ -70,15 +73,19 @@ def private(self, user=None, *args, **kwargs):
class ProjectRelationship(models.Model):
- parent = models.ForeignKey('Project', verbose_name=_('Parent'), related_name='subprojects')
- child = models.ForeignKey('Project', verbose_name=_('Child'), related_name='superprojects')
+ parent = models.ForeignKey('Project', verbose_name=_('Parent'),
+ related_name='subprojects')
+ child = models.ForeignKey('Project', verbose_name=_('Child'),
+ related_name='superprojects')
def __unicode__(self):
return "%s -> %s" % (self.parent, self.child)
#HACK
def get_absolute_url(self):
- return "http://%s.readthedocs.org/projects/%s/%s/latest/" % (self.parent.slug, self.child.slug, self.child.language)
+ return ("http://%s.readthedocs.org/projects/%s/%s/latest/"
+ % (self.parent.slug, self.child.slug, self.child.language))
+
class Project(models.Model):
#Auto fields
@@ -86,69 +93,117 @@ class Project(models.Model):
modified_date = models.DateTimeField(_('Modified date'), auto_now=True)
#Generally from conf.py
- users = models.ManyToManyField(User, verbose_name=_('User'), related_name='projects')
+ users = models.ManyToManyField(User, verbose_name=_('User'),
+ related_name='projects')
name = models.CharField(_('Name'), max_length=255)
slug = models.SlugField(_('Slug'), max_length=255, unique=True)
description = models.TextField(_('Description'), blank=True,
- help_text=_('The reStructuredText description of the project'))
+ help_text=_('The reStructuredText '
+ 'description of the project'))
repo = models.CharField(_('Repository URL'), max_length=100, blank=True,
- help_text=_('Checkout URL for your code (hg, git, etc.). Ex. http://github.com/ericholscher/django-kong.git'))
- repo_type = models.CharField(_('Repository type'), max_length=10, choices=constants.REPO_CHOICES, default='git')
- project_url = models.URLField(_('Project URL'), blank=True, help_text=_('The project\'s homepage'), verify_exists=False)
+ help_text=_('Checkout URL for your code (hg, git, '
+ 'etc.). Ex. http://github.com/'
+ 'ericholscher/django-kong.git'))
+ repo_type = models.CharField(_('Repository type'), max_length=10,
+ choices=constants.REPO_CHOICES, default='git')
+ project_url = models.URLField(_('Project URL'), blank=True,
+ help_text=_('The project\'s homepage'),
+ verify_exists=False)
version = models.CharField(_('Version'), max_length=100, blank=True,
- help_text=_('Project version these docs apply to, i.e. 1.0a'))
+ help_text=_('Project version these docs apply '
+ 'to, i.e. 1.0a'))
copyright = models.CharField(_('Copyright'), max_length=255, blank=True,
- help_text=_('Project copyright information'))
- theme = models.CharField(_('Theme'), max_length=20,
- choices=constants.DEFAULT_THEME_CHOICES, default=constants.THEME_DEFAULT,
- help_text=u'<a href="http://sphinx.pocoo.org/theming.html#builtin-themes" target="_blank">%s</a>' % _('Examples'))
- suffix = models.CharField(_('Suffix'), max_length