Skip to content

Added few changes to repository_contributor file #1383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 10 additions & 44 deletions backend/apps/github/graphql/queries/repository_contributor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"""OWASP repository contributor GraphQL queries."""

import graphene
from django.db.models import F, Window
from django.db.models.functions import Rank

from apps.common.graphql.queries import BaseQuery
from apps.github.graphql.nodes.repository_contributor import RepositoryContributorNode
Expand All @@ -16,66 +14,34 @@ class RepositoryContributorQuery(BaseQuery):
RepositoryContributorNode,
limit=graphene.Int(default_value=15),
organization=graphene.String(required=False),
project=graphene.String(required=False),
)

def resolve_top_contributors(root, info, limit, organization=None):
"""Resolve top contributors only for repositories with projects.
def resolve_top_contributors(root, info, limit, organization=None, project=None):
"""Resolve top contributors.

Args:
root (Any): The root query object.
info (ResolveInfo): The GraphQL execution context.
limit (int): Maximum number of contributors to return.
organization (str, optional): Organization login to filter by.
project (str, optional): Project key to filter by.

Returns:
list: List of top contributors with their details.

"""
queryset = (
RepositoryContributor.objects.by_humans()
.to_community_repositories()
.filter(repository__project__isnull=False)
.select_related("repository__project", "user")
.annotate(
rank=Window(
expression=Rank(),
order_by=F("contributions_count").desc(),
partition_by=F("user__login"),
)
)
)

if organization:
queryset = queryset.select_related(
"repository__organization",
).filter(
repository__organization__login=organization,
)

top_contributors = (
queryset.filter(rank=1)
.annotate(
project_name=F("repository__project__name"),
project_key=F("repository__project__key"),
)
.values(
"contributions_count",
"user__avatar_url",
"user__login",
"user__name",
"project_key",
"project_name",
)
.order_by("-contributions_count")[:limit]
top_contributors = RepositoryContributor.get_top_contributors(
organization=organization, project=project, limit=limit
)

return [
RepositoryContributorNode(
avatar_url=trc["user__avatar_url"],
avatar_url=trc["avatar_url"],
contributions_count=trc["contributions_count"],
login=trc["user__login"],
name=trc["user__name"],
project_key=trc["project_key"].replace("www-project-", ""),
login=trc["login"],
name=trc["name"],
project_key=trc["project_key"],
project_name=trc["project_name"],
)
for trc in top_contributors
Expand Down
20 changes: 1 addition & 19 deletions backend/apps/github/models/mixins/repository.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"""GitHub repository mixins."""

from apps.github.models.repository_contributor import (
TOP_CONTRIBUTORS_LIMIT,
RepositoryContributor,
)
from apps.github.models.user import User


class RepositoryIndexMixin:
Expand Down Expand Up @@ -103,23 +101,7 @@ def idx_subscribers_count(self):
@property
def idx_top_contributors(self):
"""Return top contributors for indexing."""
return [
{
"avatar_url": tc["user__avatar_url"],
"contributions_count": tc["contributions_count"],
"login": tc["user__login"],
"name": tc["user__name"],
}
for tc in RepositoryContributor.objects.filter(repository=self)
.exclude(user__login__in=User.get_non_indexable_logins())
.values(
"contributions_count",
"user__avatar_url",
"user__login",
"user__name",
)
.order_by("-contributions_count")[:TOP_CONTRIBUTORS_LIMIT]
]
return RepositoryContributor.get_top_contributors(repositories=[self])

@property
def idx_topics(self):
Expand Down
87 changes: 87 additions & 0 deletions backend/apps/github/models/repository_contributor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Github app label model."""

from django.db import models
from django.db.models import F, Sum, Window
from django.db.models.functions import Rank
from django.template.defaultfilters import pluralize

from apps.common.models import BulkSaveModel, TimestampedModel
Expand Down Expand Up @@ -102,3 +104,88 @@ def update_data(gh_contributor, repository, user, save=True):
repository_contributor.save()

return repository_contributor

@classmethod
def get_top_contributors(
cls, repositories=None, organization=None, project=None, limit=TOP_CONTRIBUTORS_LIMIT
):
"""Get top contributors across repositories, organization, or project.

Args:
repositories (QuerySet or list, optional): Repositories to filter by.
organization (str, optional): Organization login to filter by.
project (str, optional): Project key to filter by.
limit (int, optional): Maximum number of contributors to return.

Returns:
list: List of dictionaries containing contributor information.

"""
queryset = (
cls.objects.by_humans()
.to_community_repositories()
.select_related("repository__project", "user")
)

if repositories:
queryset = queryset.filter(repository__in=repositories)

if organization:
queryset = queryset.select_related(
"repository__organization",
).filter(
repository__organization__login=organization,
)

if project:
queryset = queryset.filter(
repository__project__key__iexact=f"www-project-{project}" if project else None
)

if not repositories:
# Only apply project filter when not filtering by repositories
queryset = queryset.filter(repository__project__isnull=False)

# Rank by contributions when looking across repositories
queryset = queryset.annotate(
rank=Window(
expression=Rank(),
order_by=F("contributions_count").desc(),
partition_by=F("user__login"),
)
)

# Get top contributor per user
queryset = queryset.filter(rank=1)

# Aggregate total contributions for users
result = queryset.values(
"user__avatar_url",
"user__login",
"user__name",
).annotate(total_contributions=Sum("contributions_count"))

# Add project information if we're not querying by project or organization
if not project and not organization and not repositories:
result = result.annotate(
project_name=F("repository__project__name"),
project_key=F("repository__project__key"),
)

# Order and limit
result = result.order_by("-total_contributions")[:limit]

# Format the result
return [
{
"avatar_url": item["user__avatar_url"],
"contributions_count": item["total_contributions"],
"login": item["user__login"],
"name": item["user__name"],
"project_key": item.get("project_key", "").replace("www-project-", "")
if item.get("project_key")
else None,
"project_name": item.get("project_name"),
}
for item in result
]
20 changes: 1 addition & 19 deletions backend/apps/owasp/models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@

import yaml
from django.db import models
from django.db.models import Sum

from apps.common.open_ai import OpenAi
from apps.github.constants import (
GITHUB_REPOSITORY_RE,
GITHUB_USER_RE,
)
from apps.github.models.repository_contributor import (
TOP_CONTRIBUTORS_LIMIT,
RepositoryContributor,
)
from apps.github.utils import get_repository_file_content
Expand Down Expand Up @@ -197,23 +195,7 @@ def get_related_url(self, url, exclude_domains=(), include_domains=()):

def get_top_contributors(self, repositories=()):
"""Get top contributors."""
return [
{
"avatar_url": tc["user__avatar_url"],
"contributions_count": tc["total_contributions"],
"login": tc["user__login"],
"name": tc["user__name"],
}
for tc in RepositoryContributor.objects.by_humans()
.filter(repository__in=repositories)
.values(
"user__avatar_url",
"user__login",
"user__name",
)
.annotate(total_contributions=Sum("contributions_count"))
.order_by("-total_contributions")[:TOP_CONTRIBUTORS_LIMIT]
]
return RepositoryContributor.get_top_contributors(repositories=repositories)

def parse_tags(self, tags):
"""Parse entity tags."""
Expand Down