Example #1
0
class CosinnusConferenceIndex(CosinnusGroupIndexMixin, TagObjectSearchIndex,
                              indexes.Indexable):

    text = TemplateResolveNgramField(
        document=True,
        use_template=True,
        template_name='search/indexes/cosinnus/cosinnusgroup_{field_name}.txt')
    rendered = TemplateResolveCharField(
        use_template=True,
        indexed=False,
        template_name='search/indexes/cosinnus/cosinnusgroup_{field_name}.txt')

    from_date = indexes.DateTimeField(model_attr='from_date', null=True)
    to_date = indexes.DateTimeField(model_attr='to_date', null=True)
    humanized_event_time_html = indexes.CharField(stored=True, indexed=False)
    participants_limit_count = indexes.IntegerField(stored=True, indexed=False)

    def get_model(self):
        return CosinnusConference

    def prepare_participant_count(self, obj):
        """ Mirrored the member count for simplicity """
        return self.prepare_member_count(obj)

    def prepare_participants_limit_count(self, obj):
        """ Mirrored the member count for simplicity """
        participation_managements = obj.participation_management.all()
        if len(participation_managements) > 0:
            return participation_managements[0].participants_limit
        return 0

    def prepare_humanized_event_time_html(self, obj):
        return obj.get_humanized_event_time_html()

    def boost_model(self, obj, indexed_data):
        """ We boost a combined measure of 2 added factors: soonishnes (50%) and participant count (50%).
            This means that a soon happening event with lots of participants will rank highest and an far off event 
            with no participants lowest.
            But it also means that soon happening events with no participants will still rank quite high, 
            as will far off events with lots of participants.
            
            Factors:
            - The conference's date, highest being now() and lowest >= 12 months from now
            """

        if obj.from_date:
            future_date_timedelta = obj.from_date - now()
            if future_date_timedelta.days < 0:
                if obj.to_date and (obj.to_date - now()).days > 0:
                    rank_from_date = 0.1  # running events rank not nothing
                else:
                    rank_from_date = 0.0  # past events rank worst
            else:
                rank_from_date = max(
                    1.0 - (future_date_timedelta.days / 365.0), 0)
        else:
            rank_from_date = 0.0
        return rank_from_date
Example #2
0
class ExternalBaseIndexMixin(indexes.SearchIndex):
    
    source = indexes.CharField(stored=True, indexed=False, model_attr='source')
    portal = indexes.IntegerField(default=EXTERNAL_CONTENT_PORTAL_ID)
    location = indexes.LocationField(null=True)
    
    """ from StoredDataIndexMixin """
    
    title = indexes.CharField(stored=True, indexed=False, model_attr='title')
    # slug for linking
    slug = indexes.CharField(stored=True, indexed=True, model_attr='slug')
    url = indexes.CharField(stored=True, indexed=False, model_attr='url')
    description = indexes.CharField(stored=True, indexed=False, model_attr='description')
    # the small icon image, should be a 144x144 image
    icon_image_url = indexes.CharField(stored=True, indexed=False, model_attr='icon_image_url')
    # the small background image or None, should be a 500x275 image
    background_image_small_url = indexes.CharField(stored=True, indexed=False, model_attr='background_image_small_url', null=True)
    # the large background image or None, should be a 1000x550 image
    background_image_large_url = indexes.CharField(stored=True, indexed=False, model_attr='background_image_large_url', null=True)
    # group slug for linking, subject to implementing indexed
    group_slug = indexes.CharField(stored=True, indexed=True, model_attr='group_slug', null=True)
    # group name for linking, subject to implementing indexed
    group_name = indexes.CharField(stored=True, indexed=False, model_attr='group_name', null=True)
    # attendees for events, projects for groups
    participant_count = indexes.IntegerField(stored=True, indexed=False, model_attr='participant_count', null=True)
    # member count for projects/groups, group-member count for events, memberships for users
    member_count = indexes.IntegerField(stored=True, indexed=False, model_attr='member_count', null=True)
    # groups/projects: number of upcoming events
    content_count = indexes.IntegerField(stored=True, indexed=False, model_attr='content_count', null=True)
    
    """ from DefaultTagObjectIndex """
        
    mt_location = indexes.CharField(stored=True, indexed=False, null=True, model_attr='mt_location')
    mt_location_lat = indexes.FloatField(null=True, model_attr='mt_location_lat')
    mt_location_lon = indexes.FloatField(null=True, model_attr='mt_location_lon')
    mt_topics = CommaSeperatedIntegerMultiValueField(null=True, model_attr='mt_topics')
    mt_visibility = indexes.IntegerField(stored=True, indexed=False, default=2)
    
    mt_public = indexes.BooleanField(default=True)
    
    text = TemplateResolveNgramField(document=True, model_attr='title')
    
    def prepare_location(self, obj):
        if obj.mt_location_lat and obj.mt_location_lon:
            # this expects (lat,lon)!
            return "%s,%s" % (obj.mt_location_lat, obj.mt_location_lon)
        return None
