Skip to content
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

API's to Fetch and Integrate the updated Projects and the Contributors #2464

Merged
merged 13 commits into from
Jul 23, 2024
Merged
6 changes: 6 additions & 0 deletions blt/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
IssueViewSet,
LeaderboardApiViewSet,
LikeIssueApiView,
ProjectViewSet,
StatsApiViewset,
UrlCheckApiViewset,
UserIssueViewSet,
Expand Down Expand Up @@ -535,6 +536,11 @@
),
path("api/chatbot/conversation/", chatbot_conversation, name="chatbot_conversation"),
path("blt-tomato/", blt_tomato, name="blt-tomato"),
path(
"api/v1/projects/",
ProjectViewSet.as_view({"get": "list", "post": "create", "patch": "update"}),
name="projects_api",
),
]

if settings.DEBUG:
Expand Down
91 changes: 90 additions & 1 deletion website/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,41 @@
from django.core.mail import send_mail
from django.db.models import Count, Q, Sum
from django.template.loader import render_to_string
from django.utils.text import slugify
from rest_framework import filters, status, viewsets
from rest_framework.authentication import TokenAuthentication
from rest_framework.pagination import PageNumberPagination
from rest_framework.permissions import AllowAny, IsAuthenticated, IsAuthenticatedOrReadOnly
from rest_framework.permissions import (
AllowAny,
IsAdminUser,
IsAuthenticated,
IsAuthenticatedOrReadOnly,
)
from rest_framework.response import Response
from rest_framework.views import APIView

from website.models import (
Company,
Contributor,
Domain,
Hunt,
HuntPrize,
InviteFriend,
Issue,
IssueScreenshot,
Points,
Project,
User,
UserProfile,
)
from website.serializers import (
BugHuntPrizeSerializer,
BugHuntSerializer,
CompanySerializer,
ContributorSerializer,
DomainSerializer,
IssueSerializer,
ProjectSerializer,
UserProfileSerializer,
)
from website.views import LeaderboardBase, image_validator
Expand Down Expand Up @@ -649,3 +659,82 @@ class CompanyViewSet(viewsets.ModelViewSet):
filter_backends = (filters.SearchFilter,)
search_fields = ("id", "name")
http_method_names = ("get", "post", "put")


class ContributorViewSet(viewsets.ModelViewSet):
queryset = Contributor.objects.all()
serializer_class = ContributorSerializer
http_method_names = ("get", "post", "put")


class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
# permission_classes = (IsAuthenticatedOrReadOnly,)
http_method_names = ("get", "post", "put", "patch")

def create(self, request, *args, **kwargs):
data = request.data.copy()

name = data.get("name", "")
slug = slugify(name)

contributors = Project.get_contributors(self, data["github_url"]) # get contributors

serializer = ProjectSerializer(data=data)

if serializer.is_valid():
project_instance = serializer.save()
project_instance.__setattr__("slug", slug)

# If the logo is not provided get the logo from the repo itself
if project_instance.logo == "":
logo = project_instance.get_github_logo_save_to_storage(project_instance.github_url)
if logo:
project_instance.logo = logo

project_instance.save()

# Set contributors
if contributors:
project_instance.contributors.set(contributors)

serializer = ProjectSerializer(project_instance)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

def list(self, request, *args, **kwargs):
projects = Project.objects.prefetch_related("contributors").all()

project_data = []
for project in projects:
contributors_data = []
for contributor in project.contributors.all():
contributor_info = ContributorSerializer(contributor)
contributors_data.append(contributor_info.data)
contributors_data.sort(key=lambda x: x["contributions"], reverse=True)
project_info = ProjectSerializer(project).data
project_info["contributors"] = contributors_data
project_data.append(project_info)

return Response(
{"count": len(project_data), "projects": project_data},
status=200,
)

def update(self, request, *args, **kwargs):
if IsAdminUser.has_permission(self=self, request=request, view=""):
projects = Project.objects.prefetch_related("contributors").all()
for project in projects:
contributors = Project.get_contributors(self, github_url=project.github_url)
project.contributors.set(contributors)
serializer = ProjectSerializer(projects, many=True)
return Response(
{"count": len(projects), "projects": serializer.data}, status=status.HTTP_200_OK
)
return Response(
{"success": False, "message": "Only admin's can access this api."},
status=status.HTTP_400_BAD_REQUEST,
)
70 changes: 54 additions & 16 deletions website/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from colorthief import ColorThief
from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.exceptions import MultipleObjectsReturned, ValidationError
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.core.validators import URLValidator
Expand Down Expand Up @@ -658,6 +658,18 @@ def __str__(self):
return f"Q: {self.question} | A: {self.answer} at {self.timestamp}"


class Contributor(models.Model):
name = models.CharField(max_length=255)
github_id = models.IntegerField(unique=True)
github_url = models.URLField()
avatar_url = models.URLField()
contributor_type = models.CharField(max_length=255) # type = User, Bot ,... etc
contributions = models.PositiveIntegerField()

def __str__(self):
return self.name


class Project(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
Expand All @@ -668,22 +680,48 @@ class Project(models.Model):
logo = models.ImageField(upload_to="project_logos", null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
contributors = models.ManyToManyField(
Contributor, related_name="projects", null=True, blank=True
)

def __str__(self):
return self.name

# # these need to be tested
# def get_github_logo_save_to_storage(self):
# github_logo = requests.get(f"{self.github_url}/raw/master/logo.png")
# if github_logo.status_code == 200:
# self.logo.save(f"{self.slug}.png", ContentFile(github_logo.content))
# self.save()
# return self.logo.url

# def save(self, *args, **kwargs):
# if not self.logo:
# self.get_github_logo_save_to_storage()
# super().save(*args, **kwargs)

def get_absolute_url(self):
return f"/project/{self.slug}"
def get_github_logo_save_to_storage(self, url):
owner = url.split("/")
link = "https://avatars.githubusercontent.com/" + owner[-2]
github_logo = requests.get(
link
) # the image there is of the owner so we will have this as the logo
if github_logo.status_code == 200:
file_name = f"{self.slug}.png"
self.logo.save(file_name, ContentFile(github_logo.content), save=False)
return self.logo
return None

def get_contributors(self, github_url):
owner = github_url.split("/")
url = "https://api.github.com/repos/" + owner[-2] + "/" + owner[-1] + "/contributors"
Uttkarsh-raj marked this conversation as resolved.
Show resolved Hide resolved
print("url : " + url)
response = requests.get(url)
if response.status_code == 200:
contributors_data = response.json()
contributors = []
for c in contributors_data:
try:
contributor, created = Contributor.objects.get_or_create(
github_id=c["id"],
defaults={
"name": c["login"],
"github_url": c["html_url"],
"avatar_url": c["avatar_url"],
"contributor_type": c["type"],
"contributions": c["contributions"],
},
)
contributors.append(contributor)
except MultipleObjectsReturned:
contributor = Contributor.objects.filter(github_id=c["id"]).first()
contributors.append(contributor)
return contributors
return None
26 changes: 25 additions & 1 deletion website/serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
from django.db.models import Sum
from rest_framework import serializers

from website.models import Company, Domain, Hunt, HuntPrize, Issue, Points, User, UserProfile
from website.models import (
Company,
Contributor,
Domain,
Hunt,
HuntPrize,
Issue,
Points,
Project,
User,
UserProfile,
)


class UserSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -94,3 +105,16 @@ class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = "__all__"


class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = "__all__"
read_only_fields = ("slug", "contributors")


class ContributorSerializer(serializers.ModelSerializer):
class Meta:
model = Contributor
fields = "__all__"
Loading