Skip to content

Commit

Permalink
Merge pull request #247 from OpenUpSA/feature/save-person-images-to-s3
Browse files Browse the repository at this point in the history
Feature/save person images to s3
  • Loading branch information
paulmwatson authored Dec 12, 2024
2 parents 1c339f5 + a1637ec commit b04dbdc
Show file tree
Hide file tree
Showing 23 changed files with 314 additions and 19 deletions.
1 change: 0 additions & 1 deletion docs/BACKGROUND.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ into packages that can be installed from PyPI. This is because:

Some examples of this are:

* https://github.com/mysociety/django-images
* https://github.com/mysociety/django-info-pages
* https://github.com/mysociety/django-slug-helpers

Expand Down
2 changes: 1 addition & 1 deletion pombola/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from ajax_select import make_ajax_form
from ajax_select.admin import AjaxSelectAdmin

from images.admin import ImageAdminInline
from pombola.core.images.admin import ImageAdminInline
from slug_helpers.admin import StricterSlugFieldMixin

from pombola.core import models
Expand Down
Empty file added pombola/core/images/__init__.py
Empty file.
34 changes: 34 additions & 0 deletions pombola/core/images/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from django.contrib import admin
try:
from django.contrib.contenttypes.admin import GenericTabularInline
except ImportError:
# This fallback import is the version that was deprecated in
# Django 1.7 and is removed in 1.9:
from django.contrib.contenttypes.generic import GenericTabularInline


from sorl.thumbnail import get_thumbnail
from sorl.thumbnail.admin import AdminImageMixin

from pombola.core.images import models


class ImageAdmin(AdminImageMixin, admin.ModelAdmin):
list_display = [ 'thumbnail', 'content_object', 'is_primary', 'source', ]
search_fields = ['person__legal_name', 'id', 'source']

def thumbnail(self, obj):
if obj.image:
im = get_thumbnail(obj.image, '100x100')
return '<img src="%s" />' % ( im.url )
else:
return "NO IMAGE FOUND"
thumbnail.allow_tags = True


class ImageAdminInline(AdminImageMixin, GenericTabularInline):
model = models.Image
extra = 0
can_delete = True

admin.site.register( models.Image, ImageAdmin )
26 changes: 26 additions & 0 deletions pombola/core/images/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import sorl.thumbnail.fields


class Migration(migrations.Migration):

dependencies = [
('contenttypes', '0002_remove_content_type_name'),
]

operations = [
migrations.CreateModel(
name='Image',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('object_id', models.PositiveIntegerField()),
('image', sorl.thumbnail.fields.ImageField(upload_to=b'images')),
('source', models.CharField(max_length=400)),
('is_primary', models.BooleanField(default=False)),
('content_type', models.ForeignKey(to='contenttypes.ContentType')),
],
),
]
20 changes: 20 additions & 0 deletions pombola/core/images/migrations/0002_auto_20160323_1424.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models
import sorl.thumbnail.fields


class Migration(migrations.Migration):

dependencies = [
('images', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='image',
name='image',
field=sorl.thumbnail.fields.ImageField(max_length=512, upload_to=b'images'),
),
]
Empty file.
71 changes: 71 additions & 0 deletions pombola/core/images/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from django.db import models
from django.contrib.contenttypes.models import ContentType
try:
from django.contrib.contenttypes.fields import GenericForeignKey
except ImportError:
# This fallback import is the version that was deprecated in
# Django 1.7 and is removed in 1.9:
from django.contrib.contenttypes.generic import GenericForeignKey

from sorl.thumbnail import ImageField


class Image(models.Model):

class Meta:
db_table = 'images_image'

# link to other objects using the ContentType system
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')

# store the actual image
image = ImageField(upload_to="images", max_length=512)

# added
source = models.CharField(max_length=400)
# user

is_primary = models.BooleanField( default=False )

def save(self, *args, **kwargs):
"""
Only one image should be marked as is_primary for an object.
"""

# other images for this object
siblings = Image.objects.filter(
content_type = self.content_type,
object_id = self.object_id,
)

# check that we are not first entry for content_object
if not siblings.count():
self.is_primary = True

super(Image, self).save(*args, **kwargs)

# If we are true then make sure all others are false
if self.is_primary is True:

primary_siblings = siblings.exclude( is_primary = False ).exclude( id = self.id )

for sibling in primary_siblings:
sibling.is_primary = False
sibling.save()


class HasImageMixin(object):

def primary_image(self):
primary_image_model = self.primary_image_model()
if primary_image_model:
return primary_image_model.image
return None

def primary_image_model(self):
primary_images = [i for i in self.images.all() if i.is_primary]
if primary_images:
return primary_images[0]
return None
104 changes: 104 additions & 0 deletions pombola/core/images/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import os

from django.test import TestCase
from django.core.files import File
from django.contrib.sites.models import Site
from django.contrib.contenttypes.models import ContentType

from .models import Image

from sorl.thumbnail import get_thumbnail

class ImageTest(TestCase):

def get_example_file_content(self, filename):
"""
Open the given file and return it
"""
full_path = os.path.join(
os.path.abspath( os.path.dirname(__file__) ),
'tests',
filename,
)

return File(open(full_path, 'rb'))

def reload_image(self, image):
"""because there is no reload in Django models (silly really)"""
return Image.objects.get(id=image.id)

def test_uploading(self):
"""
Test that uploading an image works
"""

