Ejemplo n.º 1
0
    def test_select_on_queryset_with_reverse_foreign_key(self):
        fields = index.RelatedFields(
            'categories',
            [index.RelatedFields('category', [index.SearchField('name')])])

        queryset = fields.select_on_queryset(ManyToManyBlogPage.objects.all())

        # reverse ForeignKey should be prefetch_related
        self.assertIn('categories', queryset._prefetch_related_lookups)
        self.assertFalse(queryset.query.select_related)
Ejemplo n.º 2
0
class Novel(Book):
    setting = models.CharField(max_length=255)
    protagonist = models.OneToOneField(Character, related_name='+', null=True)

    search_fields = Book.search_fields + [
        index.SearchField('setting', partial_match=True),
        index.RelatedFields('characters', [
            index.SearchField('name', boost=0.25),
        ]),
        index.RelatedFields('protagonist', [
            index.SearchField('name', boost=0.5),
        ]),
    ]
Ejemplo n.º 3
0
class SearchTest(index.Indexed, models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    live = models.BooleanField(default=False)
    published_date = models.DateField(null=True)
    tags = TaggableManager()

    search_fields = [
        index.SearchField('title', partial_match=True),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True),
            index.FilterField('slug'),
        ]),
        index.RelatedFields('subobjects', [
            index.SearchField('name', partial_match=True),
        ]),
        index.SearchField('content', boost=2),
        index.SearchField('callable_indexed_field'),
        index.FilterField('title'),
        index.FilterField('live'),
        index.FilterField('published_date'),
    ]

    def callable_indexed_field(self):
        return "Callable"

    @classmethod
    def get_indexed_objects(cls):
        indexed_objects = super(SearchTest, cls).get_indexed_objects()

        # Exclude SearchTests that have a SearchTestChild to stop update_index creating duplicates
        if cls is SearchTest:
            indexed_objects = indexed_objects.exclude(
                id__in=SearchTestChild.objects.all().values_list(
                    'searchtest_ptr_id', flat=True))

        # Exclude SearchTests that have the title "Don't index me!"
        indexed_objects = indexed_objects.exclude(title="Don't index me!")

        return indexed_objects

    def get_indexed_instance(self):
        # Check if there is a SearchTestChild that descends from this
        child = SearchTestChild.objects.filter(
            searchtest_ptr_id=self.id).first()

        # Return the child if there is one, otherwise return self
        return child or self

    def __str__(self):
        return self.title
Ejemplo n.º 4
0
class Book(index.Indexed, models.Model):
    title = models.CharField(max_length=255)
    authors = models.ManyToManyField(Author, related_name='books')
    publication_date = models.DateField()
    number_of_pages = models.IntegerField()
    tags = TaggableManager()

    search_fields = [
        index.SearchField('title', partial_match=True, boost=2.0),
        index.FilterField('title'),
        index.RelatedFields('authors', Author.search_fields),
        index.FilterField('publication_date'),
        index.FilterField('number_of_pages'),
        index.RelatedFields('tags', [
            index.SearchField('name'),
            index.FilterField('slug'),
        ]),
    ]

    @classmethod
    def get_indexed_objects(cls):
        indexed_objects = super(Book, cls).get_indexed_objects()

        # Don't index books using Book class that they have a more specific type
        if cls is Book:
            indexed_objects = indexed_objects.exclude(
                id__in=Novel.objects.values_list('book_ptr_id', flat=True))

            indexed_objects = indexed_objects.exclude(
                id__in=ProgrammingGuide.objects.values_list('book_ptr_id',
                                                            flat=True))

        # Exclude Books that have the title "Don't index me!"
        indexed_objects = indexed_objects.exclude(title="Don't index me!")

        return indexed_objects

    def get_indexed_instance(self):
        # Check if this object is a Novel or ProgrammingGuide and return the specific object
        novel = Novel.objects.filter(book_ptr_id=self.id).first()
        programming_guide = ProgrammingGuide.objects.filter(
            book_ptr_id=self.id).first()

        # Return the novel/programming guide object if there is one, otherwise return self
        return novel or programming_guide or self

    def __str__(self):
        return self.title