Example #3
0
class CosinnusSocietyIndex(CosinnusGroupIndexMixin, TagObjectSearchIndex,
                           indexes.Indexable):

    text = TemplateResolveNgramField(
        document=True,
        use_template=True,
        template_name='search/indexes/cosinnus/cosinnusgroup_{field_name}.txt')
    rendered = TemplateResolveCharField(
        use_template=True,
        indexed=False,
        template_name='search/indexes/cosinnus/cosinnusgroup_{field_name}.txt')

    def get_model(self):
        return CosinnusSociety

    def prepare_participant_count(self, obj):
        """ child projects for groups """
        return obj.groups.count()
Example #4
0
class CosinnusProjectIndex(CosinnusGroupIndexMixin, TagObjectSearchIndex,
                           indexes.Indexable):

    text = TemplateResolveNgramField(
        document=True,
        use_template=True,
        template_name='search/indexes/cosinnus/cosinnusgroup_{field_name}.txt')
    rendered = TemplateResolveCharField(
        use_template=True,
        indexed=False,
        template_name='search/indexes/cosinnus/cosinnusgroup_{field_name}.txt')

    def get_model(self):
        return CosinnusProject

    def prepare_group_slug(self, obj):
        """ For projects assigned to a parent group, we link that group """
        return obj.parent and obj.parent.slug or None

    def prepare_group_name(self, obj):
        """ Stub, overridden by individual indexes """
        return obj.parent and obj.parent.name or None
Example #5
0
class IdeaSearchIndex(LocalCachedIndexMixin, DocumentBoostMixin,
                      TagObjectSearchIndex, StoredDataIndexMixin,
                      indexes.Indexable):

    text = TemplateResolveNgramField(document=True, use_template=True)
    boosted = indexes.NgramField(model_attr='title', boost=BOOSTED_FIELD_BOOST)

    public = indexes.BooleanField(model_attr='public')
    visible_for_all_authenticated_users = indexes.BooleanField()
    creator = indexes.IntegerField(model_attr='creator__id', null=True)
    portal = indexes.IntegerField(model_attr='portal_id')
    location = indexes.LocationField(null=True)
    liked_user_ids = CommaSeperatedIntegerMultiValueField(indexed=False,
                                                          stored=True)

    local_cached_attrs = ['_like_count', '_liked_ids']

    def get_model(self):
        return CosinnusIdea

    def prepare_visible_for_all_authenticated_users(self, obj):
        """ This is hacky, but Haystack provides no method to filter
            for models in subqueries, so we set this indexed flag to be
            able to filter on for permissions """
        return True

    def prepare_liked_user_ids(self, obj):
        return obj.get_liked_user_ids()

    def prepare_participant_count(self, obj):
        """ Group member count for taggable objects """
        return obj.like_count

    def prepare_member_count(self, obj):
        return self.prepare_participant_count(obj)

    def prepare_content_count(self, obj):
        return obj.created_groups.filter(is_active=True).count()

    def prepare_location(self, obj):
        if obj.media_tag and obj.media_tag.location_lat and obj.media_tag.location_lon:
            # this expects (lat,lon)!
            return "%s,%s" % (obj.media_tag.location_lat,
                              obj.media_tag.location_lon)
        return None

    def get_image_field_for_background(self, obj):
        return obj.image

    def index_queryset(self, using=None):
        qs = self.get_model().objects.active()
        qs = qs.select_related('media_tag')
        return qs

    def boost_model(self, obj, indexed_data):
        """ We boost a combined measure of 2 added factors: newness (50%) and like count (50%).
            This means that a new idea with lots of likes will rank highest and an old idea with no likes lowest.
            But it also means that new ideas with no likes will still rank quite high, as will old ideas with lots
            of likes.
            
            Factors:
            - 50%: the idea's like count, normalized over the mean/stddev of the like count of all other ideas, 
                in a range of [0.0..1.0]
            - 50%: the idea's created date, highest being now() and lowest >= 3 months
            """
        def qs_func():
            return CosinnusIdea.objects.all_in_portal().annotate_likes()

        mean, stddev = self.get_mean_and_stddev(qs_func,
                                                'like_count',
                                                non_annotated_property=True)
        current_like_count = obj.likes.filter(liked=True).count()
        members_rank_from_likes = normalize_within_stddev(current_like_count,
                                                          mean,
                                                          stddev,
                                                          stddev_factor=1.0)

        age_timedelta = now() - obj.created
        members_rank_from_date = max(1.0 - (age_timedelta.days / 90.0), 0)

        return (members_rank_from_likes / 2.0) + (members_rank_from_date / 2.0)

    def apply_boost_penalty(self, obj, indexed_data):
        """ Penaliize by 15% for not having a wallpaper image.
            @return: 1.0 for no penalty, a float in range [0.0..1.0] for a penalty
        """
        if not self.get_image_field_for_background(obj):
            return DEFAULT_BOOST_PENALTY_FOR_MISSING_IMAGE
        return 1.0
