From 53f05dbb6d822a9597c5edca0e7091dc63bec5c3 Mon Sep 17 00:00:00 2001 From: Krzysztof Klimonda Date: Sun, 3 Mar 2013 04:47:34 -0800 Subject: [PATCH] A general project refactor Removed pins django app, and moved code to the core. Moved user related code out of core to the users app. --- pinry/core/api.py | 4 +- pinry/core/auth/backends.py | 4 +- pinry/core/forms.py | 46 +++++---------- pinry/core/models.py | 43 +++++++++++--- pinry/core/tests.py | 4 +- pinry/core/urls.py | 20 +++---- pinry/core/views.py | 54 ++++++------------ pinry/{pins => }/fixtures/test_resources.json | 0 pinry/pins/__init__.py | 0 pinry/pins/forms.py | 19 ------- pinry/pins/models.py | 39 ------------- pinry/pins/tests.py | 37 ------------ pinry/pins/urls.py | 15 ----- pinry/pins/utils.py | 56 ------------------- pinry/pins/views.py | 30 ---------- pinry/settings/__init__.py | 2 +- pinry/templates/base.html | 8 +-- pinry/templates/user/login.html | 2 +- pinry/templates/user/register.html | 2 +- pinry/urls.py | 4 +- pinry/users/__init__.py | 1 + pinry/users/forms.py | 38 +++++++++++++ pinry/users/models.py | 12 ++++ pinry/users/urls.py | 10 ++++ pinry/users/views.py | 44 +++++++++++++++ 25 files changed, 196 insertions(+), 298 deletions(-) rename pinry/{pins => }/fixtures/test_resources.json (100%) delete mode 100644 pinry/pins/__init__.py delete mode 100644 pinry/pins/forms.py delete mode 100644 pinry/pins/models.py delete mode 100644 pinry/pins/tests.py delete mode 100644 pinry/pins/urls.py delete mode 100644 pinry/pins/utils.py delete mode 100644 pinry/pins/views.py create mode 100644 pinry/users/__init__.py create mode 100644 pinry/users/forms.py create mode 100644 pinry/users/models.py create mode 100644 pinry/users/urls.py create mode 100644 pinry/users/views.py diff --git a/pinry/core/api.py b/pinry/core/api.py index 259951b..e9fe5da 100644 --- a/pinry/core/api.py +++ b/pinry/core/api.py @@ -4,8 +4,8 @@ from tastypie.exceptions import Unauthorized from tastypie.resources import ModelResource from django_images.models import Thumbnail -from pinry.core.models import User -from pinry.pins.models import Image, Pin +from .models import Pin, Image +from ..users.models import User class PinryAuthorization(DjangoAuthorization): diff --git a/pinry/core/auth/backends.py b/pinry/core/auth/backends.py index 149defc..f4c95c1 100644 --- a/pinry/core/auth/backends.py +++ b/pinry/core/auth/backends.py @@ -1,7 +1,7 @@ from django.core.validators import email_re -from pinry.core.models import User -from pinry.pins.models import Pin +from pinry.core.models import Pin +from pinry.users.models import User class CombinedAuthBackend(object): diff --git a/pinry/core/forms.py b/pinry/core/forms.py index 6fe0770..7c080b7 100644 --- a/pinry/core/forms.py +++ b/pinry/core/forms.py @@ -1,38 +1,18 @@ from django import forms -from django.utils.translation import ugettext, ugettext_lazy as _ -from django.contrib.auth.models import User + +from django_images.models import Image -class UserCreationForm(forms.ModelForm): - """ - A form that creates a user, with no privileges, from the given username, - email, and password. - """ - error_messages = { - 'duplicate_username': _("A user with that username already exists."), - } - username = forms.RegexField(label=_("Username"), max_length=30, - regex=r'^[\w-]+$') - password = forms.CharField(label=_("Password"), - widget=forms.PasswordInput) +FIELD_NAME_MAPPING = { + 'image': 'qqfile', +} + + +class ImageForm(forms.ModelForm): + def add_prefix(self, field_name): + field_name = FIELD_NAME_MAPPING.get(field_name, field_name) + return super(ImageForm, self).add_prefix(field_name) class Meta: - model = User - fields = ("username", "email") - - def clean_username(self): - # Since User.username is unique, this check is redundant, - # but it sets a nicer error message than the ORM. See #13147. - username = self.cleaned_data["username"] - try: - User._default_manager.get(username=username) - except User.DoesNotExist: - return username - raise forms.ValidationError(self.error_messages['duplicate_username']) - - def save(self, commit=True): - user = super(UserCreationForm, self).save(commit=False) - user.set_password(self.cleaned_data["password"]) - if commit: - user.save() - return user + model = Image + fields = ('image',) \ No newline at end of file diff --git a/pinry/core/models.py b/pinry/core/models.py index 0557b7e..cc14090 100644 --- a/pinry/core/models.py +++ b/pinry/core/models.py @@ -1,10 +1,39 @@ -import hashlib -from django.contrib.auth.models import User as BaseUser +import urllib2 +from cStringIO import StringIO -class User(BaseUser): - @property - def gravatar(self): - return hashlib.md5(self.email).hexdigest() +from django.core.files.uploadedfile import InMemoryUploadedFile +from django.db import models + +from django_images.models import Image as BaseImage +from taggit.managers import TaggableManager + +from ..users.models import User + + +class ImageManager(models.Manager): + # FIXME: Move this into an asynchronous task + def create_for_url(self, url): + file_name = url.split("/")[-1] + buf = StringIO() + buf.write(urllib2.urlopen(url).read()) + obj = InMemoryUploadedFile(buf, 'image', file_name, None, buf.tell(), None) + return Image.objects.create(image=obj) + + +class Image(BaseImage): + objects = ImageManager() class Meta: - proxy = True \ No newline at end of file + proxy = True + + +class Pin(models.Model): + submitter = models.ForeignKey(User) + url = models.TextField(blank=True, null=True) + description = models.TextField(blank=True, null=True) + image = models.ForeignKey(Image, related_name='pin') + published = models.DateTimeField(auto_now_add=True) + tags = TaggableManager() + + def __unicode__(self): + return self.url \ No newline at end of file diff --git a/pinry/core/tests.py b/pinry/core/tests.py index e13b819..08241a0 100644 --- a/pinry/core/tests.py +++ b/pinry/core/tests.py @@ -4,11 +4,11 @@ from django.conf import settings from django.test.client import Client from django_images.models import Thumbnail - from taggit.models import Tag from tastypie.test import ResourceTestCase -from ..pins.models import User, Pin, Image +from .models import Pin, Image +from ..users.models import User def filter_generator_for(size): diff --git a/pinry/core/urls.py b/pinry/core/urls.py index a9eb392..a17743a 100644 --- a/pinry/core/urls.py +++ b/pinry/core/urls.py @@ -1,9 +1,10 @@ from django.conf.urls import patterns, include, url +from django.views.generic import TemplateView from tastypie.api import Api from .api import ImageResource, ThumbnailResource, PinResource, UserResource -from .views import CreateUser +from .views import CreateImage v1_api = Api(api_name='v1') @@ -13,18 +14,15 @@ v1_api.register(PinResource()) v1_api.register(UserResource()) -urlpatterns = patterns('', -) - - urlpatterns = patterns('', url(r'^api/', include(v1_api.urls, namespace='api')), - url(r'^$', 'pinry.core.views.home', name='home'), - + url(r'^pin-form/$', TemplateView.as_view(template_name='core/pin_form.html'), + name='pin-form'), + url(r'^create-image/$', CreateImage.as_view(), name='create-image'), + url(r'^tag/(?P(\w|-)+)/$', TemplateView.as_view(template_name='core/pins.html'), + name='tag-pins'), url(r'^private/$', 'pinry.core.views.private', name='private'), - url(r'^login/$', 'django.contrib.auth.views.login', - {'template_name': 'user/login.html'}, name='login'), - url(r'^logout/$', 'pinry.core.views.logout_user', name='logout'), - url(r'^register/$', CreateUser.as_view(), name='register'), + url(r'^$', TemplateView.as_view(template_name='core/pins.html'), + name='recent-pins'), ) diff --git a/pinry/core/views.py b/pinry/core/views.py index 2f74804..722f96e 100644 --- a/pinry/core/views.py +++ b/pinry/core/views.py @@ -1,53 +1,35 @@ -from django.contrib.auth.models import Permission from django.template.response import TemplateResponse from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse -from django.contrib.auth.decorators import login_required -from django.contrib.auth import logout, authenticate, login -from django.contrib import messages -from django.conf import settings -from django.utils.functional import lazy from django.views.generic import CreateView +from django_images.models import Image -from .forms import UserCreationForm -from .models import User +from braces.views import JSONResponseMixin, LoginRequiredMixin - -reverse_lazy = lambda name=None, *args : lazy(reverse, str)(name, args=args) - - -def home(request): - return HttpResponseRedirect(reverse('pins:recent-pins')) +from .forms import ImageForm def private(request): return TemplateResponse(request, 'user/private.html', None) -class CreateUser(CreateView): - template_name = 'user/register.html' - model = User - form_class = UserCreationForm - success_url = reverse_lazy('pins:recent-pins') +class CreateImage(JSONResponseMixin, LoginRequiredMixin, CreateView): + template_name = None # JavaScript-only view + model = Image + form_class = ImageForm def get(self, request, *args, **kwargs): - if not settings.ALLOW_NEW_REGISTRATIONS: - messages.error(request, "The admin of this service is not allowing new registrations.") - return HttpResponseRedirect(reverse('pins:recent-pins')) - return super(CreateUser, self).get(request, *args, **kwargs) + if not request.is_ajax(): + return HttpResponseRedirect(reverse('core:recent-pins')) + super(CreateImage, self).get(request, *args, **kwargs) def form_valid(self, form): - redirect = super(CreateUser, self).form_valid(form) - permissions = Permission.objects.filter(codename__in=['add_pin', 'add_image']) - user = authenticate(username=form.cleaned_data['username'], - password=form.cleaned_data['password']) - user.user_permissions = permissions - login(self.request, user) - return redirect + image = form.save() + return self.render_json_response({ + 'success': { + 'id': image.id + } + }) - -@login_required -def logout_user(request): - logout(request) - messages.success(request, 'You have successfully logged out.') - return HttpResponseRedirect(reverse('core:home')) + def form_invalid(self, form): + return self.render_json_response({'error': form.errors}) \ No newline at end of file diff --git a/pinry/pins/fixtures/test_resources.json b/pinry/fixtures/test_resources.json similarity index 100% rename from pinry/pins/fixtures/test_resources.json rename to pinry/fixtures/test_resources.json diff --git a/pinry/pins/__init__.py b/pinry/pins/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pinry/pins/forms.py b/pinry/pins/forms.py deleted file mode 100644 index bf0b3ed..0000000 --- a/pinry/pins/forms.py +++ /dev/null @@ -1,19 +0,0 @@ -from django import forms - -from django_images.models import Image - - -FIELD_NAME_MAPPING = { - 'image': 'qqfile', -} - - -class ImageForm(forms.ModelForm): - - def add_prefix(self, field_name): - field_name = FIELD_NAME_MAPPING.get(field_name, field_name) - return super(ImageForm, self).add_prefix(field_name) - - class Meta: - model = Image - fields = ('image',) diff --git a/pinry/pins/models.py b/pinry/pins/models.py deleted file mode 100644 index 422943a..0000000 --- a/pinry/pins/models.py +++ /dev/null @@ -1,39 +0,0 @@ -import urllib2 -from cStringIO import StringIO - -from django.core.files.uploadedfile import InMemoryUploadedFile -from django.db import models - -from django_images.models import Image as BaseImage -from taggit.managers import TaggableManager - -from ..core.models import User - - -class ImageManager(models.Manager): - # FIXME: Move this into an asynchronous task - def create_for_url(self, url): - file_name = url.split("/")[-1] - buf = StringIO() - buf.write(urllib2.urlopen(url).read()) - obj = InMemoryUploadedFile(buf, 'image', file_name, None, buf.tell(), None) - return Image.objects.create(image=obj) - - -class Image(BaseImage): - objects = ImageManager() - - class Meta: - proxy = True - - -class Pin(models.Model): - submitter = models.ForeignKey(User) - url = models.TextField(blank=True, null=True) - description = models.TextField(blank=True, null=True) - image = models.ForeignKey(Image, related_name='pin') - published = models.DateTimeField(auto_now_add=True) - tags = TaggableManager() - - def __unicode__(self): - return self.url diff --git a/pinry/pins/tests.py b/pinry/pins/tests.py deleted file mode 100644 index 69faeeb..0000000 --- a/pinry/pins/tests.py +++ /dev/null @@ -1,37 +0,0 @@ -from django.core.urlresolvers import reverse -from django.test import TestCase - -from pinry.core.models import User - - -class CreateImageTest(TestCase): - fixtures = ['test_resources.json'] - - def setUp(self): - self.client.login(username='jdoe', password='password') - - def test_form_post_unauthenticated(self): - post_data = { - 'image': 'foobar.jpg' - } - self.client.logout() - response = self.client.post(reverse('pins:new-pin'), data=post_data) - expected_url = '{login_url}?next={next_url}'.format(**{ - 'login_url': reverse('core:login'), - 'next_url': reverse('pins:new-pin') - }) - self.assertRedirects(response, expected_url=expected_url) - - def test_form_post_browser(self): - post_data = { - 'image': 'foobar.jpg' - } - response = self.client.post(reverse('pins:new-pin'), data=post_data) - self.assertEqual(response.status_code, 200) - - def test_form_post_ajax(self): - post_data = { - 'image': 'foobar.jpg' - } - response = self.client.post(reverse('pins:new-pin'), data=post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertEqual(response.status_code, 200) \ No newline at end of file diff --git a/pinry/pins/urls.py b/pinry/pins/urls.py deleted file mode 100644 index 6e94fa7..0000000 --- a/pinry/pins/urls.py +++ /dev/null @@ -1,15 +0,0 @@ -from django.conf.urls import patterns, url -from django.views.generic import TemplateView - -from .views import CreateImage - - -urlpatterns = patterns('pinry.pins.views', - url(r'^pin-form/$', TemplateView.as_view(template_name='core/pin_form.html'), - name='pin-form'), - url(r'^create-image/$', CreateImage.as_view(), name='create-image'), - url(r'^tag/(?P(\w|-)+)/$', TemplateView.as_view(template_name='core/pins.html'), - name='tag-pins'), - url(r'^$', TemplateView.as_view(template_name='core/pins.html'), - name='recent-pins'), -) diff --git a/pinry/pins/utils.py b/pinry/pins/utils.py deleted file mode 100644 index fe0138a..0000000 --- a/pinry/pins/utils.py +++ /dev/null @@ -1,56 +0,0 @@ -import PIL -import mimetypes - -mimetypes.init() - - -# this neat function is based on django-images and easy-thumbnails -def scale_and_crop(image, size, crop=False, upscale=False, quality=None): - # Open image and store format/metadata. - image.open() - im = PIL.Image.open(image) - im_format, im_info = im.format, im.info - if quality: - im_info['quality'] = quality - - # Force PIL to load image data. - im.load() - - source_x, source_y = [float(v) for v in im.size] - target_x, target_y = [float(v) for v in size] - - if crop or not target_x or not target_y: - scale = max(target_x / source_x, target_y / source_y) - else: - scale = min(target_x / source_x, target_y / source_y) - - # Handle one-dimensional targets. - if not target_x: - target_x = source_x * scale - elif not target_y: - target_y = source_y * scale - - if scale < 1.0 or (scale > 1.0 and upscale): - im = im.resize((int(source_x * scale), int(source_y * scale)), - resample=PIL.Image.ANTIALIAS) - - if crop: - # Use integer values now. - source_x, source_y = im.size - # Difference between new image size and requested size. - diff_x = int(source_x - min(source_x, target_x)) - diff_y = int(source_y - min(source_y, target_y)) - if diff_x or diff_y: - # Center cropping (default). - halfdiff_x, halfdiff_y = diff_x // 2, diff_y // 2 - box = [halfdiff_x, halfdiff_y, - min(source_x, int(target_x) + halfdiff_x), - min(source_y, int(target_y) + halfdiff_y)] - # Finally, crop the image! - im = im.crop(box) - - # Close image and replace format/metadata, as PIL blows this away. - im.format, im.info = im_format, im_info - image.close() - return im - diff --git a/pinry/pins/views.py b/pinry/pins/views.py deleted file mode 100644 index ee586de..0000000 --- a/pinry/pins/views.py +++ /dev/null @@ -1,30 +0,0 @@ -from django.core.urlresolvers import reverse -from django.http import HttpResponseRedirect -from django.views.generic import CreateView - -from braces.views import LoginRequiredMixin, JSONResponseMixin -from django_images.models import Image - -from .forms import ImageForm - - -class CreateImage(JSONResponseMixin, LoginRequiredMixin, CreateView): - template_name = None # JavaScript-only view - model = Image - form_class = ImageForm - - def get(self, request, *args, **kwargs): - if not request.is_ajax(): - return HttpResponseRedirect(reverse('pins:recent-pins')) - super(CreateImage, self).get(request, *args, **kwargs) - - def form_valid(self, form): - image = form.save() - return self.render_json_response({ - 'success': { - 'id': image.id - } - }) - - def form_invalid(self, form): - return self.render_json_response({'error': form.errors}) diff --git a/pinry/settings/__init__.py b/pinry/settings/__init__.py index 91cf68a..439e1ae 100644 --- a/pinry/settings/__init__.py +++ b/pinry/settings/__init__.py @@ -85,7 +85,7 @@ INSTALLED_APPS = ( 'taggit', 'django_images', 'pinry.core', - 'pinry.pins', + 'pinry.users', ) IMAGE_SIZES = { diff --git a/pinry/templates/base.html b/pinry/templates/base.html index f564825..a9d9b97 100644 --- a/pinry/templates/base.html +++ b/pinry/templates/base.html @@ -44,15 +44,15 @@