Ejemplo n.º 5
0
class AbstractEmbedVideo(CollectionMember, index.Indexed, models.Model):
    title = models.CharField(max_length=255, verbose_name=_('Title'))
    url = EmbedVideoField()
    thumbnail = models.ForeignKey(image_model_name,
                                  verbose_name=_('Thumbnail'),
                                  null=True,
                                  blank=True,
                                  on_delete=models.SET_NULL,
                                  related_name='+')
    created_at = models.DateTimeField(auto_now_add=True,
                                      verbose_name=_('Created'))
    uploaded_by_user = models.ForeignKey(settings.AUTH_USER_MODEL,
                                         null=True,
                                         blank=True,
                                         editable=False,
                                         verbose_name=_('Uploader'))

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('Tags'))

    def get_usage(self):
        return get_object_usage(self)

    @property
    def usage_url(self):
        return reverse('wagtail_embed_videos:video_usage', args=(self.id, ))

    search_fields = CollectionMember.search_fields + [
        index.SearchField('title', partial_match=True, boost=10),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
        index.FilterField('uploaded_by_user'),
    ]

    def __str__(self):
        return self.title

    def __init__(self, *args, **kwargs):

        super(AbstractEmbedVideo, self).__init__(*args, **kwargs)
        if args:
            if args[3] is None:
                create_thumbnail(self)

    def save(self, *args, **kwargs):
        super(AbstractEmbedVideo, self).save(*args, **kwargs)
        if not self.thumbnail:
            create_thumbnail(self)

    @property
    def default_alt_text(self):
        return self.title

    def is_editable_by_user(self, user):
        from .permissions import permission_policy
        return permission_policy.user_has_permission_for_instance(
            user, 'change', self)

    class Meta:
        abstract = True
Ejemplo n.º 6
0
class RteiDocument(AbstractDocument, index.Indexed):
    '''A custom Document adding fields needed by RTEI Resource items.'''

    year = models.CharField(validators=[
        RegexValidator(regex='^\d{4}$',
                       message='Must be 4 numbers',
                       code='nomatch')
    ],
                            help_text='e.g. 1999',
                            max_length=4,
                            blank=True)

    country = models.CharField(max_length=256, blank=True)
    is_resource = models.BooleanField(default=True,
                                      help_text="Determines whether document "
                                      "appears on the Resources page.")
    description = RichTextField(blank=True)

    admin_form_fields = ('title', 'description', 'file', 'collection',
                         'country', 'year', 'is_resource', 'tags')

    search_fields = AbstractDocument.search_fields + [
        index.SearchField('title'),
        index.SearchField('description'),
        index.SearchField('country'),
        index.SearchField('year'),
        index.RelatedFields('tags', [
            index.SearchField('name'),
        ])
    ]

    class Meta:
        get_latest_by = "created_at"
Ejemplo n.º 7
0
class TagSearchable(index.Indexed):
    """
    Mixin to provide a 'search' method, searching on the 'title' field and tags,
    for models that provide those things.
    """

    search_fields = (
        index.SearchField('title', partial_match=True, boost=10),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
    )

    @classmethod
    def get_indexed_objects(cls):
        return super(
            TagSearchable,
            cls).get_indexed_objects().prefetch_related('tagged_items__tag')

    @classmethod
    def popular_tags(cls):
        content_type = ContentType.objects.get_for_model(cls)
        return Tag.objects.filter(
            taggit_taggeditem_items__content_type=content_type).annotate(
                item_count=Count('taggit_taggeditem_items')).order_by(
                    '-item_count')[:10]
Ejemplo n.º 8
0
    def test_select_on_queryset_with_foreign_key(self):
        fields = index.RelatedFields('page', [
            index.SearchField('title'),
        ])

        queryset = fields.select_on_queryset(SearchTestChild.objects.all())

        # ForeignKey should be select_related
        self.assertFalse(queryset._prefetch_related_lookups)
        self.assertIn('page', queryset.query.select_related)
Ejemplo n.º 9
0
    def test_select_on_queryset_with_one_to_one(self):
        fields = index.RelatedFields('searchtest_ptr', [
            index.SearchField('title'),
        ])

        queryset = fields.select_on_queryset(SearchTestChild.objects.all())

        # OneToOneField should be select_related
        self.assertFalse(queryset._prefetch_related_lookups)
        self.assertIn('searchtest_ptr', queryset.query.select_related)
Ejemplo n.º 10
0
    def test_select_on_queryset_with_foreign_key(self):
        fields = index.RelatedFields('protagonist', [
            index.SearchField('name'),
        ])

        queryset = fields.select_on_queryset(Novel.objects.all())

        # ForeignKey should be select_related
        self.assertFalse(queryset._prefetch_related_lookups)
        self.assertIn('protagonist', queryset.query.select_related)
Ejemplo n.º 11
0
    def test_select_on_queryset_with_many_to_many(self):
        fields = index.RelatedFields('adverts', [
            index.SearchField('title'),
        ])

        queryset = fields.select_on_queryset(ManyToManyBlogPage.objects.all())

        # ManyToManyField should be prefetch_related
        self.assertIn('adverts', queryset._prefetch_related_lookups)
        self.assertFalse(queryset.query.select_related)
Ejemplo n.º 12
0
    def test_select_on_queryset_with_reverse_one_to_one(self):
        fields = index.RelatedFields('novel', [
            index.SearchField('subtitle'),
        ])

        queryset = fields.select_on_queryset(Book.objects.all())

        # reverse OneToOneField should be select_related
        self.assertFalse(queryset._prefetch_related_lookups)
        self.assertIn('novel', queryset.query.select_related)
