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

Edit achievements #437

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions auth/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ def setUp(self):
"""Create a test client."""
self.client: Client = Client()

@patch('contributors.utils.github_lib.get_access_token', lambda *args: None) # noqa: E501
@patch('contributors.utils.github_lib.get_data_of_token_holder', lambda *args: None) # noqa: E501
@patch('contributors.utils.github_lib.get_access_token', lambda *args: None)
@patch('contributors.utils.github_lib.get_data_of_token_holder', lambda *args: None)
@patch('auth.backends.GitHubBackend.authenticate', lambda *args: None)
def test_github_auth_view(self):
"""Send a request without authentication and check the response."""
Expand Down
2 changes: 1 addition & 1 deletion contributors/management/commands/fetchdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def handle( # noqa: C901,WPS110,WPS213,WPS231,WPS210
if repo['name'] not in IGNORED_REPOSITORIES
]
number_of_repos = len(repos_to_process)
for i, repo_data in enumerate(repos_to_process, start=1): # noqa: WPS111,E501
for i, repo_data in enumerate(repos_to_process, start=1): # noqa: WPS111
repo, _ = misc.update_or_create_record(Repository, repo_data)
logger.info(f"{repo} ({i}/{number_of_repos})")
if repo_data['size'] == 0:
Expand Down
4 changes: 2 additions & 2 deletions contributors/models/contribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ class Contribution(models.Model):
on_delete=models.CASCADE,
verbose_name=_("contributor"),
)
id = models.CharField(primary_key=True, max_length=ID_LENGTH) # noqa: A003,WPS125,E501
type = models.CharField(_("type"), choices=TYPES, max_length=TYPE_LENGTH) # noqa: A003,WPS125,E501
id = models.CharField(primary_key=True, max_length=ID_LENGTH) # noqa: A003,WPS125
type = models.CharField(_("type"), choices=TYPES, max_length=TYPE_LENGTH) # noqa: A003,WPS125
html_url = models.URLField(_("URL"))
created_at = models.DateTimeField(_("creation date"))

Expand Down
10 changes: 10 additions & 0 deletions contributors/templatetags/contrib_extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,13 @@ def get_canonical_url(context):
if request:
return request.build_absolute_uri(request.path)
return ''


@register.simple_tag
def calc_percent_achievement(numerator, denominator):
"""Get contributor statistics and required quantity."""
if numerator is not None:
if numerator / denominator > 1:
return 100.0
return numerator / denominator * 100
return 0
7 changes: 7 additions & 0 deletions contributors/tests/test_contributors_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
EXPECTED_CONTRIBUTORS_ISSUE_COUNT = 2
EXPECTED_CONTRIBUTORS_PR_COUNT = 2

TEST_CONTRIBUTORS = ["mintough57", "kinganduld", "indecing", "pilly1964", "suir1948"]


class TestContributorDetailView(TestCase):
"""Test the methods for the contributor's details view."""
Expand Down Expand Up @@ -149,3 +151,8 @@ def test_get_context_data(self):
self.assertEqual(response.context['contributors_issues_gte_1'], 2)
self.assertEqual(response.context['contributors_comments_gte_1'], 0)
self.assertEqual(response.context['contributors_editions_gte_1'], 0)

