Beispiel #1
0
class BaseImportData(models.Model):
    # dictionary object representing a row in csv
    row_data = DictField(_('Row Data'))
    # the original row number in the uploaded csv file
    row_num = models.IntegerField(_('Row #'))
    # action_taken can be 'insert', 'update' or 'mixed'
    action_taken = models.CharField(_('Action Taken'), max_length=20, null=True)
    error = models.CharField(_('Error'), max_length=500, default='')

    class Meta:
        abstract = True
Beispiel #2
0
class Image(OrderingBaseModel, ImageModel, TendenciBaseModel):
    """
    A photo with its details
    """
    SAFETY_LEVEL = (
        (1, _('Safe')),
        (2, _('Not Safe')),
    )
    EXIF_KEYS = ('DateTimeOriginal', 'DateTime', 'ApertureValue', 'GPSInfo',
                 'Make', 'Model', 'Software', 'ExifImageWidth',
                 'ExifImageHeight', 'XResolution', 'YResolution',
                 'ResolutionUnit', 'SubjectLocation', 'Orientation')

    guid = models.CharField(max_length=40, editable=False)
    title = models.CharField(_('title'), max_length=200)
    title_slug = models.SlugField(_('slug'))
    caption = models.TextField(_('caption'), blank=True)
    date_added = models.DateTimeField(_('date added'),
                                      auto_now_add=True,
                                      editable=False)
    is_public = models.BooleanField(
        _('public'),
        default=True,
        help_text=_(
            'Public photographs will be displayed in the default views.'))
    member = models.ForeignKey(User,
                               related_name="added_photos",
                               blank=True,
                               null=True,
                               on_delete=models.SET_NULL)
    safetylevel = models.IntegerField(_('safety level'),
                                      choices=SAFETY_LEVEL,
                                      default=3)
    photoset = models.ManyToManyField(PhotoSet,
                                      blank=True,
                                      verbose_name=_('photo set'))
    tags = TagField(blank=True,
                    help_text=_("Comma delimited (eg. mickey, donald, goofy)"))
    license = models.ForeignKey('License',
                                null=True,
                                blank=True,
                                on_delete=models.SET_NULL)
    group = models.ForeignKey(Group,
                              null=True,
                              default=None,
                              on_delete=models.SET_NULL,
                              blank=True)
    exif_data = DictField(_('exif'), null=True)
    photographer = models.CharField(_('Photographer'),
                                    blank=True,
                                    null=True,
                                    max_length=100)

    # html-meta tags
    meta = models.OneToOneField(MetaTags,
                                blank=True,
                                null=True,
                                on_delete=models.SET_NULL)

    perms = GenericRelation(ObjectPermission,
                            object_id_field="object_id",
                            content_type_field="content_type")

    def get_meta(self, name):
        """
        This method is standard across all models that are
        related to the Meta model.  Used to generate dynamic
        methods coupled to this instance.
        """
        return PhotoMeta().get_meta(self, name)

    class Meta:
        #         permissions = (("view_image", "Can view image"),)
        app_label = 'photos'

    def save(self, *args, **kwargs):
        initial_save = not self.id
        if not self.id:
            self.guid = str(uuid.uuid4())
        if not self.group:
            self.group_id = get_default_group()

        super(Image, self).save(*args, **kwargs)
        # clear the cache
        #caching.instance_cache_clear(self, self.pk)
        #caching.cache_clear(PHOTOS_KEYWORDS_CACHE, key=self.pk)

        # re-add instance to the cache
        #caching.instance_cache_add(self, self.pk)

        if not self.is_public_photo() or not self.is_public_photoset():
            if hasattr(
                    settings,
                    'USE_S3_STORAGE') and settings.USE_S3_STORAGE and hasattr(
                        self.image, 'file'):
                set_s3_file_permission(self.image.file, public=False)
            cache_set = cache.get("photos_cache_set.%s" % self.pk)
            if cache_set is not None:
                # TODO remove cached images
                cache.delete_many(cache.get("photos_cache_set.%s" % self.pk))
                cache.delete("photos_cache_set.%s" % self.pk)

        if initial_save:
            try:
                exif_exists = self.get_exif_data()
                if exif_exists:
                    self.save()
            except AttributeError:
                pass

    def delete(self, *args, **kwargs):
        """
        Delete image-file and all resized versions
        """

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

        if self.image:
            cache_path = self.cache_path()

            # delete cached [resized] versions
            try:
                filename_list = default_storage.listdir(cache_path)[1]
                for filename in filename_list:
                    try:
                        default_storage.delete(
                            os.path.join(cache_path, filename))
                    except OSError:
                        pass
            except OSError:
                pass

            # delete actual image; do not save() self.instance
            self.image.delete(save=False)

    def get_absolute_url(self):
        try:
            photo_set = self.photoset.all()[0]
        except IndexError:
            return reverse("photo", args=[self.pk])
        return reverse('photo', args=[self.pk, photo_set.pk])

    def get_exif_data(self):
        """
        Extract EXIF data from image and store in the field exif_data.
        """
        try:
            img = PILImage.open(default_storage.open(self.image.name))
            exif = img._getexif()
        except (AttributeError, IOError):
            return False

        if self.exif_data is None:
            self.exif_data = {}

        if exif:
            for tag, value in exif.items():
                key = PILTAGS.get(tag, tag)
                if key in self.EXIF_KEYS:
                    self.exif_data[key] = value

        self.exif_data['lat'], self.exif_data['lng'] = self.get_lat_lng(
            self.exif_data.get('GPSInfo'))
        self.exif_data['location'] = self.get_location_via_latlng(
            self.exif_data['lat'], self.exif_data['lng'])
        return True

    def get_lat_lng(self, gps_info):
        """
        Calculate the latitude and longitude from gps_info.
        """
        lat, lng = None, None
        if isinstance(gps_info, dict):
            try:
                lat = [float(x) / float(y) for x, y in gps_info[2]]
                latref = gps_info[1]
                lng = [float(x) / float(y) for x, y in gps_info[4]]
                lngref = gps_info[3]
            except (KeyError, ZeroDivisionError):
                return None, None

            lat = lat[0] + lat[1] / 60 + lat[2] / 3600
            lng = lng[0] + lng[1] / 60 + lng[2] / 3600
            if latref == 'S':
                lat = -lat
            if lngref == 'W':
                lng = -lng

        return lat, lng

    def get_location_via_latlng(self, lat, lng):
        """
        Get location via lat and lng.
        """
        if lat and lng:
            url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=%s,%s&sensor=false' % (
                lat, lng)
            r = requests.get(url)
            if r.status_code == 200:
                data = simplejson.loads(r.content)
                for result in data.get('results'):
                    types = result.get('types')
                    if types and types[0] == 'postal_code':
                        return result.get('formatted_address')
        return None

    def meta_keywords(self):
        return ''