Ejemplo n.º 13
0
    def test_select_on_queryset_with_reverse_many_to_many(self):
        fields = index.RelatedFields('manytomanyblogpage', [
            index.SearchField('title'),
        ])

        queryset = fields.select_on_queryset(Advert.objects.all())

        # reverse ManyToManyField should be prefetch_related
        self.assertIn('manytomanyblogpage', queryset._prefetch_related_lookups)
        self.assertFalse(queryset.query.select_related)
Ejemplo n.º 14
0
    def test_select_on_queryset_with_taggable_manager(self):
        fields = index.RelatedFields('tags', [
            index.SearchField('name'),
        ])

        queryset = fields.select_on_queryset(Novel.objects.all())

        # Tags should be prefetch_related
        self.assertIn('tags', queryset._prefetch_related_lookups)
        self.assertFalse(queryset.query.select_related)
Ejemplo n.º 15
0
class AbstractDocument(CollectionMember, index.Indexed, models.Model):
    title = models.CharField(max_length=255, verbose_name=_('title'))
    file = models.FileField(upload_to='documents', verbose_name=_('file'))
    created_at = models.DateTimeField(verbose_name=_('created at'), auto_now_add=True)
    uploaded_by_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        verbose_name=_('uploaded by user'),
        null=True,
        blank=True,
        editable=False,
        on_delete=models.SET_NULL
    )

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('tags'))

    objects = DocumentQuerySet.as_manager()

    search_fields = CollectionMember.search_fields + [
        index.SearchField('title', partial_match=True, boost=10),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
        index.FilterField('uploaded_by_user'),
    ]

    def __str__(self):
        return self.title

    @property
    def filename(self):
        return os.path.basename(self.file.name)

    @property
    def file_extension(self):
        return os.path.splitext(self.filename)[1][1:]

    @property
    def url(self):
        return reverse('wagtaildocs_serve', args=[self.id, self.filename])

    def get_usage(self):
        return get_object_usage(self)

    @property
    def usage_url(self):
        return reverse('wagtaildocs:document_usage',
                       args=(self.id,))

    def is_editable_by_user(self, user):
        from wagtail.wagtaildocs.permissions import permission_policy
        return permission_policy.user_has_permission_for_instance(user, 'change', self)

    class Meta:
        abstract = True
        verbose_name = _('document')
Ejemplo n.º 16
0
class TagSearchable(index.Indexed):
    """
    Mixin to provide a 'search' method, searching on the 'title' field and tags,
    for models that provide those things.
    """

    search_fields = (
        index.SearchField('title', partial_match=True, boost=10),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
    )

    @classmethod
    def get_indexed_objects(cls):
        return super(TagSearchable, cls).get_indexed_objects().prefetch_related('tagged_items__tag')

    @classmethod
    def search(cls, q, results_per_page=None, page=1, prefetch_tags=False, filters={}):
        warnings.warn(
            "The {class_name}.search() method is deprecated. "
            "Please use the {class_name}.objects.search() method instead.".format(class_name=cls.__name__),
            RemovedInWagtail14Warning, stacklevel=2)

        results = cls.objects.all()

        if prefetch_tags:
            results = results.prefetch_related('tagged_items__tag')

        if filters:
            results = results.filter(**filters)

        results = results.search(q)

        # If results_per_page is set, return a paginator
        if results_per_page is not None:
            paginator = Paginator(results, results_per_page)
            try:
                return paginator.page(page)
            except PageNotAnInteger:
                return paginator.page(1)
            except EmptyPage:
                return paginator.page(paginator.num_pages)
        else:
            return results

    @classmethod
    def popular_tags(cls):
        content_type = ContentType.objects.get_for_model(cls)
        return Tag.objects.filter(
            taggit_taggeditem_items__content_type=content_type
        ).annotate(
            item_count=Count('taggit_taggeditem_items')
        ).order_by('-item_count')[:10]
Ejemplo n.º 17
0
class ArtworkAttributionLink(index.Indexed, models.Model):
    artist = ParentalKey('artists.ArtistProfilePage', related_name='artistartworklink')
    artwork = ParentalKey(ArtworkPage, related_name='artworkartistlink')

    panels = [
        PageChooserPanel('artist', 'artists.ArtistProfilePage'),
        PageChooserPanel('artwork', 'artworks.ArtworkPage'),
    ]

    search_fields = [
        index.RelatedFields('artist', [
            index.SearchField('title', partial_match=True),
            index.SearchField('first_name', partial_match=True),
            index.SearchField('last_name', partial_match=True)
        ]),
        index.RelatedFields('artwork', [
            index.SearchField('title', partial_match=True)
        ]),
    ]

    api_fields = ['artist', 'artwork']