def test_get_context_data_contributor(self):
for contributor in TEST_CONTRIBUTORS:
response = self.client.get(reverse('contributors:contributor_achievements', args=[contributor]))
self.assertEqual(response.status_code, HTTPStatus.OK)
5 changes: 5 additions & 0 deletions contributors/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@
views.achievements.AchievementListView.as_view(),
name='achievements',
),
path(
'contributor_achievements/<slug:slug>',
views.contributor_achievements.ContributorAchievementListView.as_view(),
name='contributor_achievements',
),
path(
'landing/',
views.landing.LandingView.as_view(),
Expand Down
4 changes: 2 additions & 2 deletions contributors/utils/github_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.conf import settings

GITHUB_API_URL = 'https://api.github.com'
GITHUB_TOKEN_PROVIDER_URL = 'https://github.com/login/oauth/access_token' # noqa: E501,S105
GITHUB_TOKEN_PROVIDER_URL = 'https://github.com/login/oauth/access_token' # noqa: S105


def merge_dicts(*dicts):
Expand Down Expand Up @@ -402,7 +402,7 @@ def get_commit_stats_for_contributor(repo_full_name, contributor_id):
return totals['c'], totals['a'], totals['d']


def get_data_of_owners_and_repos(*, owner_names=None, repo_full_names=None): # noqa: C901,R701,E501,WPS231
def get_data_of_owners_and_repos(*, owner_names=None, repo_full_names=None): # noqa: C901,R701,WPS231
"""Return data of owners and their repositories from GitHub."""
if not (owner_names or repo_full_names):
raise ValueError("Neither owner_names nor repo_full_names is provided")
Expand Down
1 change: 1 addition & 0 deletions contributors/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
about,
achievements,
config,
contributor_achievements,
contributor_compare,
filters,
home,
Expand Down
2 changes: 1 addition & 1 deletion contributors/views/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def collect_data(request):
repo.is_visible = True
Repository.objects.bulk_update(repos, ['is_tracked', 'is_visible'])
fetch_command = ['./manage.py', 'fetchdata', '--repo']
fetch_command.extend([repo.full_name for repo in repos]) # noqa: WPS441,E501
fetch_command.extend([repo.full_name for repo in repos]) # noqa: WPS441
subprocess.Popen(fetch_command) # noqa: S603
return TemplateResponse(request, 'admin/data_collection.html', context)
return HttpResponseForbidden("Forbidden.")
217 changes: 217 additions & 0 deletions contributors/views/contributor_achievements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
from django.db import models
from django.db.models.functions import Coalesce
from django.views import generic

from contributors.models import Contributor, Repository

ID = 'id'


class ContributorAchievementListView(generic.ListView):
"""Achievement list."""

template_name = 'contributor/contributor_achievements_list.html'
model = Contributor
contributors = Contributor.objects.with_contributions()

pull_request_ranges_for_achievements = [1, 10, 25, 50, 100]
commit_ranges_for_achievements = [1, 25, 50, 100, 200]
issue_ranges_for_achievements = [1, 5, 10, 25, 50]
comment_ranges_for_achievements = [1, 25, 50, 100, 200]
edition_ranges_for_achievements = [1, 100, 250, 500, 1000]

def get_context_data(self, **kwargs):
"""Add context data for achievement list."""
self.contributors_amount = Contributor.objects.count()
context = super().get_context_data(**kwargs)
contributors = Contributor.objects.with_contributions()
current_contributor = (
Contributor.objects.get(login=self.kwargs['slug'])
)

repositories = Repository.objects.select_related(
'organization',
).filter(
is_visible=True,
contribution__contributor=current_contributor,
).annotate(
commits=models.Count('id', filter=models.Q(contribution__type='cit')),
additions=Coalesce(models.Sum('contribution__stats__additions'), 0),
deletions=Coalesce(models.Sum('contribution__stats__deletions'), 0),
pull_requests=models.Count(
'contribution', filter=models.Q(contribution__type='pr'),
),
issues=models.Count('contribution', filter=models.Q(contribution__type='iss')),
comments=models.Count('contribution', filter=models.Q(contribution__type='cnt')),
).order_by('organization', 'name')

contributions = repositories.values().aggregate(
contributor_deletions=models.Sum('deletions'),
contributor_additions=models.Sum('additions'),
contributor_commits=models.Sum('commits'),
contributor_pull_requests=models.Sum('pull_requests'),
contributor_issues=models.Sum('issues'),
contributor_comments=models.Sum('comments'),
)

finished = []
unfinished = []

context['commits'] = contributions['contributor_commits']
context['pull_requests'] = contributions['contributor_pull_requests']
context['issues'] = contributions['contributor_issues']
context['comments'] = contributions['contributor_comments']
editions = sum([
0 if edit is None else edit
for edit in [
contributions['contributor_additions'],
contributions['contributor_deletions'],
]
])
context['total_editions'] = editions

context['total_actions'] = sum([
0 if action is None else action
for action in [
contributions['contributor_commits'],
contributions['contributor_pull_requests'],
contributions['contributor_issues'],
contributions['contributor_comments'],
contributions['contributor_additions'],
contributions['contributor_deletions'],
]
])

context['pull_request_ranges_for_achievements'] = (
self.pull_request_ranges_for_achievements
)
context['current_contributor'] = current_contributor
context['contributors_amount'] = self.contributors_amount
context['contributors_with_any_contribution'] = {
'stat': (
contributors.filter(contribution_amount__gte=1).count()
),
'acomplished': True,
}

# Pull request achievements:
for pr_num in self.pull_request_ranges_for_achievements:
context[f'contributors_pull_requests_gte_{pr_num}'] = {
'stat': (
contributors.filter(pull_requests__gte=pr_num).count()
),
'acomplished': True,
}
a_data = {
'img': f'images/achievments_icons/pull_requests-{pr_num}.svg',
'name': f'Pull requests (equal to or more than {pr_num})',
'description': f"Make pull requests in amount of equal to or more than {pr_num}",
'accomplished': 'yes',
}
if pr_num > (
0 if contributions['contributor_pull_requests'] is None else contributions['contributor_pull_requests']
):
unfinished.append(a_data)
context[f'contributors_pull_requests_gte_{pr_num}']['acomplished'] = False
else:
finished.append(a_data)

# Commit achievements:
for commit_num in self.commit_ranges_for_achievements:
context[f'contributors_commits_gte_{commit_num}'] = {
'stat': (
contributors.filter(commits__gte=commit_num).count()
),
'acomplished': True,
}
a_data = {
'img': f'images/achievments_icons/commits-{commit_num}.svg',
'name': f'Commits (equal to or more than {commit_num})',
'description': f"Make commits in amount of equal to or more than {commit_num}",
}
if commit_num > (
0 if contributions['contributor_commits'] is None else contributions['contributor_commits']
):
unfinished.append(a_data)
context[f'contributors_commits_gte_{commit_num}']['acomplished'] = False
else:
finished.append(a_data)

# Issue achievements:
for issue_num in self.issue_ranges_for_achievements:
context[f'contributors_issues_gte_{issue_num}'] = {
'stat': (
contributors.filter(issues__gte=issue_num).count()
),
'acomplished': True,
}
a_data = {
'img': f'images/achievments_icons/issues-{issue_num}.svg',
'name': f'Issues (equal to or more than {issue_num})',
'description': f"Make issues in amount of equal to or more than {issue_num}",
}
if issue_num > (
0 if contributions['contributor_issues'] is None else contributions['contributor_issues']
):
unfinished.append(a_data)
context[f'contributors_issues_gte_{issue_num}']['acomplished'] = False
else:
finished.append(a_data)

# Comment achievements:
for comment_num in self.comment_ranges_for_achievements:
context[f'contributors_comments_gte_{comment_num}'] = {
'stat': (
contributors.filter(comments__gte=comment_num).count()
),
'acomplished': True,
}
a_data = {
'img': f'images/achievments_icons/comments-{comment_num}.svg',
'name': f'Comments (equal to or more than {comment_num})',
'description': f"Make comments in amount of equal to or more than {comment_num}",
}
if comment_num > (
0 if contributions['contributor_comments'] is None else contributions['contributor_comments']
):
unfinished.append(a_data)
context[f'contributors_comments_gte_{comment_num}']['acomplished'] = False
else:
finished.append(a_data)

# Edition achievements:
for ed_num in self.edition_ranges_for_achievements:
context[f'contributors_editions_gte_{ed_num}'] = {
'stat': (
contributors.filter(editions__gte=ed_num).count()
),
'acomplished': True,
}
a_data = {
'img': f'images/achievments_icons/code_editions-{ed_num}.svg',
'name': f'Additions and deletions (equal to or more than {ed_num})',
'description': f"Make additions and deletions in amount of equal to or more than {ed_num}",
}

if ed_num > editions:
unfinished.append(a_data)
context[f'contributors_editions_gte_{ed_num}']['acomplished'] = False
else:
finished.append(a_data)

a_data = {
'img': 'images/achievments_icons/friend.svg',
'name': 'Hexlet friend',
'description': "Make any contribution to Hexlet projects",
}
if finished:
finished.insert(0, a_data)
else:
unfinished.insert(0, a_data)
context['contributors_with_any_contribution']['acomplished'] = False

context['finished'] = finished
context['unfinished'] = unfinished
context['closed'] = len(finished)
context['all_achievements'] = len(finished) + len(unfinished)
return context
6 changes: 3 additions & 3 deletions contributors/views/contributor_compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class CompareWithYourselfView(ListView):
"""View of comparing current user with another one."""

model = Contribution
template_name = 'contributors_sections/contributors/contributor_compare_with_yourself.html' # noqa: E501
template_name = 'contributors_sections/contributors/contributor_compare_with_yourself.html'
slug_field = 'contributor'

def get_queryset(self):
Expand Down Expand Up @@ -54,7 +54,7 @@ def get_context_data(self, **kwargs):
).first().full_name
context['me_top_repo'] = me_repo_full_name
else:
context['me_top_repo'] = '---' # noqa: E501
context['me_top_repo'] = '---'