Example #6
0
class UserProfileIndex(LocalCachedIndexMixin, DocumentBoostMixin,
                       StoredDataIndexMixin, TagObjectSearchIndex,
                       indexes.Indexable):
    text = TemplateResolveNgramField(
        document=True,
        use_template=True,
        template_name='search/indexes/cosinnus/userprofile_{field_name}.txt')
    rendered = TemplateResolveCharField(
        use_template=True,
        indexed=False,
        template_name='search/indexes/cosinnus/userprofile_{field_name}.txt')

    boosted = indexes.CharField(model_attr='get_full_name',
                                boost=BOOSTED_FIELD_BOOST)

    user_visibility_mode = indexes.BooleanField(
        default=True)  # switch to filter differently on mt_visibility
    membership_groups = indexes.MultiValueField(
        model_attr='cosinnus_groups_pks'
    )  # ids of all groups the user is member/admin of
    admin_groups = indexes.MultiValueField(
    )  # ids of all groups the user is member/admin of
    portals = indexes.MultiValueField()
    location = indexes.LocationField(null=True)
    user_id = indexes.IntegerField(model_attr='user__id')
    created = indexes.DateTimeField(model_attr='user__date_joined')

    local_cached_attrs = ['_memberships_count']

    def prepare_portals(self, obj):
        return list(
            obj.user.cosinnus_portal_memberships.values_list('group_id',
                                                             flat=True))

    def prepare_location(self, obj):
        if obj.media_tag and obj.media_tag.location_lat and obj.media_tag.location_lon:
            # this expects (lat,lon)!
            return "%s,%s" % (obj.media_tag.location_lat,
                              obj.media_tag.location_lon)
        return None

    def prepare_title(self, obj):
        return obj.user.get_full_name()

    def prepare_slug(self, obj):
        return obj.user.username

    def get_image_field_for_icon(self, obj):
        return obj.get_image_field_for_icon()

    def prepare_url(self, obj):
        """ NOTE: UserProfiles always contain a relative URL! """
        return reverse('cosinnus:profile-detail',
                       kwargs={'username': obj.user.username})

    def prepare_member_count(self, obj):
        """ Memberships for users """
        return self._get_memberships_count(obj)

    def prepare_admin_groups(self, obj):
        return list(
            get_cosinnus_group_model().objects.get_for_user_group_admin_pks(
                obj.user))

    def get_model(self):
        return get_user_profile_model()

    def index_queryset(self, using=None):
        qs = self.get_model().objects.all()
        qs = filter_active_users(qs, filter_on_user_profile_model=True)
        qs = qs.select_related('user').all()
        return qs

    def _get_memberships_count(self, obj):
        if not hasattr(obj, '_memberships_count'):
            setattr(obj, '_memberships_count',
                    obj.user.cosinnus_memberships.count())
        return obj._memberships_count

    def boost_model(self, obj, indexed_data):
        """ We boost by number of groups the user is a member of, normalized over
            the mean/stddev of the count of groups each portal user is a members of, 
            in a range of [0.0..1.0] """
        def qs_func():
            return filter_portal_users(
                filter_active_users(get_user_model().objects.all()))

        mean, stddev = self.get_mean_and_stddev(qs_func,
                                                'cosinnus_memberships')
        user_memberships_count = self._get_memberships_count(obj)
        memberships_rank = normalize_within_stddev(user_memberships_count,
                                                   mean,
                                                   stddev,
                                                   stddev_factor=2.0)
        return memberships_rank

    def apply_boost_penalty(self, obj, indexed_data):
        """ Penaliize by 15% for not having an avatar image.
            @return: 1.0 for no penalty, a float in range [0.0..1.0] for a penalty
        """
        if not self.get_image_field_for_icon(obj):
            return DEFAULT_BOOST_PENALTY_FOR_MISSING_IMAGE
        return 1.0