Ejemplo n.º 18
0
class AbstractGiphy(CollectionMember, index.Indexed, models.Model):
    title = models.CharField(max_length=255, verbose_name=_('title'))
    file = models.FileField(
        verbose_name=_('file'), upload_to=get_upload_to,
    )
    created_at = models.DateTimeField(verbose_name=_('created at'), auto_now_add=True, db_index=True)
    uploaded_by_user = models.ForeignKey(
        settings.AUTH_USER_MODEL, verbose_name=_('uploaded by user'),
        null=True, blank=True, editable=False, on_delete=models.SET_NULL
    )

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('tags'))
    file_size = models.PositiveIntegerField(null=True, editable=False)

    def get_file_size(self):
        if self.file_size is None:
            try:
                self.file_size = self.file.size
            except OSError:
                # File doesn't exist
                return

            self.save(update_fields=['file_size'])

        return self.file_size

    def get_upload_to(self, filename):
        folder_name = 'giphy'
        filename = self.file.field.storage.get_valid_name(filename)

        created_at = self.file.created_at
        return os.path.join(folder_name, '{y}/{m}/{d}/{filename}'.format(**{
            'y': created_at.year,
            'm': created_at.month,
            'd': created_at.day,
            'filename': filename
        }))

    def __str__(self):
        return self.title

    search_fields = CollectionMember.search_fields + [
        index.SearchField('title', partial_match=True, boost=10),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
        index.FilterField('uploaded_by_user'),
    ]

    class Meta:
        abstract = True
Ejemplo n.º 19
0
class SearchTestChild(SearchTest):
    subtitle = models.CharField(max_length=191, null=True, blank=True)
    extra_content = models.TextField()
    page = models.ForeignKey('wagtailcore.Page', null=True, blank=True)

    search_fields = SearchTest.search_fields + [
        index.SearchField('subtitle', partial_match=True),
        index.SearchField('extra_content'),
        index.RelatedFields('page', [
            index.SearchField('title', partial_match=True),
            index.SearchField('search_description'),
            index.FilterField('live'),
        ]),
    ]
Ejemplo n.º 20
0
class RteiDocument(AbstractDocument, index.Indexed):
    '''A custom Document adding fields needed by RTEI Resource items.'''

    year = models.CharField(validators=[
        RegexValidator(regex='^\d{4}$',
                       message=_('Must be 4 numbers'),
                       code='nomatch')
    ],
                            help_text='e.g. 1999',
                            max_length=4,
                            blank=True)

    file = models.FileField(
        upload_to='documents',
        verbose_name=_('file'),
        blank=True,
        help_text="Use this to upload a file and list it as a resource")
    external_url = models.CharField(
        validators=[URLValidator(message=_('Must be a valid URL'))],
        blank=True,
        max_length=1000,
        verbose_name=_('External Link'),
        help_text="Use this to add an external website as a listed resource")
    country = models.CharField(max_length=256, blank=True)
    is_resource = models.BooleanField(default=True,
                                      help_text="Determines whether document "
                                      "appears on the Resources page.")
    description = RichTextField(blank=True)

    admin_form_fields = ('title', 'description', 'file', 'external_url',
                         'collection', 'country', 'year', 'is_resource',
                         'tags')

    search_fields = AbstractDocument.search_fields + [
        index.SearchField('title'),
        index.SearchField('description'),
        index.SearchField('country'),
        index.SearchField('year'),
        index.RelatedFields('tags', [
            index.SearchField('name'),
        ])
    ]

    class Meta:
        get_latest_by = "created_at"
Ejemplo n.º 21
0
class BlogPost(Page, WithStreamField, WithFeedImage):
    date = models.DateField()
    tags = ClusterTaggableManager(through=BlogPostTag, blank=True)

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.SearchField('date'),
        index.RelatedFields('tags', [
            index.SearchField('name'),
            index.SearchField('slug'),
        ]),
    ]

    subpage_types = []

    def get_index_page(self):
        # Find closest ancestor which is a blog index
        return BlogIndexPage.objects.ancestor_of(self).last()
Ejemplo n.º 22
0
class NewsPost(Page, WithStreamField, WithFeedImage):

    image = models.ForeignKey('wagtailimages.Image',
                              on_delete=models.PROTECT,
                              null=True)

    date = models.DateField()
    tags = ClusterTaggableManager(through=NewsPostTag, blank=True)

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.SearchField('date'),
        index.RelatedFields('tags', [
            index.SearchField('name'),
            index.SearchField('slug'),
        ]),
    ]

    subpage_types = []

    def get_index_page(self):
        # Find closest ancestor which is a news index
        return NewsIndexPage.objects.ancestor_of(self).last()