enemy_qs = context['filter'].qs.filter(
contributor=context['enemy_obj'].pk,
Expand All @@ -75,5 +75,5 @@ def get_context_data(self, **kwargs):
).first().full_name
context['enemy_top_repo'] = enemy_repo_full_name
else:
context['enemy_top_repo'] = '---' # noqa: E501
context['enemy_top_repo'] = '---'
return context
2 changes: 1 addition & 1 deletion contributors/views/contributors_views/contributor.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def get_context_data(self, **kwargs):
self.object.contribution_set.for_year()
)
context['top_repository'] = repositories.annotate(
summary=F('commits') + F('pull_requests') + F('issues') + F('comments'), # noqa: WPS221, E501
summary=F('commits') + F('pull_requests') + F('issues') + F('comments'), # noqa: WPS221
).order_by('-summary').first()

context['summary'] = Contribution.objects.filter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
class ListView(contributors.ListView):
"""A list of contributors with monthly contributions."""

template_name = 'contributors_sections/contributors/contributors_for_period.html' # noqa: E501
template_name = 'contributors_sections/contributors/contributors_for_period.html'
context_object_name = 'contributors_list'

def get_context_data(self, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion contributors/views/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class DetailTablePeriodFilter(django_filters.FilterSet):
field_name='period_filter',
)

def get_contributions_by_period(self, queryset, name, value): # noqa: WPS110, E501
def get_contributions_by_period(self, queryset, name, value): # noqa: WPS110
"""Contributions filter for a period."""
if value == 'for_year':
datetime_now = timezone.now()
Expand Down
2 changes: 1 addition & 1 deletion contributors/views/generic_list_views/pull_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ListView(TableSortSearchAndPaginationMixin, generic.ListView):
)
ordering = sortable_fields[0]

template_name = 'contributors_sections/pull_requests/pull_requests_list.html' # noqa: E501
template_name = 'contributors_sections/pull_requests/pull_requests_list.html'

def get_queryset(self): # noqa: WPS615
"""Get pull requests.
Expand Down
2 changes: 1 addition & 1 deletion contributors/views/organizations_views/organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class ListView(TableSortSearchAndPaginationMixin, generic.ListView):
queryset = Organization.objects.filter(
repository__is_visible=True,
).distinct().annotate(repository_count=Count('repository'))
template_name = 'contributors_sections/organizations/organizations_list.html' # noqa: E501
template_name = 'contributors_sections/organizations/organizations_list.html'
sortable_fields = (
'name',
('repository_count', _("Repositories")),
Expand Down
Loading