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

feat: Add Pull Request model and integrate with sync_repository #845

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0aff313
feat: Add Pull Request model and integrate with sync_repository
space-techy Feb 15, 2025
aa72a12
Merge branch 'main' into feature/github-pull-request-tracking
space-techy Feb 16, 2025
1bffa45
Merge branch 'main' into feature/github-pull-request-tracking
space-techy Feb 16, 2025
2a74139
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 17, 2025
022555a
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 17, 2025
180616d
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 18, 2025
ad3fe10
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 19, 2025
a1b81d9
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 20, 2025
8d1e84c
Add Pull Request Model and Sync repo
space-techy Feb 20, 2025
b601a62
Add Pull Request Mode and Improve Repository Model and Sync Repo
space-techy Feb 20, 2025
093b6bc
Merge branch 'main' into feature/github-pull-request-tracking
space-techy Feb 21, 2025
4b1d97f
resolve conflict with issue
space-techy Feb 24, 2025
3989495
resolve conflict with issue
space-techy Feb 24, 2025
c787aa1
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 24, 2025
4d03eb0
Update code
arkid15r Feb 25, 2025
df52c43
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 25, 2025
bf4eaf7
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 25, 2025
0bbd86e
remove issue part
space-techy Feb 25, 2025
ef422c7
Fix conflict
space-techy Feb 25, 2025
5268b4c
Fix conflict
space-techy Feb 25, 2025
03a0fa3
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 25, 2025
0963487
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 27, 2025
c609b75
Merge branch 'OWASP:main' into feature/github-pull-request-tracking
space-techy Feb 28, 2025
946bf40
Add Generic Issue and PR model
space-techy Feb 28, 2025
b43cbfe
Removed local migration
space-techy Feb 28, 2025
4bd0594
Add Generic Model and Migrations
space-techy Feb 28, 2025
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
32 changes: 32 additions & 0 deletions backend/apps/github/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from apps.github.models.issue import Issue
from apps.github.models.label import Label
from apps.github.models.organization import Organization
from apps.github.models.pull_request import PullRequest
from apps.github.models.release import Release
from apps.github.models.repository import Repository
from apps.github.models.repository_contributor import RepositoryContributor
Expand All @@ -16,6 +17,36 @@ class LabelAdmin(admin.ModelAdmin):
search_fields = ("name", "description")


class PullRequestAdmin(admin.ModelAdmin):
"""Admin settings for Pull Requests."""

autocomplete_fields = (
"repository",
"author",
"assignees",
"labels",
)
list_display = (
"repository",
"title",
"state",
"custom_field_github_url",
"created_at",
"updated_at",
)
list_filter = (
"state",
"merged_at",
)
search_fields = ("title", "author__login", "repository__name")

def custom_field_github_url(self, obj):
"""Pull Request GitHub URL."""
return mark_safe(f"<a href='{obj.url}' target='_blank'>↗️</a>") # noqa: S308

custom_field_github_url.short_description = "GitHub 🔗"


