Exemple #1
0
def search(request):
    """Find users by username and displayname.

    Uses the ES user's index.
    """
    results = []
    q = request.GET.get("q")

    if q:
        search = ProfileDocument.search().query(
            "simple_query_string",
            query=q,
            fields=["username", "name"],
            default_operator="AND",
        )

        results = search.execute().hits

    # For now, we're just truncating results at 30 and not doing any
    # pagination. If somebody complains, we can add pagination or something.
    results = list(results[:30])

    data = {"q": q, "results": results}

    return render(request, "community/search.html", data)
Exemple #2
0
def search(request):
    """Find users by username and displayname.

    Uses the ES user's index.
    """
    results = []
    q = request.GET.get("q")

    if q:
        contributor_group_ids = list(
            Group.objects.filter(
                name__in=[
                    "Contributors",
                    CONTRIBUTOR_GROUP,
                ]
            ).values_list("id", flat=True)
        )
        search = ProfileDocument.search().query(
            "boosting",
            positive=Q(
                "simple_query_string",
                query=q,
                fields=["username", "name"],
                default_operator="AND",
            ),
            # reduce the scores of users not in the contributor groups:
            negative=Q(
                "bool",
                must_not=Q("terms", group_ids=contributor_group_ids),
            ),
            negative_boost=0.5,
        )[:30]

        results = search.execute().hits

    data = {"q": q, "results": results}

    return render(request, "community/search.html", data)
Exemple #3
0
def top_contributors_questions(start=None,
                               end=None,
                               locale=None,
                               product=None,
                               count=10,
                               page=1):
    """Get the top Support Forum contributors."""

    search = AnswerDocument.search()

    search = (
        search.filter(
            # filter out answers by the question author
            "script",
            script=
            "doc['creator_id'].value != doc['question_creator_id'].value",
        ).filter(
            # filter answers created between `start` and `end`, or within the last 90 days
            "range",
            created={
                "gte": start or datetime.now() - timedelta(days=90),
                "lte": end
            },
        )
        # set the query size to 0 because we don't care about the results
        # we're just filtering for the aggregations defined below
        .extra(size=0))
    if locale:
        search = search.filter("term", locale=locale)
    if product:
        search = search.filter("term", question_product_id=product.id)

    # our filters above aren't perfect, and don't only return answers from contributors
    # so we need to collect more buckets than `count`, so we can hopefully find `count`
    # number of contributors within
    search.aggs.bucket(
        # create buckets for the `count * 10` most active users
        "contributions",
        A("terms", field="creator_id", size=count * 10),
    ).bucket(
        # within each of those, create a bucket for the most recent answer, and extract its date
        "latest",
        A(
            "top_hits",
            sort={"created": {
                "order": "desc"
            }},
            _source={"includes": "created"},
            size=1,
        ),
    )

    contribution_buckets = search.execute().aggregations.contributions.buckets

    if not contribution_buckets:
        return [], 0

    user_ids = [bucket.key for bucket in contribution_buckets]
    contributor_group_ids = list(
        Group.objects.filter(name__in=CONTRIBUTOR_GROUPS).values_list(
            "id", flat=True))

    # fetch all the users returned by the aggregation which are in the contributor groups
    user_hits = (ProfileDocument.search().query("terms", **{
        "_id": user_ids
    }).query("terms", group_ids=contributor_group_ids).extra(
        size=len(user_ids)).execute().hits)
    users = {hit.meta.id: hit for hit in user_hits}

    total_contributors = len(user_hits)
    top_contributors = []
    for bucket in contribution_buckets:
        if len(top_contributors) == page * count:
            # stop once we've collected enough contributors
            break
        user = users.get(bucket.key)
        if user is None:
            continue
        last_activity = datetime.fromisoformat(
            bucket.latest.hits.hits[0]._source.created)
        days_since_last_activity = (datetime.now(tz=timezone.utc) -
                                    last_activity).days
        top_contributors.append({
            "count": bucket.doc_count,
            "term": bucket.key,
            "user": {
                "id": user.meta.id,
                "username": user.username,
                "display_name": user.name,
                "avatar": getattr(getattr(user, "avatar", None), "url", None),
                "days_since_last_activity": days_since_last_activity,
            },
        })

    return top_contributors[count * (page - 1):], total_contributors
Exemple #4
0
 def get_doc(self):
     return ProfileDocument.get(self.user_id)
Exemple #5
0
def top_contributors_questions(start=None, end=None, locale=None, product=None, count=10):
    """Get the top Support Forum contributors."""

    search = AnswerDocument.search()

    search = search.filter(
        # filter out answers by the question author
        "script",
        script="doc['creator_id'].value != doc['question_creator_id'].value",
    ).filter(
        # filter answers created between `start` and `end`, or within the last 90 days
        "range",
        created={"gte": start or datetime.now() - timedelta(days=90), "lte": end},
    )
    if locale:
        search = search.filter("term", locale=locale)
    if product:
        search = search.filter("term", question_product_id=product.id)

    search.aggs.bucket(
        # create buckets for the `count + 1` most active contributors
        "contributions",
        A("terms", field="creator_id", size=count + 1),
    ).bucket(
        # within each of those, create a bucket for the most recent answer, and extract its date
        "latest",
        A(
            "top_hits",
            sort={"created": {"order": "desc"}},
            _source={"includes": "created"},
            size=1,
        ),
    )

    contribution_buckets = search.execute().aggregations.contributions.buckets
    total_contributors = len(contribution_buckets)

    if not total_contributors:
        return [], 0

    user_ids = [bucket.key for bucket in contribution_buckets[:count]]
    users = ProfileDocument.mget(user_ids, missing="none")

    top_contributors = []
    for loop_index, bucket in enumerate(contribution_buckets[:count]):
        user = users[loop_index]
        if user is None:
            continue
        if bucket.key != user.meta.id:
            raise RuntimeError("ES returned profiles out of order")
        last_activity = datetime.fromisoformat(bucket.latest.hits.hits[0]._source.created)
        days_since_last_activity = (datetime.now(tz=timezone.utc) - last_activity).days
        top_contributors.append(
            {
                "count": bucket.doc_count,
                "term": bucket.key,
                "user": {
                    "id": user.meta.id,
                    "username": user.username,
                    "display_name": user.name,
                    "avatar": getattr(user.avatar, "url", None),
                    "days_since_last_activity": days_since_last_activity,
                },
            }
        )

    return top_contributors, total_contributors
Exemple #6
0
 def doc(self):
     return ProfileDocument.get(self.profile.pk)
Exemple #7
0
 def prepare(self):
     return ProfileDocument.prepare(self.profile)