Skip to content

Commit 7590a6d

Browse files
committed
Added contribution app (#2022)
1 parent c1ee1da commit 7590a6d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+10061
-54
lines changed

config/settings.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
"testimonials",
125125
"patches",
126126
"asciidoctor_sandbox",
127+
"contributions",
127128
]
128129

129130
AUTH_USER_MODEL = "users.User"
@@ -386,7 +387,8 @@
386387
)
387388

388389
# GitHub settings
389-
390+
GITHUB_API_REST_URL = "https://api.github.com/repos"
391+
GITHUB_API_GRAPHQL_URL = "https://api.github.com/graphql"
390392
GITHUB_TOKEN = env("GITHUB_TOKEN", default=None)
391393
JDOODLE_API_CLIENT_ID = env("JDOODLE_API_CLIENT_ID", "")
392394
JDOODLE_API_CLIENT_SECRET = env("JDOODLE_API_CLIENT_SECRET", "")

contributions/__init__.py

Whitespace-only changes.

contributions/admin.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
from django.contrib import admin
2+
from django.db.models import Count
3+
4+
from .models import (
5+
Email,
6+
GitContribution,
7+
GitProfile,
8+
GithubContribution,
9+
GithubProfile,
10+
Identity,
11+
Wg21Contribution,
12+
Wg21Profile,
13+
)
14+
15+
16+
class ReadOnlyInlineMixin:
17+
"""Base mixin for read-only inline admin classes."""
18+
19+
extra = 0
20+
readonly_fields = ["created", "modified"]
21+
22+
23+
class ReadOnlyAdminMixin:
24+
"""Base mixin for read-only admin classes."""
25+
26+
readonly_fields = ["created", "modified"]
27+
28+
def has_add_permission(self, request):
29+
return False
30+
31+
32+
class GitProfileInline(ReadOnlyInlineMixin, admin.TabularInline):
33+
model = GitProfile
34+
fields = ["name", "email", "identity"]
35+
autocomplete_fields = ["email", "identity"]
36+
37+
38+
class GithubProfileInline(ReadOnlyInlineMixin, admin.TabularInline):
39+
model = GithubProfile
40+
fields = ["name", "github_user_id", "identity"]
41+
autocomplete_fields = ["identity"]
42+
43+
44+
class Wg21ProfileInline(ReadOnlyInlineMixin, admin.TabularInline):
45+
model = Wg21Profile
46+
fields = ["name", "identity"]
47+
autocomplete_fields = ["identity"]
48+
49+
50+
class GithubContributionInline(ReadOnlyInlineMixin, admin.TabularInline):
51+
model = GithubContribution
52+
fields = ["type", "contributed_at", "repo", "info"]
53+
54+
55+
class Wg21ContributionInline(ReadOnlyInlineMixin, admin.TabularInline):
56+
model = Wg21Contribution
57+
fields = ["info", "comment", "contributed_at"]
58+
59+
60+
class GitContributionInline(ReadOnlyInlineMixin, admin.TabularInline):
61+
model = GitContribution
62+
fields = ["contributed_at", "repo", "info", "comment"]
63+
64+
65+
@admin.register(Email)
66+
class EmailAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
67+
list_display = ["email", "git_profile_count", "created"]
68+
search_fields = ["email"]
69+
ordering = ["-created"]
70+
inlines = [GitProfileInline]
71+
72+
def get_queryset(self, request):
73+
return (
74+
super()
75+
.get_queryset(request)
76+
.annotate(_git_profile_count=Count("git_profiles", distinct=True))
77+
)
78+
79+
@admin.display(
80+
description="Git Profiles",
81+
ordering="_git_profile_count",
82+
)
83+
def git_profile_count(self, obj):
84+
return obj._git_profile_count
85+
86+
87+
class ProfileTypeFilter(admin.SimpleListFilter):
88+
"""Filter identities by profile type."""
89+
90+
title = "profile type"
91+
parameter_name = "profile_type"
92+
93+
PROFILE_FILTERS = {
94+
"git": "gitprofile_profiles__isnull",
95+
"github": "githubprofile_profiles__isnull",
96+
"wg21": "wg21profile_profiles__isnull",
97+
}
98+
99+
def lookups(self, request, model_admin):
100+
return (
101+
("git", "Has Git Profile"),
102+
("github", "Has GitHub Profile"),
103+
("wg21", "Has WG21 Profile"),
104+
)
105+
106+
def queryset(self, request, queryset):
107+
if filter_field := self.PROFILE_FILTERS.get(self.value()):
108+
return queryset.filter(**{filter_field: False}).distinct()
109+
return queryset
110+
111+
112+
@admin.register(Identity)
113+
class IdentityAdmin(admin.ModelAdmin):
114+
list_display = ["name", "profile_type", "needs_review", "created"]
115+
list_filter = [ProfileTypeFilter, "needs_review", "created"]
116+
search_fields = ["name", "description"]
117+
readonly_fields = ["created", "modified"]
118+
ordering = ["-created"]
119+
inlines = [GitProfileInline, GithubProfileInline, Wg21ProfileInline]
120+
121+
def get_queryset(self, request):
122+
return (
123+
super()
124+
.get_queryset(request)
125+
.annotate(
126+
_git_profile_count=Count("gitprofile_profiles", distinct=True),
127+
_github_profile_count=Count("githubprofile_profiles", distinct=True),
128+
_wg21_profile_count=Count("wg21profile_profiles", distinct=True),
129+
)
130+
)
131+
132+
@admin.display(description="Profile Type")
133+
def profile_type(self, obj):
134+
if obj._git_profile_count > 0:
135+
return "Git"
136+
if obj._github_profile_count > 0:
137+
return "GitHub"
138+
if obj._wg21_profile_count > 0:
139+
return "WG21"
140+
return "—"
141+
142+
143+
@admin.register(GithubProfile)
144+
class GithubProfileAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
145+
list_display = ["name", "github_user_id", "identity", "created"]
146+
list_filter = ["created"]
147+
search_fields = ["name", "identity__name"]
148+
autocomplete_fields = ["identity"]
149+
ordering = ["-created"]
150+
inlines = [GithubContributionInline]
151+
152+
153+
@admin.register(GitProfile)
154+
class GitProfileAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
155+
list_display = ["name", "email", "identity", "created"]
156+
list_filter = ["created"]
157+
search_fields = ["name", "email__email", "identity__name"]
158+
autocomplete_fields = ["email", "identity"]
159+
ordering = ["-created"]
160+
inlines = [GitContributionInline]
161+
162+
163+
@admin.register(Wg21Profile)
164+
class Wg21ProfileAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
165+
list_display = ["name", "identity", "created"]
166+
list_filter = ["created"]
167+
search_fields = ["name", "identity__name"]
168+
autocomplete_fields = ["identity"]
169+
ordering = ["-created"]
170+
inlines = [Wg21ContributionInline]
171+
172+
173+
@admin.register(GithubContribution)
174+
class GithubContributionAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
175+
list_display = ["profile", "type", "repo", "contributed_at", "info", "created"]
176+
list_filter = ["type", "contributed_at", "created"]
177+
search_fields = ["profile__name", "repo", "comment", "info"]
178+
autocomplete_fields = ["profile"]
179+
ordering = ["-contributed_at"]
180+
date_hierarchy = "contributed_at"
181+
182+
183+
@admin.register(Wg21Contribution)
184+
class Wg21ContributionAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
185+
list_display = ["profile", "info", "comment", "contributed_at", "created"]
186+
list_filter = ["contributed_at", "created"]
187+
search_fields = ["profile__name", "info", "comment"]
188+
autocomplete_fields = ["profile"]
189+
ordering = ["-contributed_at"]
190+
191+
192+
@admin.register(GitContribution)
193+
class GitContributionAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
194+
list_display = ["profile", "repo", "info", "contributed_at", "created"]
195+
list_filter = ["contributed_at", "created", "repo"]
196+
search_fields = [
197+
"profile__name",
198+
"profile__email__email",
199+
"repo",
200+
"comment",
201+
"info",
202+
]
203+
autocomplete_fields = ["profile"]
204+
ordering = ["-contributed_at"]
205+
date_hierarchy = "contributed_at"

contributions/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class ContributionsConfig(AppConfig):
5+
default_auto_field = "django.db.models.AutoField"
6+
name = "contributions"

contributions/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
BULK_CREATE_BATCH_SIZE = 4000

contributions/management/__init__.py

Whitespace-only changes.

contributions/management/commands/__init__.py

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import djclick as click
2+
3+
from contributions.tasks import update_contributions
4+
5+
6+
@click.command()
7+
@click.option(
8+
"--github-token",
9+
help="Optional GitHub API token (defaults to system token)",
10+
default=None,
11+
)
12+
def command(github_token):
13+
"""Update the contributions list for all versions.
14+
15+
This command iterates through all versions and updates their contribution
16+
data from GitHub. If no token is provided, it uses the system default token.
17+
"""
18+
click.secho("Starting contributions update...", fg="green")
19+
update_contributions.delay(github_token=github_token)
20+
21+
click.secho("Contributions update queued!", fg="green")

0 commit comments

Comments
 (0)