Ejemplo n.º 23
0
class BlogPage(Page):

    intro = models.TextField(blank=True)
    body = RichTextField(blank=True)
    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
    date = models.DateField("Post date")
    feed_image = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+')

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.RelatedFields('tags', [
            index.SearchField('name'),
        ])
    ]

    @property
    def blog_index(self):
        # Find closest ancestor which is a blog index
        return self.get_ancestors().type(BlogIndexPage).last()
Ejemplo n.º 24
0
class Product(Page):
    parent_page_types = ['longclawproducts.ProductIndex']
    description = RichTextField()
    tags = ClusterTaggableManager(through=ProductTag, blank=True)

    search_fields = Page.search_fields + [
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
    ]

    content_panels = Page.content_panels + [
        FieldPanel('description'),
        InlinePanel('variants', label='Product variants'),
        InlinePanel('images', label='Product images'),
        FieldPanel('tags'),
    ]

    @property
    def first_image(self):
        return self.images.first()

    @property
    def price_range(self):
        ''' Calculate the price range of the products variants
        '''
        ordered = self.variants.order_by('price')
        if ordered:
            return ordered.first().price, ordered.last().price
        else:
            return None, None

    @property
    def in_stock(self):
        ''' Returns True if any of the product variants are in stock
        '''
        return any(self.variants.filter(stock__gt=0))
Ejemplo n.º 25
0
class Event(Page, WithStreamField, WithFeedImage):

    image = models.ForeignKey(
        'wagtailimages.Image',
        on_delete=models.PROTECT,
    )
    date_from = models.DateField(verbose_name="Start Date")
    date_to = models.DateField(verbose_name="End Date (Leave blank if\
                               not required)",
                               blank=True,
                               null=True)
    time = models.TimeField(verbose_name="Time of Event")
    time_end = models.TimeField(verbose_name="End Time (leave blank if\
                                not required)",
                                blank=True,
                                null=True)

    location = models.TextField(verbose_name="Location")

    tags = ClusterTaggableManager(through=BlogPostTag, blank=True)

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.SearchField('date_from'),
        index.SearchField('date_to'),
        index.RelatedFields('tags', [
            index.SearchField('name'),
            index.SearchField('slug'),
        ]),
    ]

    subpage_types = []

    def get_index_page(self):
        # Find closest ancestor which is a blog index
        return EventIndexPage.objects.ancestor_of(self).last()
Ejemplo n.º 26
0
class WorkPage(Page, WithStreamField, WithFeedImage):
    subtitle = models.CharField(max_length=256)
    category = models.ForeignKey(
        WorkCategory,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
    )
    tags = ClusterTaggableManager(through=WorkPageTag, blank=True)

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.SearchField('subtitle'),
        index.SearchField('category'),
        index.RelatedFields('tags', [
            index.SearchField('name'),
        ]),
    ]

    subpage_types = []

    def get_index_page(self):
        # Find closest ancestor which is a blog index
        return WorkIndexPage.objects.ancestor_of(self).last()
