@@ -59,7 +59,7 @@ class UserResource(ModelResource): | |||||
def filter_generator_for(size): | def filter_generator_for(size): | ||||
def wrapped_func(bundle, **kwargs): | def wrapped_func(bundle, **kwargs): | ||||
return Thumbnail.objects.get_or_create_at_size(bundle.obj.pk, size, **kwargs) | |||||
return bundle.obj.get_by_size(size) | |||||
return wrapped_func | return wrapped_func | ||||
@@ -1,10 +1,11 @@ | |||||
import requests | import requests | ||||
from cStringIO import StringIO | from cStringIO import StringIO | ||||
from django.conf import settings | |||||
from django.core.files.uploadedfile import InMemoryUploadedFile | from django.core.files.uploadedfile import InMemoryUploadedFile | ||||
from django.db import models | |||||
from django.db import models, transaction | |||||
from django_images.models import Image as BaseImage | |||||
from django_images.models import Image as BaseImage, Thumbnail | |||||
from taggit.managers import TaggableManager | from taggit.managers import TaggableManager | ||||
from ..users.models import User | from ..users.models import User | ||||
@@ -19,7 +20,13 @@ class ImageManager(models.Manager): | |||||
buf.write(response.content) | buf.write(response.content) | ||||
obj = InMemoryUploadedFile(buf, 'image', file_name, | obj = InMemoryUploadedFile(buf, 'image', file_name, | ||||
None, buf.tell(), None) | None, buf.tell(), None) | ||||
return Image.objects.create(image=obj) | |||||
# create the image and its thumbnails in one transaction, removing | |||||
# a chance of getting Database into a inconsistent state when we | |||||
# try to create thumbnails one by one later | |||||
image = self.create(image=obj) | |||||
for size in settings.IMAGE_SIZES.keys(): | |||||
Thumbnail.objects.get_or_create_at_size(image.pk, size) | |||||
return image | |||||
class Image(BaseImage): | class Image(BaseImage): | ||||
@@ -3,6 +3,7 @@ from django.contrib.auth.models import Permission | |||||
from django.core.files.images import ImageFile | from django.core.files.images import ImageFile | ||||
from django.db.models.query import QuerySet | from django.db.models.query import QuerySet | ||||
from django.test import TestCase | from django.test import TestCase | ||||
from django_images.models import Thumbnail | |||||
import factory | import factory | ||||
from taggit.models import Tag | from taggit.models import Tag | ||||
@@ -33,8 +34,15 @@ class TagFactory(factory.Factory): | |||||
class ImageFactory(factory.Factory): | class ImageFactory(factory.Factory): | ||||
FACTORY_FOR = Image | |||||
image = factory.LazyAttribute(lambda a: ImageFile(open(TEST_IMAGE_PATH, 'rb'))) | image = factory.LazyAttribute(lambda a: ImageFile(open(TEST_IMAGE_PATH, 'rb'))) | ||||
@factory.post_generation() | |||||
def create_thumbnails(self, create, extracted, **kwargs): | |||||
for size in settings.IMAGE_SIZES.keys(): | |||||
Thumbnail.objects.get_or_create_at_size(self.pk, size) | |||||
class PinFactory(factory.Factory): | class PinFactory(factory.Factory): | ||||
submitter = factory.SubFactory(UserFactory) | submitter = factory.SubFactory(UserFactory) | ||||
@@ -1,9 +1,11 @@ | |||||
from django.http import HttpResponseRedirect | from django.http import HttpResponseRedirect | ||||
from django.conf import settings | |||||
from django.core.urlresolvers import reverse | from django.core.urlresolvers import reverse | ||||
from django.views.generic import CreateView | from django.views.generic import CreateView | ||||
from django_images.models import Image | from django_images.models import Image | ||||
from braces.views import JSONResponseMixin, LoginRequiredMixin | from braces.views import JSONResponseMixin, LoginRequiredMixin | ||||
from django_images.models import Thumbnail | |||||
from .forms import ImageForm | from .forms import ImageForm | ||||
@@ -20,6 +22,8 @@ class CreateImage(JSONResponseMixin, LoginRequiredMixin, CreateView): | |||||
def form_valid(self, form): | def form_valid(self, form): | ||||
image = form.save() | image = form.save() | ||||
for size in settings.IMAGE_SIZES.keys(): | |||||
Thumbnail.objects.get_or_create_at_size(image.pk, size) | |||||
return self.render_json_response({ | return self.render_json_response({ | ||||
'success': { | 'success': { | ||||
'id': image.id | 'id': image.id | ||||
@@ -22,12 +22,22 @@ $(window).load(function() { | |||||
'margin-top': -$('body').data('scroll-level') | 'margin-top': -$('body').data('scroll-level') | ||||
}); | }); | ||||
$(window).scrollTop(0); | $(window).scrollTop(0); | ||||
/* disable the global pin-loading scroll handler so we don't | |||||
load pins when scrolling a selected image */ | |||||
$(window).off('scroll'); | |||||
} else { | } else { | ||||
$('#pins').css({ | $('#pins').css({ | ||||
'position': 'static', | 'position': 'static', | ||||
'margin-top': 0 | 'margin-top': 0 | ||||
}); | }); | ||||
$(window).scrollTop($('body').data('scroll-level')); | $(window).scrollTop($('body').data('scroll-level')); | ||||
/* enable the pin-loading scroll handler unless we've already | |||||
loaded all pins from the server (in which case an element | |||||
with id 'the-end' exists */ | |||||
var theEnd = document.getElementById('the-end'); | |||||
if (!theEnd) { | |||||
$(window).scroll(scrollHandler); | |||||
} | |||||
} | } | ||||
} | } | ||||
// End Helper Functions | // End Helper Functions | ||||
@@ -91,6 +91,14 @@ $(window).load(function() { | |||||
blockContainer.css('height', colHeights.sort().slice(-1)[0]); | blockContainer.css('height', colHeights.sort().slice(-1)[0]); | ||||
} | } | ||||
/** | |||||
* On scroll load more pins from the server | |||||
*/ | |||||
window.scrollHandler = function() { | |||||
var windowPosition = $(window).scrollTop() + $(window).height(); | |||||
var bottom = $(document).height() - 100; | |||||
if(windowPosition > bottom) loadPins(); | |||||
} | |||||
/** | /** | ||||
* Load our pins using the pins template into our UI, be sure to define a | * Load our pins using the pins template into our UI, be sure to define a | ||||
@@ -138,11 +146,7 @@ $(window).load(function() { | |||||
$('body').append(theEnd); | $('body').append(theEnd); | ||||
} | } | ||||
} else { | } else { | ||||
$(window).scroll(function() { | |||||
var windowPosition = $(window).scrollTop() + $(window).height(); | |||||
var bottom = $(document).height() - 100; | |||||
if(windowPosition > bottom) loadPins(); | |||||
}); | |||||
$(window).scroll(scrollHandler); | |||||
} | } | ||||
}); | }); | ||||
@@ -4,7 +4,7 @@ South | |||||
django-tastypie==0.9.12 | django-tastypie==0.9.12 | ||||
django-braces | django-braces | ||||
mock | mock | ||||
factory-boy | |||||
factory-boy>=1.3,<2.0 | |||||
django_compressor | django_compressor | ||||
requests | requests | ||||
http://github.com/kklimonda/django-images/tarball/master#egg=django-images | http://github.com/kklimonda/django-images/tarball/master#egg=django-images | ||||
@@ -27,7 +27,7 @@ install_requires = [ | |||||
setup( | setup( | ||||
name="pinry", | name="pinry", | ||||
version="1.0.0", | |||||
version="1.1.0", | |||||
author="Pinry contributors", | author="Pinry contributors", | ||||
author_email="devs@getpinry.com", | author_email="devs@getpinry.com", | ||||
description=("A tiling image board system for people who want to save, " | description=("A tiling image board system for people who want to save, " | ||||
@@ -38,7 +38,7 @@ setup( | |||||
packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), | packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), | ||||
long_description=read('README.md'), | long_description=read('README.md'), | ||||
dependency_links=dependency_links, | dependency_links=dependency_links, | ||||
tests_require=['mock', 'factory-boy'], | |||||
tests_require=['mock', 'factory-boy>=1.3,<2.0'], | |||||
install_requires=install_requires, | install_requires=install_requires, | ||||
classifiers=[ | classifiers=[ | ||||
"Development Status :: 4 - Beta", | "Development Status :: 4 - Beta", | ||||