From 6894f443490be6dfc4c3a310e230f3b1819e209e Mon Sep 17 00:00:00 2001 From: Nick Sergeant Date: Fri, 24 Jul 2015 21:28:31 -0400 Subject: [PATCH] So much PEP8. --- accounts/admin.py | 4 +- accounts/models.py | 51 +++++---- accounts/urls.py | 14 ++- accounts/views.py | 12 +- admin.py | 6 +- blogs/middleware.py | 15 ++- blogs/urls.py | 4 +- blogs/views.py | 69 ++++++----- jobs/__init__.py | 0 jobs/admin.py | 9 -- jobs/management/__init__.py | 0 jobs/management/commands/__init__.py | 0 jobs/management/commands/importjobs.py | 48 -------- jobs/migrations/0001_initial.py | 25 ---- jobs/migrations/__init__.py | 0 jobs/models.py | 16 --- jobs/templates/jobs/jobs.html | 70 ------------ jobs/tests.py | 16 --- jobs/views.py | 22 ---- settings.py | 24 ++-- settings_local.py-template | 2 +- snipts/admin.py | 5 +- snipts/api.py | 118 ++++++++++++------- snipts/forms.py | 1 + snipts/models.py | 151 ++++++++++++++++--------- snipts/search_indexes.py | 3 +- snipts/templatetags/snipt_tags.py | 5 + snipts/tests.py | 62 ++++++---- snipts/urls.py | 63 +++++++---- snipts/utils.py | 2 + snipts/views.py | 50 +++++--- templates/analytics.html | 1 - urls.py | 73 ++++++------ utils/backends.py | 2 +- utils/forms.py | 14 ++- utils/templatetags/intercom.py | 10 +- utils/templatetags/verbatim.py | 2 +- utils/views.py | 2 +- views.py | 34 ++++-- 39 files changed, 514 insertions(+), 491 deletions(-) delete mode 100644 jobs/__init__.py delete mode 100644 jobs/admin.py delete mode 100644 jobs/management/__init__.py delete mode 100644 jobs/management/commands/__init__.py delete mode 100644 jobs/management/commands/importjobs.py delete mode 100644 jobs/migrations/0001_initial.py delete mode 100644 jobs/migrations/__init__.py delete mode 100644 jobs/models.py delete mode 100644 jobs/templates/jobs/jobs.html delete mode 100644 jobs/tests.py delete mode 100644 jobs/views.py diff --git a/accounts/admin.py b/accounts/admin.py index 36495a3..7d99a81 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -2,8 +2,10 @@ from django.contrib import admin from accounts.models import UserProfile + class UserProfileAdmin(admin.ModelAdmin): - list_display = ('user', 'is_pro', 'stripe_id', 'gittip_username', 'teams_beta_seen') + list_display = ('user', 'is_pro', 'stripe_id', 'gittip_username', + 'teams_beta_seen') list_filter = ['teams_beta_seen', 'teams_beta_applied'] search_fields = ('user__username', 'gittip_username',) diff --git a/accounts/models.py b/accounts/models.py index 2f42eac..ed3a0fa 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -1,9 +1,9 @@ from django.contrib.auth.models import User -from datetime import date from datetime import datetime from django.db import models from snipts.models import Snipt + class UserProfile(models.Model): LIST_VIEW_CHOICES = ( @@ -42,38 +42,45 @@ class UserProfile(models.Model): ) # User - user = models.OneToOneField(User) - is_pro = models.BooleanField(default=False) + user = models.OneToOneField(User) + is_pro = models.BooleanField(default=False) teams_beta_seen = models.BooleanField(default=False) teams_beta_applied = models.BooleanField(default=False) - pro_date = models.DateTimeField(blank=True, null=True) - stripe_id = models.CharField(max_length=100, null=True, blank=True) - has_gravatar = models.BooleanField(default=False) - list_view = models.CharField(max_length=1, null=False, blank=False,default='N', choices=LIST_VIEW_CHOICES) + pro_date = models.DateTimeField(blank=True, null=True) + stripe_id = models.CharField(max_length=100, null=True, blank=True) + has_gravatar = models.BooleanField(default=False) + list_view = models.CharField(max_length=1, null=False, blank=False, + default='N', choices=LIST_VIEW_CHOICES) # Blog - blog_title = models.CharField(max_length=250, null=True, blank=True) - blog_theme = models.CharField(max_length=1, null=False, blank=False, default='A', choices=THEME_CHOICES) - blog_domain = models.CharField(max_length=250, null=True, blank=True) + blog_title = models.CharField(max_length=250, null=True, blank=True) + blog_theme = models.CharField(max_length=1, null=False, blank=False, + default='A', choices=THEME_CHOICES) + blog_domain = models.CharField(max_length=250, null=True, blank=True) # Editor - default_editor = models.CharField(max_length=250, null=False, blank=False, default='C', choices=EDITOR_CHOICES) - editor_theme = models.CharField(max_length=250, null=False, blank=False, default='default', choices=EDITOR_THEME_CHOICES) + default_editor = models.CharField(max_length=250, null=False, blank=False, + default='C', choices=EDITOR_CHOICES) + editor_theme = models.CharField(max_length=250, null=False, blank=False, + default='default', + choices=EDITOR_THEME_CHOICES) # Services and Analytics - gittip_username = models.CharField(max_length=250, null=True, blank=True) + gittip_username = models.CharField(max_length=250, null=True, blank=True) disqus_shortname = models.CharField(max_length=250, null=True, blank=True) - google_analytics_tracking_id = models.CharField(max_length=250, null=True, blank=True) - gauges_site_id = models.CharField(max_length=250, null=True, blank=True) + google_analytics_tracking_id = models.CharField(max_length=250, null=True, + blank=True) + gauges_site_id = models.CharField(max_length=250, null=True, blank=True) # Google Ads google_ad_client = models.CharField(max_length=250, null=True, blank=True) - google_ad_slot = models.CharField(max_length=250, null=True, blank=True) - google_ad_width = models.CharField(max_length=250, null=True, blank=True) + google_ad_slot = models.CharField(max_length=250, null=True, blank=True) + google_ad_width = models.CharField(max_length=250, null=True, blank=True) google_ad_height = models.CharField(max_length=250, null=True, blank=True) def get_blog_posts(self): - return Snipt.objects.filter(user=self.user, blog_post=True, public=True) + return Snipt.objects.filter(user=self.user, blog_post=True, + public=True) def get_primary_blog_domain(self): if not self.blog_domain: @@ -98,10 +105,14 @@ class UserProfile(models.Model): return url def has_public_snipts(self): - return True if Snipt.objects.filter(user=self, public=True).count() > 0 else False + return True \ + if Snipt.objects.filter(user=self, + public=True).count() > 0 \ + else False def get_account_age(self): - delta = datetime.now().replace(tzinfo=None) - self.user.date_joined.replace(tzinfo=None) + delta = datetime.now().replace(tzinfo=None) - \ + self.user.date_joined.replace(tzinfo=None) return delta.days User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0]) diff --git a/accounts/urls.py b/accounts/urls.py index 9a9715a..c2e6be8 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -2,9 +2,11 @@ from django.conf.urls import * from accounts import views -urlpatterns = patterns('', - url(r'^stats/$', views.stats, name='account-stats'), - url(r'^cancel-subscription/$', views.cancel_subscription, name='cancel-subscription'), - url(r'^stripe-account-details/$', views.stripe_account_details, name='stripe-account-details'), - url(r'^', views.account, name='account-detail'), -) +urlpatterns = \ + patterns('', + url(r'^stats/$', views.stats, name='account-stats'), + url(r'^cancel-subscription/$', views.cancel_subscription, + name='cancel-subscription'), + url(r'^stripe-account-details/$', views.stripe_account_details, + name='stripe-account-details'), + url(r'^', views.account, name='account-detail')) diff --git a/accounts/views.py b/accounts/views.py index 5b7b187..ee97f5c 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -3,7 +3,8 @@ from django.contrib.auth.decorators import login_required from annoying.decorators import ajax_request, render_to from snipts.models import Snipt -import os, stripe +import os +import stripe @login_required @@ -19,7 +20,8 @@ def cancel_subscription(request): if request.user.profile.stripe_id is None: return {} else: - stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', settings.STRIPE_SECRET_KEY) + stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', + settings.STRIPE_SECRET_KEY) customer = stripe.Customer.retrieve(request.user.profile.stripe_id) customer.delete() @@ -28,7 +30,8 @@ def cancel_subscription(request): profile.stripe_id = None profile.save() - return { 'deleted': True } + return {'deleted': True} + @login_required @ajax_request @@ -37,7 +40,8 @@ def stripe_account_details(request): if request.user.profile.stripe_id is None: return {} else: - stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', settings.STRIPE_SECRET_KEY) + stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', + settings.STRIPE_SECRET_KEY) customer = stripe.Customer.retrieve(request.user.profile.stripe_id) data = { diff --git a/admin.py b/admin.py index 9bb164e..1d076e9 100644 --- a/admin.py +++ b/admin.py @@ -2,9 +2,11 @@ from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from django.contrib import admin + class UserAdmin(UserAdmin): - list_display = ['username', 'email', 'first_name', 'last_name', 'last_login', - 'date_joined', 'is_active', 'is_staff', 'api_key'] + list_display = ['username', 'email', 'first_name', 'last_name', + 'last_login', 'date_joined', 'is_active', 'is_staff', + 'api_key'] list_filter = ['is_staff', 'is_superuser', 'is_active'] ordering = ['-date_joined'] diff --git a/blogs/middleware.py b/blogs/middleware.py index 64b5778..d18fd8c 100644 --- a/blogs/middleware.py +++ b/blogs/middleware.py @@ -12,19 +12,26 @@ class BlogMiddleware: host = request.META.get('HTTP_HOST', '') host_s = host.replace('www.', '').split('.') - if host != 'snipt.net' and host != 'snipt.localhost' and host != 'local.snipt.net': + if host != 'snipt.net' and \ + host != 'snipt.localhost' and \ + host != 'local.snipt.net': if len(host_s) > 2: if host_s[1] == 'snipt': blog_user = ''.join(host_s[:-2]) if '-' in blog_user: - request.blog_user = get_object_or_None(User, username__iexact=blog_user) + request.blog_user = \ + get_object_or_None(User, + username__iexact=blog_user) if request.blog_user is None: - request.blog_user = get_object_or_404(User, username__iexact=blog_user.replace('-', '_')) + request.blog_user = \ + get_object_or_404(User, + username__iexact=blog_user.replace('-', '_')) else: - request.blog_user = get_object_or_404(User, username__iexact=blog_user) + request.blog_user = \ + get_object_or_404(User, username__iexact=blog_user) if request.blog_user is None: pro_users = User.objects.filter(userprofile__is_pro=True) diff --git a/blogs/urls.py b/blogs/urls.py index a9219b5..1c83ade 100644 --- a/blogs/urls.py +++ b/blogs/urls.py @@ -1,5 +1,3 @@ from django.conf.urls import * -urlpatterns = patterns('', - url(r'^$', views.blog, name='blog'), -) +urlpatterns = patterns('', url(r'^$', views.blog, name='blog')) diff --git a/blogs/views.py b/blogs/views.py index bf06335..db254fe 100644 --- a/blogs/views.py +++ b/blogs/views.py @@ -11,6 +11,7 @@ THEME_CHOICES = { 'A': 'blogs/themes/pro-adams/', } + def blog_list(request, username_or_custom_slug=None): if username_or_custom_slug: @@ -19,17 +20,25 @@ def blog_list(request, username_or_custom_slug=None): snipts = Snipt.objects.filter(user=request.blog_user, blog_post=True, public=True, - publish_date__lte=datetime.datetime.now() - ).order_by('-publish_date').exclude(title__iexact='Homepage').exclude(title__iexact='Work') + publish_date__lte=datetime.datetime.now()) \ + .order_by('-publish_date') \ + .exclude(title__iexact='Homepage') \ + .exclude(title__iexact='Work') - normal_snipts = Snipt.objects.filter(blog_post=False, user=request.blog_user, public=True).order_by('-created') + normal_snipts = Snipt.objects.filter(blog_post=False, + user=request.blog_user, + public=True) \ + .order_by('-created') normal_snipts = normal_snipts.exclude(title__in=['']) normal_snipts = normal_snipts.exclude(tags__name__in=['tmp']) normal_snipts = normal_snipts[:3] - sidebar = get_object_or_None(Snipt, user=request.blog_user, title='Sidebar', blog_post=True) - header = get_object_or_None(Snipt, user=request.blog_user, title='Header', blog_post=True) - custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', lexer='css', blog_post=True) + sidebar = get_object_or_None(Snipt, user=request.blog_user, + title='Sidebar', blog_post=True) + header = get_object_or_None(Snipt, user=request.blog_user, title='Header', + blog_post=True) + custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', + lexer='css', blog_post=True) context = { 'blog_user': request.blog_user, @@ -55,26 +64,33 @@ def blog_list(request, username_or_custom_slug=None): context, context_instance=RequestContext(request)) + def blog_post(request, username_or_custom_slug): snipt = get_object_or_404(Snipt, user=request.blog_user, - blog_post=True, - public=True, - publish_date__lte=datetime.datetime.now(), - slug=username_or_custom_slug, - ) + blog_post=True, + public=True, + publish_date__lte=datetime.datetime.now(), + slug=username_or_custom_slug) snipts = Snipt.objects.filter(user=request.blog_user, blog_post=True, public=True, - publish_date__lte=datetime.datetime.now() - ).order_by('-publish_date').exclude(title__iexact='Homepage').exclude(title__iexact='Work') + publish_date__lte=datetime.datetime.now()) \ + .order_by('-publish_date') \ + .exclude(title__iexact='Homepage') \ + .exclude(title__iexact='Work') - sidebar = get_object_or_None(Snipt, user=request.blog_user, title='Sidebar', blog_post=True) - header = get_object_or_None(Snipt, user=request.blog_user, title='Header', blog_post=True) - custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', lexer='css', blog_post=True) + sidebar = get_object_or_None(Snipt, user=request.blog_user, + title='Sidebar', blog_post=True) + header = get_object_or_None(Snipt, user=request.blog_user, title='Header', + blog_post=True) + custom_css = get_object_or_None(Snipt, user=request.blog_user, title='CSS', + lexer='css', blog_post=True) - normal_snipts = Snipt.objects.filter(blog_post=False, user=request.blog_user, public=True).order_by('-created') + normal_snipts = Snipt.objects.filter(blog_post=False, + user=request.blog_user, + public=True).order_by('-created') normal_snipts = normal_snipts.exclude(title__in=['']) normal_snipts = normal_snipts.exclude(tags__name__in=['tmp']) normal_snipts = normal_snipts[:3] @@ -100,16 +116,13 @@ def blog_post(request, username_or_custom_slug): template = '{}/post.html'.format(template) - return render_to_response( - template, - context, - context_instance=RequestContext(request) - ) + return render_to_response(template, + context, + context_instance=RequestContext(request)) + def rss(request, context): - return render_to_response( - 'blogs/themes/default/rss.xml', - context, - context_instance=RequestContext(request), - content_type="application/rss+xml" - ) + return render_to_response('blogs/themes/default/rss.xml', + context, + context_instance=RequestContext(request), + content_type="application/rss+xml") diff --git a/jobs/__init__.py b/jobs/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/jobs/admin.py b/jobs/admin.py deleted file mode 100644 index 9824b17..0000000 --- a/jobs/admin.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.contrib import admin -from jobs.models import Job - - -class JobAdmin(admin.ModelAdmin): - list_display = ('title', 'created', 'company', 'url',) - ordering = ('-created',) - -admin.site.register(Job, JobAdmin) diff --git a/jobs/management/__init__.py b/jobs/management/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/jobs/management/commands/__init__.py b/jobs/management/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/jobs/management/commands/importjobs.py b/jobs/management/commands/importjobs.py deleted file mode 100644 index e15eda5..0000000 --- a/jobs/management/commands/importjobs.py +++ /dev/null @@ -1,48 +0,0 @@ -from django.conf import settings -from django.core.management.base import BaseCommand -from jobs.models import Job - -import json -import requests - - -class Command(BaseCommand): - help = 'Import jobs' - listings = [] - - def get_for_page(self, page): - r = requests.get('{}&page={}'.format(settings.JOBS_BASE_URL, page)) - obj = r.json() - self.listings.extend(obj['listings']['listing']) - - if page < obj['listings']['pages']: - return self.get_for_page(page + 1) - - return self.listings - - def handle(self, *args, **options): - listings = self.get_for_page(1) - - jobs = Job.objects.all() - for job in jobs: - job.delete() - - for listing in listings: - - try: - location = listing['company']['location']['city'] - except: - location = '' - - try: - company = str(listing['company']['name']) - except: - company = '' - - newjob = Job(title=listing['title'], - company=company, - location=location, - url=listing['url'], - data=json.dumps(listing), - created=listing['post_date']) - newjob.save() diff --git a/jobs/migrations/0001_initial.py b/jobs/migrations/0001_initial.py deleted file mode 100644 index 481e981..0000000 --- a/jobs/migrations/0001_initial.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Job', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=255)), - ('company', models.CharField(max_length=255)), - ('location', models.CharField(max_length=255)), - ('url', models.CharField(max_length=255)), - ('data', models.TextField(null=True, blank=True)), - ('created', models.DateTimeField()), - ], - ), - ] diff --git a/jobs/migrations/__init__.py b/jobs/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/jobs/models.py b/jobs/models.py deleted file mode 100644 index ce03ed0..0000000 --- a/jobs/models.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.db import models - - -class Job(models.Model): - """An individual Job.""" - - title = models.CharField(max_length=255) - company = models.CharField(max_length=255) - location = models.CharField(max_length=255) - url = models.CharField(max_length=255) - data = models.TextField(blank=True, null=True) - - created = models.DateTimeField() - - def __unicode__(self): - return self.title diff --git a/jobs/templates/jobs/jobs.html b/jobs/templates/jobs/jobs.html deleted file mode 100644 index 7bad9ef..0000000 --- a/jobs/templates/jobs/jobs.html +++ /dev/null @@ -1,70 +0,0 @@ -{% extends "base.html" %} - -{% load humanize %} -{% load pagination_tags %} - -{% block page-title %}Snipt Jobs{% endblock %} - -{% block body-class %}{{ block.super }} static jobs search{% endblock %} - -{% block js %} - window.jobs = [ - {% for job in jobs %} - {{ job.data|safe }}{% if not forloop.last %},{% endif %} - {% endfor %} - ]; -{% endblock %} - -{% block breadcrumb %} -
  • Snipt Jobs
  • -{% endblock %} - -{% block content %} -
    -
    - -
    -
    - Loading jobs… -
    - -
    - -
    - -
    -{% endblock %} - -{% block ad %}{% endblock %} -{% block aside-top %} -
    - Post a job -

    - We use Authentic Jobs for our job board provider. Once you post your ad, it will appear on Snipt within 30 minutes. -

    -
    -{% endblock %} diff --git a/jobs/tests.py b/jobs/tests.py deleted file mode 100644 index 501deb7..0000000 --- a/jobs/tests.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. -""" - -from django.test import TestCase - - -class SimpleTest(TestCase): - def test_basic_addition(self): - """ - Tests that 1 + 1 always equals 2. - """ - self.assertEqual(1 + 1, 2) diff --git a/jobs/views.py b/jobs/views.py deleted file mode 100644 index 1b529dc..0000000 --- a/jobs/views.py +++ /dev/null @@ -1,22 +0,0 @@ -from annoying.decorators import ajax_request, render_to -from jobs.models import Job - -import json - - -@render_to('jobs/jobs.html') -def jobs(request): - return {} - - -@ajax_request -def jobs_json(request): - - jobs_json = [] - - jobs = Job.objects.all().order_by('-created') - - for job in jobs: - jobs_json.append(json.loads(job.data)) - - return jobs_json diff --git a/settings.py b/settings.py index 50c2cd4..d276e0a 100644 --- a/settings.py +++ b/settings.py @@ -1,13 +1,15 @@ -import dj_database_url, os +import dj_database_url +import os from urlparse import urlparse if 'DATABASE_URL' in os.environ: - DATABASES = { 'default': dj_database_url.config() } + DATABASES = {'default': dj_database_url.config()} - es = urlparse(os.environ.get('SEARCHBOX_SSL_URL') or 'http://127.0.0.1:9200/') + es = urlparse(os.environ.get('SEARCHBOX_SSL_URL') or + 'http://127.0.0.1:9200/') port = es.port or 80 HAYSTACK_CONNECTIONS = { @@ -19,9 +21,11 @@ if 'DATABASE_URL' in os.environ: } if es.username: - HAYSTACK_CONNECTIONS['default']['KWARGS'] = {"http_auth": es.username + ':' + es.password} + HAYSTACK_CONNECTIONS['default']['KWARGS'] = { + "http_auth": es.username + ':' + es.password + } -ABSOLUTE_URL_OVERRIDES = { 'auth.user': lambda u: "/%s/" % u.username, } +ABSOLUTE_URL_OVERRIDES = {'auth.user': lambda u: "/%s/" % u.username} ACCOUNT_ACTIVATION_DAYS = 0 ADMINS = (('Nick Sergeant', 'nick@snipt.net'),) ALLOWED_HOSTS = ['*'] @@ -46,7 +50,7 @@ MEDIA_URL = '/media/uploads/' MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' POSTMARK_API_KEY = os.environ.get('POSTMARK_API_KEY', '') PROJECT_PATH = os.path.abspath(os.path.dirname(__file__)) -RAVEN_CONFIG = { 'dsn': os.environ.get('RAVEN_CONFIG_DSN', '') } +RAVEN_CONFIG = {'dsn': os.environ.get('RAVEN_CONFIG_DSN', '')} REGISTRATION_EMAIL_HTML = False ROOT_URLCONF = 'urls' SECRET_KEY = os.environ.get('SECRET_KEY', 'changeme') @@ -57,11 +61,14 @@ SESSION_COOKIE_AGE = 15801100 SESSION_COOKIE_SECURE = True if 'USE_SSL' in os.environ else False SITE_ID = 1 STATICFILES_DIRS = (os.path.join(BASE_PATH, 'media'),) -STATICFILES_FINDERS = ('django.contrib.staticfiles.finders.FileSystemFinder','django.contrib.staticfiles.finders.AppDirectoriesFinder',) +STATICFILES_FINDERS = ('django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder') STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' STATIC_ROOT = os.path.join(BASE_PATH, 'static') STATIC_URL = '/static/' -TASTYPIE_CANNED_ERROR = "There was an error with your request. The site developers have a record of this error, please email api@snipt.net and we'll help you out." +TASTYPIE_CANNED_ERROR = """There was an error with your request. The site + developers have a record of this error, please email api@snipt.net and + we'll help you out.""" TEMPLATE_DIRS = (os.path.join(PROJECT_PATH, 'templates'),) TEMPLATE_DEBUG = DEBUG TIME_ZONE = 'America/New_York' @@ -93,7 +100,6 @@ INSTALLED_APPS = ( 'typogrify', 'accounts', 'blogs', - 'jobs', 'snipts', 'utils', ) diff --git a/settings_local.py-template b/settings_local.py-template index 1a8c484..aed01ed 100644 --- a/settings_local.py-template +++ b/settings_local.py-template @@ -2,7 +2,7 @@ CSRF_COOKIE_SECURE = False DEBUG = True INTERCOM_SECRET_KEY = '' POSTMARK_API_KEY = '' -RAVEN_CONFIG = { 'dsn': '' } +RAVEN_CONFIG = {'dsn': ''} SECRET_KEY = 'changeme' SESSION_COOKIE_SECURE = False SSLIFY_DISABLE = False diff --git a/snipts/admin.py b/snipts/admin.py index 46a1ee0..734517f 100644 --- a/snipts/admin.py +++ b/snipts/admin.py @@ -5,7 +5,9 @@ from snipts.models import Favorite, Snipt class SniptAdmin(admin.ModelAdmin): readonly_fields = ('user',) - list_display = ('title', 'slug', 'views', 'favs', 'user', 'lexer', 'public', 'blog_post', 'created', 'modified', 'publish_date',) + list_display = ('title', 'slug', 'views', 'favs', 'user', 'lexer', + 'public', 'blog_post', 'created', 'modified', + 'publish_date') list_filter = ('blog_post',) search_fields = ('title', 'slug', 'user__username', 'lexer', 'id', 'key',) ordering = ('-created',) @@ -13,6 +15,7 @@ class SniptAdmin(admin.ModelAdmin): admin.site.register(Snipt, SniptAdmin) + class FavoriteAdmin(admin.ModelAdmin): readonly_fields = ('snipt', 'user',) list_display = ('snipt', 'user', 'created',) diff --git a/snipts/api.py b/snipts/api.py index 56468eb..dbf2c3c 100644 --- a/snipts/api.py +++ b/snipts/api.py @@ -12,12 +12,14 @@ from haystack.query import SearchQuerySet from accounts.models import UserProfile from tastypie.cache import SimpleCache from tastypie.fields import ListField -from django.http import HttpResponse from taggit.models import Tag from django.db import models from tastypie import fields -import datetime, hashlib, time, re +import datetime +import hashlib +import re +import time import parsedatetime as pdt @@ -49,6 +51,7 @@ class PrivateFavoriteAuthorization(Authorization): def delete_detail(self, object_list, bundle): return bundle.obj.user == bundle.request.user + class PrivateSniptAuthorization(Authorization): def read_list(self, object_list, bundle): return object_list.filter(user=bundle.request.user) @@ -74,6 +77,7 @@ class PrivateSniptAuthorization(Authorization): def delete_detail(self, object_list, bundle): return bundle.obj.user == bundle.request.user + class PrivateUserProfileAuthorization(Authorization): def read_list(self, object_list, bundle): raise Unauthorized() @@ -99,6 +103,7 @@ class PrivateUserProfileAuthorization(Authorization): def delete_detail(self, object_list, bundle): raise Unauthorized() + class PrivateUserAuthorization(Authorization): def read_list(self, object_list, bundle): raise Unauthorized() @@ -130,22 +135,27 @@ class FavoriteValidation(Validation): errors = {} snipt = bundle.data['snipt'] - if Favorite.objects.filter(user=bundle.request.user, snipt=snipt).count(): + if Favorite.objects.filter(user=bundle.request.user, + snipt=snipt).count(): errors['duplicate'] = 'User has already favorited this snipt.' return errors + class SniptValidation(Validation): def is_valid(self, bundle, request=None): errors = {} if 'pk' not in bundle.data and \ - request.user.profile.get_account_age() > 7 and \ - request.user.profile.is_pro == False: - errors['expired'] = "Your trial has expired. You'll need to go Pro (https://snipt.net/pro/) in order to create new snipts." + request.user.profile.get_account_age() > 7 and \ + request.user.profile.is_pro is False: + errors['expired'] = """Your trial has expired. You'll need + to go Pro (https://snipt.net/pro/) + in order to create new snipts.""" return errors + class UserProfileValidation(Validation): def is_valid(self, bundle, request=None): errors = {} @@ -153,7 +163,9 @@ class UserProfileValidation(Validation): for field in bundle.data: if bundle.data[field]: if not re.match('^[ A-Za-z0-9\/\@\._-]*$', bundle.data[field]): - errors[field] = 'Only spaces, letters, numbers, underscores, dashes, periods, forward slashes, and "at sign" are valid.' + errors[field] = """Only spaces, letters, numbers, + underscores, dashes, periods, forward + slashes, and "at sign" are valid.""" return errors @@ -162,26 +174,31 @@ class PublicUserResource(ModelResource): class Meta: queryset = User.objects.all() resource_name = 'user' - fields = ['id', 'username',] + fields = ['id', 'username'] include_absolute_url = True allowed_methods = ['get'] - filtering = { 'username': 'exact', } + filtering = {'username': 'exact'} max_limit = 200 cache = SimpleCache() def dehydrate(self, bundle): bundle.data['snipts'] = '/api/public/snipt/?user=%d' % bundle.obj.id - bundle.data['email_md5'] = hashlib.md5(bundle.obj.email.lower()).hexdigest() - bundle.data['snipts_count'] = Snipt.objects.filter(user=bundle.obj.id, public=True).count() + bundle.data['email_md5'] = hashlib \ + .md5(bundle.obj.email.lower()) \ + .hexdigest() + bundle.data['snipts_count'] = Snipt.objects.filter(user=bundle.obj.id, + public=True).count() return bundle + class PublicTagResource(ModelResource): class Meta: queryset = Tag.objects.filter() - queryset = queryset.annotate(count=models.Count('taggit_taggeditem_items__id')) + queryset = queryset.annotate( + count=models.Count('taggit_taggeditem_items__id')) queryset = queryset.order_by('-count', 'name') resource_name = 'tag' - fields = ['id', 'name',] + fields = ['id', 'name'] allowed_methods = ['get'] max_limit = 200 cache = SimpleCache() @@ -202,19 +219,22 @@ class PublicTagResource(ModelResource): bundle.data['snipts'] = '/api/public/snipt/?tag=%d' % bundle.obj.id return bundle + class PublicSniptResource(ModelResource): user = fields.ForeignKey(PublicUserResource, 'user', full=True) - tags = fields.ToManyField(PublicTagResource, 'tags', related_name='tag', full=True) + tags = fields.ToManyField(PublicTagResource, 'tags', related_name='tag', + full=True) class Meta: queryset = Snipt.objects.filter(public=True).order_by('-created') resource_name = 'snipt' - fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', 'line_count', - 'stylized', 'created', 'modified', 'publish_date', 'blog_post', 'meta',] + fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', + 'line_count', 'stylized', 'created', 'modified', + 'publish_date', 'blog_post', 'meta'] include_absolute_url = True allowed_methods = ['get'] - filtering = { 'user': 'exact', 'blog_post': 'exact' } - ordering = ['created', 'modified',] + filtering = {'user': 'exact', 'blog_post': 'exact'} + ordering = ['created', 'modified'] max_limit = 200 cache = SimpleCache() @@ -222,7 +242,8 @@ class PublicSniptResource(ModelResource): bundle.data['embed_url'] = bundle.obj.get_embed_url() bundle.data['raw_url'] = bundle.obj.get_raw_url() bundle.data['full_absolute_url'] = bundle.obj.get_full_absolute_url() - bundle.data['description_rendered'] = linebreaksbr(urlize(bundle.obj.description)) + bundle.data['description_rendered'] = \ + linebreaksbr(urlize(bundle.obj.description)) if 'omit_code' in bundle.request.GET: del bundle.data['code'] @@ -272,8 +293,10 @@ class PrivateUserProfileResource(ModelResource): bundle.data['is_pro'] = bundle.obj.user.profile.is_pro return bundle + class PrivateUserResource(ModelResource): - profile = fields.ForeignKey(PrivateUserProfileResource, 'profile', full=False) + profile = fields.ForeignKey(PrivateUserProfileResource, 'profile', + full=False) class Meta: queryset = User.objects.all() @@ -289,18 +312,25 @@ class PrivateUserResource(ModelResource): cache = SimpleCache() def dehydrate(self, bundle): - bundle.data['email_md5'] = hashlib.md5(bundle.obj.email.lower()).hexdigest() + bundle.data['email_md5'] = hashlib \ + .md5(bundle.obj.email.lower()) \ + .hexdigest() bundle.data['is_pro'] = bundle.obj.profile.is_pro bundle.data['stats'] = { - 'public_snipts': Snipt.objects.filter(user=bundle.obj.id, public=True).count(), - 'private_snipts': Snipt.objects.filter(user=bundle.obj.id, public=False).count(), + 'public_snipts': Snipt.objects.filter(user=bundle.obj.id, + public=True).count(), + 'private_snipts': Snipt.objects.filter(user=bundle.obj.id, + public=False).count(), 'total_snipts': Snipt.objects.filter(user=bundle.obj.id).count(), - 'total_views': Snipt.objects.filter(user=bundle.obj.id).aggregate(models.Sum('views'))['views__sum'] + 'total_views': Snipt.objects.filter(user=bundle.obj.id).aggregate( + models.Sum('views'))['views__sum'] } - bundle.data['lexers'] = [snipt['lexer'] for snipt in \ - Snipt.objects.filter(user=bundle.obj).values('lexer').distinct()] + bundle.data['lexers'] = [ + snipt['lexer'] for snipt in Snipt.objects.filter(user=bundle.obj) + .values('lexer').distinct()] return bundle + class PrivateSniptResource(ModelResource): user = fields.ForeignKey(PrivateUserResource, 'user', full=True) tags_list = ListField() @@ -308,15 +338,16 @@ class PrivateSniptResource(ModelResource): class Meta: queryset = Snipt.objects.all().order_by('-created') resource_name = 'snipt' - fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', 'line_count', 'stylized', - 'key', 'public', 'blog_post', 'created', 'modified', 'publish_date', 'meta',] + fields = ['id', 'title', 'slug', 'lexer', 'code', 'description', + 'line_count', 'stylized', 'key', 'public', 'blog_post', + 'created', 'modified', 'publish_date', 'meta'] include_absolute_url = True detail_allowed_methods = ['get', 'patch', 'put', 'delete'] list_allowed_methods = ['get', 'post'] authentication = ApiKeyAuthentication() authorization = PrivateSniptAuthorization() validation = SniptValidation() - ordering = ['created', 'modified',] + ordering = ['created', 'modified'] always_return_data = True max_limit = 200 cache = SimpleCache() @@ -326,12 +357,14 @@ class PrivateSniptResource(ModelResource): bundle.data['raw_url'] = bundle.obj.get_raw_url() bundle.data['tags_list'] = edit_string_for_tags(bundle.obj.tags.all()) bundle.data['full_absolute_url'] = bundle.obj.get_full_absolute_url() - bundle.data['description_rendered'] = linebreaksbr(urlize(bundle.obj.description)) + bundle.data['description_rendered'] = \ + linebreaksbr(urlize(bundle.obj.description)) bundle.data['views'] = bundle.obj.views bundle.data['favs'] = bundle.obj.favs() if bundle.data['publish_date']: - bundle.data['publish_date'] = date(bundle.data['publish_date'], 'M d, Y \\a\\t h:i A') + bundle.data['publish_date'] = \ + date(bundle.data['publish_date'], 'M d, Y \\a\\t h:i A') return bundle @@ -342,8 +375,9 @@ class PrivateSniptResource(ModelResource): if 'blog_post' in bundle.data: bundle = self._clean_publish_date(bundle) - return super(PrivateSniptResource, self).obj_create(bundle, - user=bundle.request.user, **kwargs) + return super(PrivateSniptResource, self) \ + .obj_create(bundle, + user=bundle.request.user, **kwargs) def obj_update(self, bundle, **kwargs): bundle.data['user'] = bundle.request.user @@ -359,8 +393,9 @@ class PrivateSniptResource(ModelResource): if 'blog_post' in bundle.data: bundle = self._clean_publish_date(bundle) - return super(PrivateSniptResource, self).obj_update(bundle, - user=bundle.request.user, **kwargs) + return super(PrivateSniptResource, self) \ + .obj_update(bundle, + user=bundle.request.user, **kwargs) def _clean_publish_date(self, bundle): if bundle.data['blog_post'] and 'publish_date' not in bundle.data: @@ -407,6 +442,7 @@ class PrivateSniptResource(ModelResource): else: bundle.obj.tags.set() + class PrivateFavoriteResource(ModelResource): user = fields.ForeignKey(PrivateUserResource, 'user', full=True) snipt = fields.ForeignKey(PrivateSniptResource, 'snipt', full=False) @@ -414,24 +450,24 @@ class PrivateFavoriteResource(ModelResource): class Meta: queryset = Favorite.objects.all().order_by('-created') resource_name = 'favorite' - fields = ['id',] + fields = ['id'] validation = FavoriteValidation() detail_allowed_methods = ['get', 'post', 'delete'] - list_allowed_methods = ['get', 'post',] + list_allowed_methods = ['get', 'post'] authentication = ApiKeyAuthentication() authorization = PrivateFavoriteAuthorization() - ordering = ['created',] + ordering = ['created'] always_return_data = True max_limit = 200 cache = SimpleCache() def dehydrate(self, bundle): bundle.data['snipt'] = '/api/public/snipt/{}/'.format( - bundle.obj.snipt.pk) + bundle.obj.snipt.pk) return bundle def obj_create(self, bundle, **kwargs): bundle.data['user'] = bundle.request.user bundle.data['snipt'] = Snipt.objects.get(pk=bundle.data['snipt']) - return super(PrivateFavoriteResource, self).obj_create(bundle, - user=bundle.request.user, **kwargs) + return super(PrivateFavoriteResource, self) \ + .obj_create(bundle, user=bundle.request.user, **kwargs) diff --git a/snipts/forms.py b/snipts/forms.py index 8065c99..825f1a7 100644 --- a/snipts/forms.py +++ b/snipts/forms.py @@ -1,6 +1,7 @@ from django.forms import ModelForm from snipts.models import Snipt + class SniptForm(ModelForm): class Meta: model = Snipt diff --git a/snipts/models.py b/snipts/models.py index b333599..b3725f7 100644 --- a/snipts/models.py +++ b/snipts/models.py @@ -15,36 +15,40 @@ from pygments.formatters import HtmlFormatter from snipts.utils import slugify_uniquely -import datetime, hashlib, random, re +import datetime +import hashlib +import random +import re class Snipt(models.Model): """An individual Snipt.""" - user = models.ForeignKey(User, blank=True, null=True) + user = models.ForeignKey(User, blank=True, null=True) - title = models.CharField(max_length=255, blank=True, null=True, default='Untitled') - slug = models.SlugField(max_length=255, blank=True) - custom_slug = models.SlugField(max_length=255, blank=True) - tags = TaggableManager() + title = models.CharField(max_length=255, blank=True, null=True, + default='Untitled') + slug = models.SlugField(max_length=255, blank=True) + custom_slug = models.SlugField(max_length=255, blank=True) + tags = TaggableManager() - lexer = models.CharField(max_length=50) - code = models.TextField() - meta = models.TextField(blank=True, null=True) - description = models.TextField(blank=True, null=True) - stylized = models.TextField(blank=True, null=True) + lexer = models.CharField(max_length=50) + code = models.TextField() + meta = models.TextField(blank=True, null=True) + description = models.TextField(blank=True, null=True) + stylized = models.TextField(blank=True, null=True) stylized_min = models.TextField(blank=True, null=True) - embedded = models.TextField(blank=True, null=True) - line_count = models.IntegerField(blank=True, null=True, default=None) + embedded = models.TextField(blank=True, null=True) + line_count = models.IntegerField(blank=True, null=True, default=None) - key = models.CharField(max_length=100, blank=True, null=True) - public = models.BooleanField(default=False) - blog_post = models.BooleanField(default=False) + key = models.CharField(max_length=100, blank=True, null=True) + public = models.BooleanField(default=False) + blog_post = models.BooleanField(default=False) - views = models.IntegerField(default=0) - - created = models.DateTimeField(auto_now_add=True, editable=False) - modified = models.DateTimeField(auto_now=True, editable=False) + views = models.IntegerField(default=0) + + created = models.DateTimeField(auto_now_add=True, editable=False) + modified = models.DateTimeField(auto_now=True, editable=False) publish_date = models.DateTimeField(blank=True, null=True) def save(self, *args, **kwargs): @@ -53,7 +57,9 @@ class Snipt(models.Model): self.slug = slugify_uniquely(self.title, Snipt) if not self.key: - self.key = hashlib.md5(self.slug + str(datetime.datetime.now()) + str(random.random())).hexdigest() + self.key = hashlib.md5(self.slug + + str(datetime.datetime.now()) + + str(random.random())).hexdigest() if self.lexer == 'markdown': self.stylized = markdown(self.code, 'default') @@ -61,21 +67,46 @@ class Snipt(models.Model): # Snipt embeds for match in re.findall('\[\[(\w{32})\]\]', self.stylized): self.stylized = self.stylized.replace('[[' + str(match) + ']]', - '
    '.format(match, match)) + """ + +
    """.format(match, match)) # YouTube embeds - for match in re.findall('\[\[youtube-(\w{11})\-(\d+)x(\d+)\]\]', self.stylized): - self.stylized = self.stylized.replace('[[youtube-{}-{}x{}]]'.format(str(match[0]), str(match[1]), str(match[2])), - ''.format(match[1], match[2], match[0])) + for match in re.findall('\[\[youtube-(\w{11})\-(\d+)x(\d+)\]\]', + self.stylized): + self.stylized = self.stylized \ + .replace('[[youtube-{}-{}x{}]]'.format( + str(match[0]), + str(match[1]), + str(match[2])), + """""" + .format(match[1], match[2], match[0])) # Vimeo embeds - for match in re.findall('\[\[vimeo-(\d+)\-(\d+)x(\d+)\]\]', self.stylized): - self.stylized = self.stylized.replace('[[vimeo-{}-{}x{}]]'.format(str(match[0]), str(match[1]), str(match[2])), - ''.format(match[0], match[1], match[2])) + for match in re.findall('\[\[vimeo-(\d+)\-(\d+)x(\d+)\]\]', + self.stylized): + self.stylized = self.stylized \ + .replace('[[vimeo-{}-{}x{}]]'.format( + str(match[0]), + str(match[1]), + str(match[2])), + """""" + .format(match[0], match[1], match[2])) # Tweet embeds for match in re.findall('\[\[tweet-(\d+)\]\]', self.stylized): - self.stylized = self.stylized.replace('[[tweet-{}]]'.format(str(match)), '
    '.format(str(match))) + self.stylized = self.stylized \ + .replace( + '[[tweet-{}]]'.format(str(match)), + '
    ' + .format(str(match))) # Parse Snipt usernames for match in re.findall('@(\w+) ', self.stylized): @@ -85,17 +116,19 @@ class Snipt(models.Model): if user: url = user.profile.get_user_profile_url() - self.stylized = self.stylized.replace('@{} '.format(str(match)), '@{} '.format(url, match)) + self.stylized = self.stylized \ + .replace('@{} '.format(str(match)), + '@{} '.format(url, match)) else: self.stylized = highlight(self.code, - get_lexer_by_name(self.lexer, encoding='UTF-8'), + get_lexer_by_name(self.lexer, + encoding='UTF-8'), HtmlFormatter(linenos='table', - anchorlinenos=True, - lineanchors='L', - linespans='L', - ) - ) + anchorlinenos=True, + lineanchors='L', + linespans='L', + )) self.line_count = len(self.code.split('\n')) if self.lexer == 'markdown': @@ -104,7 +137,8 @@ class Snipt(models.Model): lexer_for_embedded = self.lexer embedded = highlight(self.code, - get_lexer_by_name(lexer_for_embedded, encoding='UTF-8'), + get_lexer_by_name(lexer_for_embedded, + encoding='UTF-8'), HtmlFormatter( style='native', noclasses=True, @@ -120,8 +154,8 @@ class Snipt(models.Model): -webkit-border-radius: 5px; -moz-border-radius: 5px; """)) - embedded = (embedded.replace("\\\"","\\\\\"") - .replace('\'','\\\'') + embedded = (embedded.replace("\\\"", "\\\\\"") + .replace('\'', '\\\'') .replace("\\", "\\\\") .replace('background: #202020', '')) self.embedded = embedded @@ -139,18 +173,23 @@ class Snipt(models.Model): if self.lexer == 'markdown': self.stylized_min = markdown(self.code[:1000], 'default') else: - self.stylized_min = highlight(self.code[:1000], - get_lexer_by_name(self.lexer, encoding='UTF-8'), - HtmlFormatter(linenos='table', linenospecial=1, lineanchors='line')) + self.stylized_min = highlight( + self.code[:1000], + get_lexer_by_name(self.lexer, encoding='UTF-8'), + HtmlFormatter(linenos='table', + linenospecial=1, + lineanchors='line')) return self.stylized_min def get_absolute_url(self): if self.blog_post: if self.user.profile.blog_domain: - return u'http://{}/{}/'.format(self.user.profile.blog_domain.split(' ')[0], self.slug) + return u'http://{}/{}/'.format( + self.user.profile.blog_domain.split(' ')[0], self.slug) else: - return u'https://{}.snipt.net/{}/'.format(self.user.username.replace('_', '-'), self.slug) + return u'https://{}.snipt.net/{}/'.format( + self.user.username.replace('_', '-'), self.slug) if self.custom_slug: return u'/{}/'.format(self.custom_slug) @@ -158,15 +197,18 @@ class Snipt(models.Model): if self.public: return u'/{}/{}/'.format(self.user.username, self.slug) else: - return u'/{}/{}/?key={}'.format(self.user.username, self.slug, self.key) + return u'/{}/{}/?key={}'.format( + self.user.username, self.slug, self.key) def get_full_absolute_url(self): if self.blog_post: if self.user.profile.blog_domain: - return u'http://{}/{}/'.format(self.user.profile.blog_domain.split(' ')[0], self.slug) + return u'http://{}/{}/'.format( + self.user.profile.blog_domain.split(' ')[0], self.slug) else: - return u'https://{}.snipt.net/{}/'.format(self.user.username, self.slug) + return u'https://{}.snipt.net/{}/'.format( + self.user.username, self.slug) if settings.DEBUG: root = 'http://local.snipt.net' @@ -176,7 +218,10 @@ class Snipt(models.Model): if self.public: return u'{}/{}/{}/'.format(root, self.user.username, self.slug) else: - return u'{}/{}/{}/?key={}'.format(root, self.user.username, self.slug, self.key) + return u'{}/{}/{}/?key={}'.format(root, + self.user.username, + self.slug, + self.key) def get_download_url(self): @@ -233,12 +278,14 @@ class Snipt(models.Model): else: return get_lexer_by_name(self.lexer).name + class Favorite(models.Model): snipt = models.ForeignKey(Snipt) - user = models.ForeignKey(User) + user = models.ForeignKey(User) - created = models.DateTimeField(auto_now_add=True, editable=False) + created = models.DateTimeField(auto_now_add=True, editable=False) modified = models.DateTimeField(auto_now=True, editable=False) - + def __unicode__(self): - return u'{} favorited by {}'.format(self.snipt.title, self.user.username) + return u'{} favorited by {}'.format(self.snipt.title, + self.user.username) diff --git a/snipts/search_indexes.py b/snipts/search_indexes.py index 5aa2ac9..120ddf7 100644 --- a/snipts/search_indexes.py +++ b/snipts/search_indexes.py @@ -15,4 +15,5 @@ class SniptIndex(indexes.SearchIndex, indexes.Indexable): def index_queryset(self, **kwargs): """Used when the entire index for model is updated.""" - return self.get_model().objects.filter(created__lte=datetime.datetime.now()) + return self.get_model().objects.filter( + created__lte=datetime.datetime.now()) diff --git a/snipts/templatetags/snipt_tags.py b/snipts/templatetags/snipt_tags.py index 85bced6..4a300f1 100644 --- a/snipts/templatetags/snipt_tags.py +++ b/snipts/templatetags/snipt_tags.py @@ -11,6 +11,7 @@ import hashlib register = template.Library() + @tag(register, [Constant('as'), Variable()]) def snipt_is_favorited_by_user(context, asvar): @@ -30,6 +31,7 @@ def snipt_is_favorited_by_user(context, asvar): return '' + @tag(register, []) def snipts_count_for_user(context): @@ -42,11 +44,13 @@ def snipts_count_for_user(context): return snipts + @tag(register, [Constant('as'), Variable()]) def get_lexers(context, asvar): context[asvar] = get_lexers_list() return '' + @tag(register, [Constant('for'), Variable()]) def generate_line_numbers(context, line_numbers): html = '' @@ -56,6 +60,7 @@ def generate_line_numbers(context, line_numbers): return html + @register.filter def md5(string): return hashlib.md5(string.lower()).hexdigest() diff --git a/snipts/tests.py b/snipts/tests.py index 602aee0..1e7f08a 100644 --- a/snipts/tests.py +++ b/snipts/tests.py @@ -11,11 +11,19 @@ class SniptResourceTest(ResourceTestCase): super(SniptResourceTest, self).setUp() # Johnny - self.johnny = User.objects.create_user('johnny', 'johnny@snipt.net', 'password') + self.johnny = User.objects.create_user('johnny', 'johnny@snipt.net', + 'password') ApiKey.objects.get_or_create(user=self.johnny) - self.johnny_auth = self.create_apikey(self.johnny, self.johnny.api_key.key) - self.johnny_private = Snipt(title='Private snipt for Johnny', lexer='text', public=False, user=self.johnny) - self.johnny_public = Snipt(title='Public snipt for Johnny', lexer='text', public=True, user=self.johnny) + self.johnny_auth = self.create_apikey(self.johnny, + self.johnny.api_key.key) + self.johnny_private = Snipt(title='Private snipt for Johnny', + lexer='text', + public=False, + user=self.johnny) + self.johnny_public = Snipt(title='Public snipt for Johnny', + lexer='text', + public=True, + user=self.johnny) self.johnny_private.save() self.johnny_public.save() @@ -23,16 +31,21 @@ class SniptResourceTest(ResourceTestCase): self.bob = User.objects.create_user('bob', 'bob@snipt.net', 'password') ApiKey.objects.get_or_create(user=self.bob) self.bob_auth = self.create_apikey(self.bob, self.bob.api_key.key) - self.bob_private = Snipt(title='Private snipt for Bob', lexer='text', public=False, user=self.bob) - self.bob_public = Snipt(title='Public snipt for Bob', lexer='text', public=True, user=self.bob) + self.bob_private = Snipt(title='Private snipt for Bob', + lexer='text', + public=False, + user=self.bob) + self.bob_public = Snipt(title='Public snipt for Bob', + lexer='text', + public=True, + user=self.bob) self.bob_private.save() self.bob_public.save() - - # Private def test_get_private_list(self): - resp = self.api_client.get('/api/private/snipt/', format='json', authentication=self.johnny_auth) + resp = self.api_client.get('/api/private/snipt/', format='json', + authentication=self.johnny_auth) self.assertHttpOK(resp) self.assertValidJSONResponse(resp) @@ -40,18 +53,27 @@ class SniptResourceTest(ResourceTestCase): def test_get_private_detail(self): - resp = self.api_client.get('/api/private/snipt/{}/'.format(self.johnny_private.pk), format='json', authentication=self.johnny_auth) + resp = self.api_client.get( + '/api/private/snipt/{}/'.format(self.johnny_private.pk), + format='json', + authentication=self.johnny_auth) self.assertHttpOK(resp) self.assertValidJSONResponse(resp) - self.assertEqual(self.deserialize(resp)['key'], self.johnny_private.key) + self.assertEqual(self.deserialize(resp)['key'], + self.johnny_private.key) # Unauthenticated request. - resp = self.api_client.get('/api/private/snipt/{}/'.format(self.johnny_private.pk), format='json') + resp = self.api_client.get( + '/api/private/snipt/{}/'.format(self.johnny_private.pk), + format='json') self.assertHttpUnauthorized(resp) # Unauthorized request. - resp = self.api_client.get('/api/private/snipt/{}/'.format(self.johnny_private.pk), format='json', authentication=self.bob_auth) + resp = self.api_client.get( + '/api/private/snipt/{}/'.format(self.johnny_private.pk), + format='json', + authentication=self.bob_auth) self.assertHttpUnauthorized(resp) def test_post_private_list(self): @@ -63,21 +85,21 @@ class SniptResourceTest(ResourceTestCase): } resp = self.api_client.post('/api/private/snipt/', - data=new_snipt, - format='json', - authentication=self.johnny_auth) - + data=new_snipt, + format='json', + authentication=self.johnny_auth) + self.assertHttpCreated(resp) self.assertEqual(Snipt.objects.count(), 5) - resp = self.api_client.get('/api/private/snipt/', format='json', authentication=self.johnny_auth) + resp = self.api_client.get('/api/private/snipt/', + format='json', + authentication=self.johnny_auth) self.assertEqual(len(self.deserialize(resp)['objects']), 3) resp = self.api_client.get('/api/public/snipt/', format='json') self.assertEqual(len(self.deserialize(resp)['objects']), 2) - - # Public def test_get_public_list(self): self.assertEqual(Snipt.objects.count(), 4) diff --git a/snipts/urls.py b/snipts/urls.py index e5e2971..ee9bbbb 100644 --- a/snipts/urls.py +++ b/snipts/urls.py @@ -3,23 +3,46 @@ from django.conf.urls import * from snipts import views -urlpatterns = patterns('', - - # Redirects - url(r'^s/(?P[^/]+)/(?P[^\?]+)?$', views.redirect_snipt, name='redirect-snipt'), - url(r'^(?P[^/]+)/feed/$', views.redirect_user_feed, name='redirect-feed'), - url(r'^public/tag/(?P[^/]+)/feed/$', views.redirect_public_tag_feed, name='redirect-public-tag-feed'), - url(r'^(?P[^/]+)/tag/(?P[^/]+)/feed/$', views.redirect_user_tag_feed, name='redirect-user-tag-feed'), - - url(r'^public/$', views.list_public, name='list-public'), - url(r'^public/tag/(?P[^/]+)/$', views.list_public, name='list-public-tag'), - url(r'^download/(?P[^/]+).*$', views.download, name='download'), - url(r'^embed/(?P[^/]+)/$', views.embed, name='embed'), - url(r'^raw/(?P[^/]+)/(?P[^\?]+)?$', views.raw, name='raw'), - url(r'^(?P[^/]+)/$', views.list_user, name='list-user'), - url(r'^(?P[^/]+)/tag/(?P[^/]+)/$', views.list_user, name='list-user-tag'), - url(r'^(?P[^/]+)/favorites/$', views.favorites, name='favorites'), - url(r'^(?P[^/]+)/blog-posts/$', views.blog_posts, name='blog-posts'), - url(r'^(?P[^/]+)/(?P[^/]+)/$', views.detail, name='detail'), - -) +urlpatterns = \ + patterns('', + url(r'^s/(?P[^/]+)/(?P[^\?]+)?$', + views.redirect_snipt, name='redirect-snipt'), + url(r'^(?P[^/]+)/feed/$', + views.redirect_user_feed, + name='redirect-feed'), + url(r'^public/tag/(?P[^/]+)/feed/$', + views.redirect_public_tag_feed, + name='redirect-public-tag-feed'), + url(r'^(?P[^/]+)/tag/(?P[^/]+)/feed/$', + views.redirect_user_tag_feed, + name='redirect-user-tag-feed'), + url(r'^public/$', + views.list_public, + name='list-public'), + url(r'^public/tag/(?P[^/]+)/$', + views.list_public, + name='list-public-tag'), + url(r'^download/(?P[^/]+).*$', + views.download, + name='download'), + url(r'^embed/(?P[^/]+)/$', + views.embed, + name='embed'), + url(r'^raw/(?P[^/]+)/(?P[^\?]+)?$', + views.raw, + name='raw'), + url(r'^(?P[^/]+)/$', + views.list_user, + name='list-user'), + url(r'^(?P[^/]+)/tag/(?P[^/]+)/$', + views.list_user, + name='list-user-tag'), + url(r'^(?P[^/]+)/favorites/$', + views.favorites, + name='favorites'), + url(r'^(?P[^/]+)/blog-posts/$', + views.blog_posts, + name='blog-posts'), + url(r'^(?P[^/]+)/(?P[^/]+)/$', + views.detail, + name='detail')) diff --git a/snipts/utils.py b/snipts/utils.py index bfa5fe0..909b4fc 100644 --- a/snipts/utils.py +++ b/snipts/utils.py @@ -17,6 +17,7 @@ def slugify_uniquely(value, model, slugfield="slug"): return potential suffix = str(uuid.uuid4()).split('-')[0] + def activate_user(user, request, **kwargs): user.is_active = True user.save() @@ -25,6 +26,7 @@ def activate_user(user, request, **kwargs): password=request.POST['password1']) login(request, user) + def get_lexers_list(): lexers = list(get_all_lexers()) diff --git a/snipts/views.py b/snipts/views.py index e303e79..5640e11 100644 --- a/snipts/views.py +++ b/snipts/views.py @@ -62,10 +62,12 @@ def detail(request, username, snipt_slug): 'user': user, } + def download(request, snipt_key): snipt = get_object_or_404(Snipt, key=snipt_key) return HttpResponse(snipt.code, content_type='application/x-download') + def embed(request, snipt_key): snipt = get_object_or_404(Snipt, key=snipt_key) @@ -75,6 +77,7 @@ def embed(request, snipt_key): context_instance=RequestContext(request), content_type='application/javascript') + @render_to('snipts/list-user.html') def blog_posts(request, username): @@ -92,7 +95,8 @@ def blog_posts(request, username): public_user = True user = get_object_or_404(User, username=username) snipts = Snipt.objects.filter(blog_post=True, user=user, public=True) - tags = Tag.objects.filter(snipt__user=user, snipt__public=True).distinct() + tags = Tag.objects.filter(snipt__user=user, + snipt__public=True).distinct() tags = tags.order_by('name') snipts = snipts.order_by('-created') @@ -112,6 +116,7 @@ def blog_posts(request, username): return context + @render_to('snipts/list-user.html') def favorites(request, username): @@ -148,6 +153,7 @@ def favorites(request, username): return context + @render_to('snipts/list-public.html') def list_public(request, tag_slug=None): @@ -175,6 +181,7 @@ def list_public(request, tag_slug=None): return context + @render_to('snipts/list-user.html') def list_user(request, username_or_custom_slug, tag_slug=None): @@ -190,7 +197,8 @@ def list_user(request, username_or_custom_slug, tag_slug=None): tags = Tag.objects snipts = Snipt.objects - if user == request.user or (request.GET.get('api_key') == user.api_key.key): + if user == request.user or \ + (request.GET.get('api_key') == user.api_key.key): public = False favorites = Favorite.objects.filter(user=user).values('snipt') @@ -232,6 +240,7 @@ def list_user(request, username_or_custom_slug, tag_slug=None): return context + def raw(request, snipt_key, lexer=None): snipt = get_object_or_404(Snipt, key=snipt_key) @@ -250,23 +259,23 @@ def raw(request, snipt_key, lexer=None): snipt.lexer = lexer snipt.save() - content_type='text/plain' + content_type = 'text/plain' if 'nice' in request.GET: - content_type='text/html' + content_type = 'text/html' return render_to_response('snipts/raw.html', {'snipt': snipt}, context_instance=RequestContext(request), content_type=content_type) + def rss(request, context): - return render_to_response( - 'rss.xml', - context, - context_instance=RequestContext(request), - content_type="application/rss+xml" - ) + return render_to_response('rss.xml', + context, + context_instance=RequestContext(request), + content_type="application/rss+xml") + def search(request, template='search/search.html', load_all=True, form_class=ModelSearchForm, searchqueryset=None, @@ -279,12 +288,18 @@ def search(request, template='search/search.html', load_all=True, # We have a query. if request.GET.get('q'): - if request.user.is_authenticated() and '--mine' in request.GET.get('q'): - searchqueryset = SearchQuerySet().filter(author=request.user).order_by('-pub_date') + if request.user.is_authenticated() and '--mine' in \ + request.GET.get('q'): + searchqueryset = SearchQuerySet().filter(author=request.user) \ + .order_by('-pub_date') else: - searchqueryset = SearchQuerySet().filter(Q(public=True) | Q(author=request.user)).order_by('-pub_date') + searchqueryset = SearchQuerySet() \ + .filter(Q(public=True) | Q(author=request.user)) \ + .order_by('-pub_date') - form = ModelSearchForm(request.GET, searchqueryset=searchqueryset, load_all=load_all) + form = ModelSearchForm(request.GET, + searchqueryset=searchqueryset, + load_all=load_all) if form.is_valid(): query = form.cleaned_data['q'] @@ -314,19 +329,24 @@ def search(request, template='search/search.html', load_all=True, if extra_context: context.update(extra_context) - return render_to_response(template, context, context_instance=context_class(request)) + return render_to_response(template, + context, + context_instance=context_class(request)) def redirect_snipt(request, snipt_key, lexer=None): snipt = get_object_or_404(Snipt, key=snipt_key) return HttpResponseRedirect(snipt.get_absolute_url()) + def redirect_public_tag_feed(request, tag_slug): return HttpResponseRedirect('/public/tag/{}/?rss'.format(tag_slug)) + def redirect_user_feed(request, username): user = get_object_or_404(User, username=username) return HttpResponseRedirect(user.get_absolute_url() + '?rss') + def redirect_user_tag_feed(request, username, tag_slug): return HttpResponseRedirect(u'/{}/tag/{}/?rss'.format(username, tag_slug)) diff --git a/templates/analytics.html b/templates/analytics.html index 5a3d968..99e1442 100644 --- a/templates/analytics.html +++ b/templates/analytics.html @@ -66,7 +66,6 @@ {% endif %} {% else %} {% endif %} diff --git a/urls.py b/urls.py index ddd3b0c..1b2fb91 100644 --- a/urls.py +++ b/urls.py @@ -1,6 +1,4 @@ -from django.conf import settings from django.conf.urls import include, patterns, url -from django.conf.urls.static import static from django.views.generic import TemplateView from django.http import HttpResponseRedirect from django.contrib import admin @@ -11,11 +9,9 @@ from snipts.api import (PublicSniptResource, from snipts.views import search from tastypie.api import Api from utils.views import SniptRegistrationView -from jobs.views import jobs, jobs_json from views import (homepage, lexers, login_redirect, pro, sitemap, tags, pro_complete, user_api_key, for_teams, for_teams_complete) -import admin as custom_admin import os @@ -32,47 +28,56 @@ private_api.register(PrivateUserResource()) private_api.register(PrivateFavoriteResource()) private_api.register(PrivateUserProfileResource()) -urlpatterns = patterns('', +urlpatterns = \ + patterns('', - url(r'^$', homepage), - url(r'^login-redirect/$', login_redirect), + url(r'^$', homepage), + url(r'^login-redirect/$', login_redirect), - url(r'^admin/', include(admin.site.urls)), + url(r'^admin/', include(admin.site.urls)), - url(r'^404/$', TemplateView.as_view(template_name='404.html')), - url(r'^500/$', TemplateView.as_view(template_name='500.html')), + url(r'^404/$', TemplateView.as_view(template_name='404.html')), + url(r'^500/$', TemplateView.as_view(template_name='500.html')), - url(r'^robots.txt$', TemplateView.as_view(template_name='robots.txt')), - url(r'^humans.txt$', TemplateView.as_view(template_name='humans.txt')), - url(r'^sitemap.xml$', sitemap), - url(r'^tags/$', tags), + url(r'^robots.txt$', + TemplateView.as_view(template_name='robots.txt')), + url(r'^humans.txt$', + TemplateView.as_view(template_name='humans.txt')), + url(r'^sitemap.xml$', sitemap), + url(r'^tags/$', tags), - url(r'^pro/$', pro), - url(r'^pro/complete/$', pro_complete), + url(r'^pro/$', pro), + url(r'^pro/complete/$', pro_complete), - url(r'^for-teams/$', for_teams), - url(r'^for-teams/complete/$', for_teams_complete), + url(r'^for-teams/$', for_teams), + url(r'^for-teams/complete/$', for_teams_complete), - url(r'^account/', include('accounts.urls')), + url(r'^account/', include('accounts.urls')), - url(r'^api/public/lexer/$', lexers), + url(r'^api/public/lexer/$', lexers), - url(r'^api/private/key/$', user_api_key), - url(r'^api/', include(public_api.urls)), - url(r'^api/', include(private_api.urls)), + url(r'^api/private/key/$', user_api_key), + url(r'^api/', include(public_api.urls)), + url(r'^api/', include(private_api.urls)), - url(r'^search/$', search), + url(r'^search/$', search), - url(r'^register/$', lambda x: HttpResponseRedirect('/signup/')), - url(r'^signup/$', SniptRegistrationView.as_view(), - name='registration_register'), - url(r'', include('registration.backends.default.urls')), + url(r'^register/$', lambda x: HttpResponseRedirect('/signup/')), + url(r'^signup/$', SniptRegistrationView.as_view(), + name='registration_register'), + url(r'', include('registration.backends.default.urls')), - url(r'^', include('snipts.urls')), + url(r'^', include('snipts.urls')), - url(r'^(?Pfavicon\.ico)$', 'django.views.static.serve', {'document_root': os.path.join(os.path.dirname(__file__), 'static/img')}), -) + url(r'^(?Pfavicon\.ico)$', 'django.views.static.serve', { + 'document_root': os.path.join(os.path.dirname(__file__), + 'static/img') + }), + ) -urlpatterns += patterns('', - (r'^static/(?P.*)$', 'django.views.static.serve', {'document_root': os.path.join(os.path.dirname(__file__), 'media')}), -) +urlpatterns += \ + patterns('', + (r'^static/(?P.*)$', 'django.views.static.serve', { + 'document_root': os.path.join(os.path.dirname(__file__), + 'media') + })) diff --git a/utils/backends.py b/utils/backends.py index 4939337..aec16b1 100644 --- a/utils/backends.py +++ b/utils/backends.py @@ -1,6 +1,6 @@ -from django.conf import settings from django.contrib.auth.models import User + class EmailOrUsernameModelBackend(object): def authenticate(self, username=None, password=None): if '@' in username: diff --git a/utils/forms.py b/utils/forms.py index f178b8f..81a1525 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -3,21 +3,22 @@ from registration.forms import RegistrationForm from django.contrib.auth.models import User from django import forms + class SniptRegistrationForm(RegistrationForm): """ Subclass of ``RegistrationForm`` which enforces uniqueness of email addresses and further restricts usernames. - """ def clean_username(self): """ Validate that the username is alphanumeric and is not already in use. - """ - existing = User.objects.filter(username__iexact=self.cleaned_data['username']) + existing = User.objects.filter( + username__iexact=self.cleaned_data['username']) if existing.exists(): - raise forms.ValidationError(_("A user with that username already exists.")) + raise forms.ValidationError( + _("A user with that username already exists.")) elif '@' in self.cleaned_data['username']: raise forms.ValidationError(_("Cannot have '@' in username.")) @@ -33,8 +34,9 @@ class SniptRegistrationForm(RegistrationForm): """ Validate that the supplied email address is unique for the site. - """ if User.objects.filter(email__iexact=self.cleaned_data['email']): - raise forms.ValidationError(_("This email address is already in use. Please supply a different email address.")) + raise forms.ValidationError( + _("""This email address is already in use. Please supply a + different email address.""")) return self.cleaned_data['email'] diff --git a/utils/templatetags/intercom.py b/utils/templatetags/intercom.py index 8fc76c2..da1e58f 100644 --- a/utils/templatetags/intercom.py +++ b/utils/templatetags/intercom.py @@ -1,10 +1,16 @@ from django.conf import settings from django import template -import hmac, hashlib, os +import hmac +import hashlib +import os register = template.Library() + @register.filter def intercom_sha_256(user_id): - return hmac.new(os.environ.get('INTERCOM_SECRET_KEY', settings.INTERCOM_SECRET_KEY), str(user_id), digestmod=hashlib.sha256).hexdigest() + return hmac.new(os.environ.get('INTERCOM_SECRET_KEY', + settings.INTERCOM_SECRET_KEY), + str(user_id), + digestmod=hashlib.sha256).hexdigest() diff --git a/utils/templatetags/verbatim.py b/utils/templatetags/verbatim.py index a956607..f2057c4 100644 --- a/utils/templatetags/verbatim.py +++ b/utils/templatetags/verbatim.py @@ -20,7 +20,7 @@ class VerbatimNode(template.Node): def __init__(self, text): self.text = text - + def render(self, context): return self.text diff --git a/utils/views.py b/utils/views.py index 7204558..029e5ab 100644 --- a/utils/views.py +++ b/utils/views.py @@ -1,9 +1,9 @@ from registration.backends.default.views import RegistrationView from utils.forms import SniptRegistrationForm + class SniptRegistrationView(RegistrationView): """ Custom registration view that uses our custom form. - """ form_class = SniptRegistrationForm diff --git a/views.py b/views.py index c73f22d..fb233c3 100644 --- a/views.py +++ b/views.py @@ -1,10 +1,9 @@ from accounts.models import UserProfile -from annoying.decorators import ajax_request +from annoying.decorators import ajax_request, render_to from blogs.views import blog_list from django.http import HttpResponseRedirect, HttpResponseBadRequest from django.conf import settings from django.contrib.auth.decorators import login_required -from annoying.decorators import ajax_request, render_to from django.shortcuts import render_to_response from django.template import RequestContext from snipts.utils import get_lexers_list @@ -19,6 +18,7 @@ import hashlib import os import stripe + @render_to('for-teams.html') def for_teams(request): if request.user.is_authenticated(): @@ -27,6 +27,7 @@ def for_teams(request): profile.save() return {} + @render_to('for-teams-complete.html') def for_teams_complete(request): @@ -43,7 +44,8 @@ def for_teams_complete(request): Info: %s - """ % (request.user.username, request.user.email, name, members, info), 'support@snipt.net', + """ % (request.user.username, request.user.email, name, members, + info), 'support@snipt.net', ['nick@nicksergeant.com'], fail_silently=False) profile = request.user.profile @@ -70,6 +72,7 @@ def for_teams_complete(request): else: return HttpResponseBadRequest() + @render_to('homepage.html') def homepage(request): @@ -99,6 +102,7 @@ def homepage(request): 'users_count': User.objects.all().count(), } + @ajax_request def lexers(request): lexers = get_lexers_list() @@ -125,12 +129,14 @@ def lexers(request): return {'objects': objects} + def login_redirect(request): if request.user.is_authenticated(): return HttpResponseRedirect('/' + request.user.username + '/') else: return HttpResponseRedirect('/') + @login_required @render_to('pro.html') def pro(request): @@ -138,6 +144,7 @@ def pro(request): return HttpResponseRedirect('/' + request.user.username + '/') return {} + @login_required @render_to('pro-complete.html') def pro_complete(request): @@ -145,7 +152,8 @@ def pro_complete(request): if request.method == 'POST': token = request.POST['token'] - stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', settings.STRIPE_SECRET_KEY) + stripe.api_key = os.environ.get('STRIPE_SECRET_KEY', + settings.STRIPE_SECRET_KEY) if 'plan' in request.GET: plan = request.GET['plan'] @@ -158,7 +166,8 @@ def pro_complete(request): email=request.user.email) except stripe.CardError, e: error_message = e.json_body['error']['message'] - return HttpResponseRedirect('/pro/?declined=%s' % error_message or 'Your card was declined.') + return HttpResponseRedirect('/pro/?declined=%s' % error_message or + 'Your card was declined.') profile = request.user.profile profile.is_pro = True @@ -171,6 +180,7 @@ def pro_complete(request): else: return HttpResponseBadRequest() + def sitemap(request): tags = Tag.objects.filter(snipt__public=True) @@ -182,6 +192,7 @@ def sitemap(request): context_instance=RequestContext(request), content_type='application/xml') + @render_to('tags.html') def tags(request): @@ -198,13 +209,14 @@ def tags(request): 'all_tags': all_tags, 'tags': popular_tags } + + @ajax_request def user_api_key(request): - if not request.user.is_authenticated(): - return HttpResponseBadRequest() - - return { - 'api_key': request.user.api_key.key - } + if not request.user.is_authenticated(): + return HttpResponseBadRequest() + return { + 'api_key': request.user.api_key.key + }