Ejemplo n.º 27
0
class Face(Page):
    image = models.ForeignKey("core.AffixImage",
                              related_name="face_for_image",
                              null=True,
                              on_delete=models.SET_NULL)
    location = models.ForeignKey("location.Location",
                                 null=True,
                                 on_delete=models.SET_NULL,
                                 verbose_name="Place of Origin")
    additional_info = RichTextField(blank=True)
    language = models.CharField(max_length=7, choices=settings.LANGUAGES)

    occupation = models.CharField(
        max_length=100,
        null=True,
        blank=True,
        help_text="Enter the occupation of the person")
    occupation_of_parent = models.CharField(max_length=100,
                                            null=True,
                                            blank=True)
    adivasi = models.CharField(max_length=100, null=True, blank=True)
    quote = RichTextField(blank=True)
    child = models.BooleanField(default=False)
    age = models.IntegerField(null=True, blank=True)

    GENDER_CHOICES = (
        ('F', 'Female'),
        ('M', 'Male'),
        ('T', 'Transgender'),
    )
    gender = models.CharField(max_length=1,
                              choices=GENDER_CHOICES,
                              null=True,
                              blank=True)

    def __str__(self):
        return "{0} {1}".format(self.title, self.location.district)

    @property
    def featured_image(self):
        return self.image

    @property
    def title_to_share(self):
        title = "Meet " + self.title
        title += ", " + self.occupation if self.occupation else ""
        title += " from " + self.location.district
        title += ", " + self.location.state
        return title

    @property
    def locations(self):
        return [self.location]

    @property
    def photographers(self):
        return self.image.photographers.all()

    def get_authors_or_photographers(self):
        return [photographer.name for photographer in self.photographers]

    search_fields = Page.search_fields + [
        index.SearchField('title', partial_match=True,
                          boost=SearchBoost.TITLE),
        index.FilterField('image'),
        index.SearchField(
            'additional_info', partial_match=True, boost=SearchBoost.CONTENT),
        index.FilterField('location'),
        index.RelatedFields('location', [
            index.SearchField('name'),
            index.SearchField('block'),
            index.SearchField('district'),
            index.SearchField('state'),
            index.SearchField('panchayat'),
        ]),
        index.SearchField('occupation'),
        index.SearchField('occupation_of_parent'),
        index.SearchField('quote'),
        index.SearchField('get_locations_index',
                          partial_match=True,
                          boost=SearchBoost.LOCATION),
        index.SearchField('get_photographers_index',
                          partial_match=True,
                          boost=SearchBoost.AUTHOR),
        index.SearchField('adivasi'),
        index.SearchField('language'),
        index.FilterField('get_search_type'),
        index.FilterField('language'),
        index.FilterField('get_minimal_locations'),
        index.FilterField('get_authors_or_photographers')
    ]

    def get_locations_index(self):
        return self.image.get_locations_index()

    def get_minimal_locations(self):
        return [self.location.minimal_address]

    def get_photographers_index(self):
        return self.image.get_all_photographers()

    def get_search_type(self):
        return self.__class__.__name__.lower()

    content_panels = Page.content_panels + [
        ImageChooserPanel('image'),
        M2MFieldPanel('location'),
        FieldPanel('adivasi'),
        MultiFieldPanel([
            FieldPanel('child'),
            FieldPanel('occupation'),
            FieldPanel('occupation_of_parent'),
            FieldPanel('age'),
            FieldPanel('gender'),
        ],
                        heading="Personal details",
                        classname="collapsible "),
        MultiFieldPanel([
            FieldPanel('additional_info'),
            FieldPanel('language'),
            FieldPanel('quote'),
        ],
                        heading="Additional details",
                        classname="collapsible"),
    ]

    def get_absolute_url(self):
        name = "face-detail-single"
        return reverse(name,
                       kwargs={
                           "alphabet": self.location.district[0].lower(),
                           "slug": self.slug
                       })

    def get_context(self, request, *args, **kwargs):
        return {
            'faces': [self],
            'alphabet': self.location.district[0].lower(),
            'request': request
        }