test_site = Site.objects.all()[0]
test_site_ct = ContentType.objects.get_for_model(test_site)
test_site_id = test_site.id

# check that there are no images
self.assertEqual( Image.objects.all().count(), 0 )

# create an image
first = Image(
content_type = test_site_ct,
object_id = test_site_id,
source = 'test directory',
)
first.image.save(
name = 'foo.png',
content = self.get_example_file_content('foo.png'),
)

# check that the is_primary is true
self.assertTrue( first.is_primary )

# create another image
second = Image(
content_type = test_site_ct,
object_id = test_site_id,
source = 'test directory',
)
second.image.save(
name = 'bar.png',
content = self.get_example_file_content('bar.png'),
)

# check that is_primary is false
self.assertTrue( self.reload_image(first).is_primary )
self.assertFalse( self.reload_image(second).is_primary )

# change is_primary on second image
second.is_primary = True
second.save()

# check that it changed on first too
self.assertFalse( self.reload_image(first).is_primary )
self.assertTrue( self.reload_image(second).is_primary )

# create a third image with is_primary true at the start
third = Image(
content_type = test_site_ct,
object_id = test_site_id,
source = 'test directory',
is_primary = True,
)
third.image.save(
name = 'baz.png',
content = self.get_example_file_content('baz.png'),
)

# check that is_primary is updated for all
self.assertFalse( self.reload_image(first).is_primary )
self.assertFalse( self.reload_image(second).is_primary )
self.assertTrue( self.reload_image(third).is_primary )

# Now try to create an thumbnail with sorl. If this fails
# with "IOError: decoder zip not available", then probably
# this is a problem with an old version of PIL, or one that
# wasn't installed when the right build dependencies were
# present. The simplest solution in most solutions is:
# pip uninstall PIL
# pip install pillow

get_thumbnail(third.image, '100x100', crop='center', quality=99)
Binary file added pombola/core/images/tests/bar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pombola/core/images/tests/baz.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pombola/core/images/tests/foo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions pombola/core/images/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This app doesn't provide any URLs
1 change: 1 addition & 0 deletions pombola/core/images/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Create your views here.
2 changes: 1 addition & 1 deletion pombola/core/kenya_import_scripts/import_member_photos.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from django.core.files.base import ContentFile
from pombola.core import models
from pombola.images.models import Image
from pombola.core.images.models import Image


constituency_kind = models.PlaceKind.objects.get(slug="constituency")
Expand Down
2 changes: 1 addition & 1 deletion pombola/core/management/merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from django.db import transaction

from slug_helpers.models import SlugRedirect
from images.models import Image
from pombola.core.images.models import Image

import pombola.core.models as core_models

Expand Down
6 changes: 3 additions & 3 deletions pombola/core/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import markitup.fields
import django.contrib.gis.db.models.fields

import images.models
import pombola.core.images.models


class Migration(migrations.Migration):
Expand Down Expand Up @@ -118,7 +118,7 @@ class Migration(migrations.Migration):
options={
'ordering': ['slug'],
},
bases=(models.Model, images.models.HasImageMixin, pombola.core.models.IdentifierMixin),
bases=(models.Model, pombola.core.images.models.HasImageMixin, pombola.core.models.IdentifierMixin),
),
migrations.CreateModel(
name='OrganisationKind',
Expand Down Expand Up @@ -209,7 +209,7 @@ class Migration(migrations.Migration):
options={
'ordering': ['sort_name'],
},
bases=(images.models.HasImageMixin, models.Model, pombola.core.models.IdentifierMixin),
bases=(pombola.core.images.models.HasImageMixin, models.Model, pombola.core.models.IdentifierMixin),
),
migrations.CreateModel(
name='Place',
Expand Down
27 changes: 27 additions & 0 deletions pombola/core/migrations/0017_auto_20241211_1411.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2024-12-11 14:11
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion
import sorl.thumbnail.fields


class Migration(migrations.Migration):

dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('core', '0016_auto_20201119_0713'),
]

operations = [
migrations.AlterModelOptions(
name='organisationhistory',
options={'ordering': ['date_changed'], 'verbose_name_plural': 'organisation histories'},
),
migrations.AlterField(
model_name='organisationhistory',
name='new_organisation',
field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='org_history_new', to='core.Organisation'),
)
]
4 changes: 2 additions & 2 deletions pombola/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from django_date_extensions.fields import ApproximateDateField, ApproximateDate

from slug_helpers.models import validate_slug_not_redirecting
from images.models import HasImageMixin, Image
from pombola.core.images.models import HasImageMixin, Image

from pombola.tasks.models import Task

Expand Down Expand Up @@ -1973,7 +1973,7 @@ def raw_query_with_prefetch(query_model, query, params, fields_prefetches):

class OrganisationHistory(ModelBase):
old_organisation = models.ForeignKey('Organisation', null=False, related_name='org_history_old')
new_organisation = models.ForeignKey('Organisation', null=False, related_name='org_history_new')
new_organisation = models.ForeignKey('Organisation', null=False, related_name='org_history_new', default=0)
date_changed = DateField(null=False)

class Meta:
Expand Down
2 changes: 1 addition & 1 deletion pombola/core/tests/test_popolo.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from mapit.models import Generation, Area, Type

from images.models import Image
from pombola.core.images.models import Image

from pombola.core import models
from pombola.core.popolo import get_popolo_data
Expand Down
Loading

0 comments on commit b04dbdc

Please sign in to comment.