#        from base.utils import generate_meta_keywords
#        keywords = caching.cache_get(PHOTOS_KEYWORDS_CACHE, key=self.pk)
#        if not keywords:
#            value = self.title + ' ' + self.caption + ' ' + self.tags
#            keywords = generate_meta_keywords(value)
#            caching.cache_add(PHOTOS_KEYWORDS_CACHE, keywords, key=self.pk)
#        return keywords

    def check_perm(self, user, permission, *args, **kwargs):
        """
            has_perms(self, user, permission, *args, **kwargs)
            returns boolean
        """
        if user == self.member or user.has_perm(permission):
            return True
        return False

    def get_next(self, set=None):
        # decide which set to pull from
        if set:
            images = Image.objects.filter(photoset=set,
                                          position__gt=self.position)
        else:
            images = Image.objects.filter(position__gt=self.position)
        images = images.values_list("position", flat=True)
        images = images.order_by('-position')
        if set and images:
            [image] = Image.objects.filter(photoset=set,
                                           position=min(images))[:1] or [None]
            return image
        return None

    def get_prev(self, set=None):
        # decide which set to pull from
        if set:
            images = Image.objects.filter(photoset=set,
                                          position__lt=self.position)
        else:
            images = Image.objects.filter(position__lt=self.position)
        images = images.values_list("position", flat=True)
        images = images.order_by('-position')
        if set:
            try:
                return Image.objects.get(photoset=set, position=max(images))
            except (ValueError, Image.MultipleObjectsReturned):
                return None
        return None

    def get_first(self, set=None):
        # decide which set to pull from
        if set:
            images = Image.objects.filter(photoset=set)
        else:
            return None
        images = images.values_list("position", flat=True)
        images = images.order_by('-position')
        if set:
            try:
                return Image.objects.get(photoset=set, position=min(images))
            except (ValueError, Image.MultipleObjectsReturned):
                return None
        return None

    def get_position(self, set=None):
        # decide which set to pull from
        if set:
            images = Image.objects.filter(photoset=set,
                                          position__lte=self.position)
        else:
            images = Image.objects.filter(position__lte=self.position)
        position = images.count()
        return position

    def is_public_photo(self):
        return all([
            self.is_public, self.allow_anonymous_view, self.status,
            self.status_detail.lower() == "active"
        ])

    def is_public_photoset(self):
        for photo_set in self.photoset.all():
            if not all([
                    self.allow_anonymous_view, self.status,
                    self.status_detail.lower() == "active"
            ]):
                return False
        return True

    def get_license(self):
        return self.license or self.default_license()

    def default_license(self):
        return License.objects.get(id=1)

    def file_exists(self):
        return default_storage.exists(str(self.image))

    def default_thumbnail(self):
        return static('images/default-photo-album-cover.jpg')

    def get_file_from_remote_storage(self):
        return BytesIO(default_storage.open(self.image.file.name).read())

    def image_dimensions(self):
        try:
            if hasattr(settings, 'USE_S3_STORAGE') and settings.USE_S3_STORAGE:
                with PILImage.open(self.get_file_from_remote_storage()) as im:
                    return im.size
            else:
                with PILImage.open(self.image.path) as im:
                    return im.size
        except Exception:
            return (0, 0)

    objects = PhotoManager()

    def __str__(self):
        return self.title