Ejemplo n.º 28
0
class AbstractImage(CollectionMember, index.Indexed, models.Model):
    title = models.CharField(max_length=255, verbose_name=_('title'))
    file = models.ImageField(verbose_name=_('file'),
                             upload_to=get_upload_to,
                             width_field='width',
                             height_field='height')
    width = models.IntegerField(verbose_name=_('width'), editable=False)
    height = models.IntegerField(verbose_name=_('height'), editable=False)
    created_at = models.DateTimeField(verbose_name=_('created at'),
                                      auto_now_add=True,
                                      db_index=True)
    uploaded_by_user = models.ForeignKey(settings.AUTH_USER_MODEL,
                                         verbose_name=_('uploaded by user'),
                                         null=True,
                                         blank=True,
                                         editable=False,
                                         on_delete=models.SET_NULL)

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('tags'))

    focal_point_x = models.PositiveIntegerField(null=True, blank=True)
    focal_point_y = models.PositiveIntegerField(null=True, blank=True)
    focal_point_width = models.PositiveIntegerField(null=True, blank=True)
    focal_point_height = models.PositiveIntegerField(null=True, blank=True)

    file_size = models.PositiveIntegerField(null=True, editable=False)

    objects = ImageQuerySet.as_manager()

    def is_stored_locally(self):
        """
        Returns True if the image is hosted on the local filesystem
        """
        try:
            self.file.path

            return True
        except NotImplementedError:
            return False

    def get_file_size(self):
        if self.file_size is None:
            try:
                self.file_size = self.file.size
            except AttributeError:
                # self.file is NoneType
                return
            except OSError:
                # File doesn't exist
                return

            self.save(update_fields=['file_size'])

        return self.file_size

    def get_upload_to(self, filename):
        folder_name = 'original_images'
        filename = self.file.field.storage.get_valid_name(filename)

        # do a unidecode in the filename and then
        # replace non-ascii characters in filename with _ , to sidestep issues with filesystem encoding
        filename = "".join(
            (i if ord(i) < 128 else '_') for i in unidecode(filename))

        # Truncate filename so it fits in the 100 character limit
        # https://code.djangoproject.com/ticket/9893
        full_path = os.path.join(folder_name, filename)
        if len(full_path) >= 95:
            chars_to_trim = len(full_path) - 94
            prefix, extension = os.path.splitext(filename)
            filename = prefix[:-chars_to_trim] + extension
            full_path = os.path.join(folder_name, filename)

        return full_path

    def get_usage(self):
        return get_object_usage(self)

    @property
    def usage_url(self):
        return reverse('wagtailimages:image_usage', args=(self.id, ))

    search_fields = CollectionMember.search_fields + [
        index.SearchField('title', partial_match=True, boost=10),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
        index.FilterField('uploaded_by_user'),
    ]

    def __str__(self):
        return self.title

    @contextmanager
    def get_willow_image(self):
        # Open file if it is closed
        close_file = False
        try:
            image_file = self.file

            if self.file.closed:
                # Reopen the file
                if self.is_stored_locally():
                    self.file.open('rb')
                else:
                    # Some external storage backends don't allow reopening
                    # the file. Get a fresh file instance. #1397
                    storage = self._meta.get_field('file').storage
                    image_file = storage.open(self.file.name, 'rb')

                close_file = True
        except IOError as e:
            # re-throw this as a SourceImageIOError so that calling code can distinguish
            # these from IOErrors elsewhere in the process
            raise SourceImageIOError(text_type(e))

        # Seek to beginning
        image_file.seek(0)

        try:
            yield WillowImage.open(image_file)
        finally:
            if close_file:
                image_file.close()

    def get_rect(self):
        return Rect(0, 0, self.width, self.height)

    def get_focal_point(self):
        if self.focal_point_x is not None and \
           self.focal_point_y is not None and \
           self.focal_point_width is not None and \
           self.focal_point_height is not None:
            return Rect.from_point(
                self.focal_point_x,
                self.focal_point_y,
                self.focal_point_width,
                self.focal_point_height,
            )

    def has_focal_point(self):
        return self.get_focal_point() is not None

    def set_focal_point(self, rect):
        if rect is not None:
            self.focal_point_x = rect.centroid_x
            self.focal_point_y = rect.centroid_y
            self.focal_point_width = rect.width
            self.focal_point_height = rect.height
        else:
            self.focal_point_x = None
            self.focal_point_y = None
            self.focal_point_width = None
            self.focal_point_height = None

    def get_suggested_focal_point(self):
        with self.get_willow_image() as willow:
            faces = willow.detect_faces()

            if faces:
                # Create a bounding box around all faces
                left = min(face[0] for face in faces)
                top = min(face[1] for face in faces)
                right = max(face[2] for face in faces)
                bottom = max(face[3] for face in faces)
                focal_point = Rect(left, top, right, bottom)
            else:
                features = willow.detect_features()
                if features:
                    # Create a bounding box around all features
                    left = min(feature[0] for feature in features)
                    top = min(feature[1] for feature in features)
                    right = max(feature[0] for feature in features)
                    bottom = max(feature[1] for feature in features)
                    focal_point = Rect(left, top, right, bottom)
                else:
                    return None

        # Add 20% to width and height and give it a minimum size
        x, y = focal_point.centroid
        width, height = focal_point.size

        width *= 1.20
        height *= 1.20

        width = max(width, 100)
        height = max(height, 100)

        return Rect.from_point(x, y, width, height)

    @classmethod
    def get_rendition_model(cls):
        """ Get the Rendition model for this Image model """
        if django.VERSION >= (1, 9):
            return cls.renditions.rel.related_model
        else:
            return cls.renditions.related.related_model

    def get_rendition(self, filter):
        if isinstance(filter, string_types):
            filter = Filter(spec=filter)

        cache_key = filter.get_cache_key(self)
        Rendition = self.get_rendition_model()

        try:
            rendition = self.renditions.get(
                filter_spec=filter.spec,
                focal_point_key=cache_key,
            )
        except Rendition.DoesNotExist:
            # Generate the rendition image
            generated_image = filter.run(self, BytesIO())

            # Generate filename
            input_filename = os.path.basename(self.file.name)
            input_filename_without_extension, input_extension = os.path.splitext(
                input_filename)

            # A mapping of image formats to extensions
            FORMAT_EXTENSIONS = {
                'jpeg': '.jpg',
                'png': '.png',
                'gif': '.gif',
            }

            output_extension = filter.spec.replace(
                '|', '.') + FORMAT_EXTENSIONS[generated_image.format_name]
            if cache_key:
                output_extension = cache_key + '.' + output_extension

            # Truncate filename to prevent it going over 60 chars
            output_filename_without_extension = input_filename_without_extension[:(
                59 - len(output_extension))]
            output_filename = output_filename_without_extension + '.' + output_extension

            rendition, created = self.renditions.get_or_create(
                filter_spec=filter.spec,
                focal_point_key=cache_key,
                defaults={
                    'file': File(generated_image.f, name=output_filename)
                })

        return rendition

    def is_portrait(self):
        return (self.width < self.height)

    def is_landscape(self):
        return (self.height < self.width)

    @property
    def filename(self):
        return os.path.basename(self.file.name)

    @property
    def default_alt_text(self):
        # by default the alt text field (used in rich text insertion) is populated
        # from the title. Subclasses might provide a separate alt field, and
        # override this
        return self.title

    def is_editable_by_user(self, user):
        from wagtail.wagtailimages.permissions import permission_policy
        return permission_policy.user_has_permission_for_instance(
            user, 'change', self)

    class Meta:
        abstract = True