class IssueAdmin(admin.ModelAdmin):
autocomplete_fields = (
"repository",
Expand Down Expand Up @@ -114,6 +145,7 @@ class UserAdmin(admin.ModelAdmin):
search_fields = ("login", "name")


admin.site.register(PullRequest, PullRequestAdmin)
admin.site.register(Issue, IssueAdmin)
admin.site.register(Label, LabelAdmin)
admin.site.register(Organization, OrganizationAdmin)
Expand Down
43 changes: 43 additions & 0 deletions backend/apps/github/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from apps.github.models.issue import Issue
from apps.github.models.label import Label
from apps.github.models.organization import Organization
from apps.github.models.pull_request import PullRequest
from apps.github.models.release import Release
from apps.github.models.repository import Repository
from apps.github.models.repository_contributor import RepositoryContributor
Expand Down Expand Up @@ -91,6 +92,48 @@ def sync_repository(gh_repository, organization=None, user=None):
issue.labels.add(Label.update_data(gh_issue_label))
except UnknownObjectException:
logger.info("Couldn't get GitHub issue label %s", issue.url)
# GitHub Pull Requests Part!
else:
logger.info("Skipping issues sync for %s", repository.name)
if not repository.is_archived and repository.project:
kwargs = {
"direction": "asc",
"sort": "created",
"state": "open",
}
latest_pull_request = (
PullRequest.objects.filter(repository=repository).order_by("-updated_at").first()
)
if latest_pull_request:
# Sync open/closed issues for subsequent runs.
kwargs.update(
{
"since": latest_pull_request.updated_at,
"state": "all",
}
)

for gh_pull_request in gh_repository.get_pulls(**kwargs):
author = (
User.update_data(gh_pull_request.user)
if gh_pull_request.user and gh_pull_request.user.type != "Bot"
else None
)

pull_request = PullRequest.update_data(
gh_pull_request, author=author, repository=repository
)

pull_request.assignees.clear()
for gh_pull_request_assignee in gh_pull_request.assignees:
pull_request.assignees.add(User.update_data(gh_pull_request_assignee))

pull_request.labels.clear()
for gh_pull_request_label in gh_pull_request.labels:
try:
pull_request.labels.add(Label.update_data(gh_pull_request_label))
except UnknownObjectException:
logger.info("Couldn't get GitHub pull request label %s", pull_request.url)
else:
logger.info("Skipping issues sync for %s", repository.name)

Expand Down
106 changes: 106 additions & 0 deletions backend/apps/github/migrations/0016_pullrequest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Generated by Django 5.1.6 on 2025-02-15 11:03

import django.db.models.deletion
from django.db import migrations, models

import apps.github.models.mixins.issue


class Migration(migrations.Migration):
dependencies = [
("github", "0015_alter_release_author"),
]

operations = [
migrations.CreateModel(
name="PullRequest",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("nest_created_at", models.DateTimeField(auto_now_add=True)),
("nest_updated_at", models.DateTimeField(auto_now=True)),
("node_id", models.CharField(unique=True, verbose_name="Node ID")),
("title", models.CharField(max_length=1000, verbose_name="Title")),
("body", models.TextField(default="", verbose_name="Body")),
(
"state",
models.CharField(
choices=[("open", "Open"), ("closed", "Closed")],
default="open",
max_length=20,
verbose_name="State",
),
),
("url", models.URLField(default="", max_length=500, verbose_name="URL")),
("number", models.PositiveBigIntegerField(default=0, verbose_name="Number")),
(
"sequence_id",
models.PositiveBigIntegerField(default=0, verbose_name="Pull Requests ID"),
),
("is_locked", models.BooleanField(default=False, verbose_name="Is Locked")),
(
"comments_count",
models.PositiveIntegerField(default=0, verbose_name="Comments"),
),
(
"closed_at",
models.DateTimeField(blank=True, null=True, verbose_name="Closed at"),
),
("created_at", models.DateTimeField(verbose_name="Created at")),
("updated_at", models.DateTimeField(db_index=True, verbose_name="Updated at")),
(
"merged_at",
models.DateTimeField(blank=True, null=True, verbose_name="Merged at"),
),
(
"assignees",
models.ManyToManyField(
blank=True,
related_name="assigned_pull_requests",
to="github.user",
verbose_name="Assignees",
),
),
(
"author",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="created_pull_requests",
to="github.user",
verbose_name="Author",
),
),
(
"labels",
models.ManyToManyField(
blank=True,
related_name="pull_request_labels",
to="github.label",
verbose_name="Labels",
),
),
(
"repository",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="pull_requests",
to="github.repository",
),
),
],
options={
"verbose_name_plural": "Pull Requests",
"db_table": "github_pullrequests",
"ordering": ("-updated_at", "-state"),
},
bases=(apps.github.models.mixins.issue.IssueIndexMixin, models.Model),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Generated by Django 5.1.6 on 2025-02-15 20:47

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("github", "0016_pullrequest"),
]

operations = [
migrations.RemoveField(
model_name="pullrequest",
name="comments_count",
),
]
2 changes: 2 additions & 0 deletions backend/apps/github/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
"""Github app."""

from .pull_request import PullRequest
28 changes: 28 additions & 0 deletions backend/apps/github/models/managers/pullrequest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""GitHub app pull requests managers."""

from datetime import timedelta as td

from django.db import models
from django.utils import timezone


class OpenPullRequestManager(models.Manager):
"""Open pull requests."""

def get_queryset(self):
"""Get queryset."""
return (
super()
.get_queryset()
.select_related(
"repository",
)
.prefetch_related(
"assignees",
)
.filter(
created_at__gte=timezone.now() - td(days=90),
repository__project__isnull=False,
state="open",
)
)
Loading
Loading