Ejemplo n.º 29
0
class AbstractMedia(CollectionMember, index.Indexed, models.Model):
    MEDIA_TYPES = (
        ('audio', _('Audio file')),
        ('video', _('Video file')),
    )

    title = models.CharField(max_length=255, verbose_name=_('title'))
    file = models.FileField(upload_to='media', verbose_name=_('file'))

    type = models.CharField(choices=MEDIA_TYPES,
                            max_length=255,
                            blank=False,
                            null=False)
    duration = models.PositiveIntegerField(verbose_name=_('duration'),
                                           help_text=_('Duration in seconds'))
    width = models.PositiveIntegerField(null=True,
                                        blank=True,
                                        verbose_name=_('width'))
    height = models.PositiveIntegerField(null=True,
                                         blank=True,
                                         verbose_name=_('height'))
    thumbnail = models.FileField(upload_to='media_thumbnails',
                                 blank=True,
                                 verbose_name=_('thumbnail'))

    created_at = models.DateTimeField(verbose_name=_('created at'),
                                      auto_now_add=True)
    uploaded_by_user = models.ForeignKey(settings.AUTH_USER_MODEL,
                                         verbose_name=_('uploaded by user'),
                                         null=True,
                                         blank=True,
                                         editable=False,
                                         on_delete=models.SET_NULL)

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('tags'))

    objects = MediaQuerySet.as_manager()

    search_fields = CollectionMember.search_fields + [
        index.SearchField('title', partial_match=True, boost=10),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
        index.FilterField('uploaded_by_user'),
    ]

    def __str__(self):
        return self.title

    @property
    def filename(self):
        return os.path.basename(self.file.name)

    @property
    def thumbnail_filename(self):
        return os.path.basename(self.thumbnail.name)

    @property
    def file_extension(self):
        return os.path.splitext(self.filename)[1][1:]

    @property
    def url(self):
        return self.file.url

    def get_usage(self):
        return get_object_usage(self)

    @property
    def usage_url(self):
        return reverse('wagtailmedia:media_usage', args=(self.id, ))

    def is_editable_by_user(self, user):
        from wagtailmedia.permissions import permission_policy
        return permission_policy.user_has_permission_for_instance(
            user, 'change', self)

    class Meta:
        abstract = True
        verbose_name = _('media')
Ejemplo n.º 30
0
class Video(CollectionMember, index.Indexed, models.Model):
    '''
    Video model
    '''
    block_id = models.CharField(max_length=128, unique=True)
    display_name = models.CharField(max_length=255)
    view_url = models.URLField(max_length=255)
    transcript_url = models.URLField(max_length=255, null=True)
    source_course_run = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('tags'))

    objects = VideoQuerySet.as_manager()

    search_fields = CollectionMember.search_fields + [
        index.SearchField('display_name', partial_match=True),
        index.SearchField('transcript',
                          partial_match=False,
                          es_extra=LARGE_TEXT_FIELD_SEARCH_PROPS),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
        index.FilterField('id'),
        index.FilterField('source_course_run'),
    ]

    def get_action_url_name(self, action):
        return '%s_%s_modeladmin_%s' % (self._meta.app_label,
                                        self._meta.object_name.lower(), action)

    def get_usage(self):
        return JournalPage.objects.filter(videos=self)

    def transcript(self):
        '''
        Read the transcript from the transcript url to provide
        to elasticsearch
        '''
        if not self.transcript_url:
            return None

        try:
            response = requests.get(
                self.transcript_url)  # No auth needed for transcripts
            contents = response.content
            return contents.decode(
                'utf-8')[:settings.
                         MAX_ELASTICSEARCH_UPLOAD_SIZE] if contents else None
        except Exception as err:  # pylint: disable=broad-except
            logger.error(
                'Exception trying to read transcript url={url} for Video err={err}'
                .format(url=self.transcript_url, err=err))
            return None

    @property
    def view_access_url(self):
        '''
        Return the url to access the video on LMS based on the Journal that the video
        is found in.
        '''
        journal_page = JournalPage.objects.filter(
            videos=self).live().distinct().first()
        journal_uuid = journal_page.get_journal().uuid if journal_page else 0

        url = self.view_url.replace("xblock", "journals/render_journal_block")
        return "{url}?journal_uuid={journal_uuid}".format(
            url=url, journal_uuid=journal_uuid)

    def __str__(self):
        return self.display_name