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

GitHub Commit Tracker #171

Open
wants to merge 5 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
50 changes: 50 additions & 0 deletions portal/services/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,53 @@ def get_repository_details(client: Client, repo_url: str):

result = client.execute(query, variable_values={"owner": owner, "name": name})
return result

def get_repository_commits(client: Client, repo_url: str, since: str):
owner, name = repo_url.split("/")[-2:]
query = gql(
"""
query RepoCommits($owner: String!, $name: String!, $since: GitTimestamp!) {
repository(owner: $owner, name: $name) {
refs(refPrefix: "refs/heads/", orderBy: {direction: DESC, field: TAG_COMMIT_DATE}, first: 100) {
edges {
node {
... on Ref {
name
target {
... on Commit {
history(since: $since) {
edges {
node {
... on Commit {
commitUrl
committedDate
additions
deletions
committer {
user {
login
}
}
}
}
}
}
}
}
}
}
}
}
}
rateLimit {
limit
cost
remaining
resetAt
}
}
"""
)

result = client.execute(query, variable_values={"owner": owner, "name": name, "since": since})
return result
23 changes: 20 additions & 3 deletions portal/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from django.db.models import Manager
from django.utils import timezone
from requests import HTTPError
from django.core.cache import cache

from portal.models import Meeting
from portal.services import discord

from portal.models import Meeting, Semester
from portal.services import discord, github

@shared_task
def delete_discord_channels(channel_ids: list[str]):
Expand All @@ -32,3 +32,20 @@ def meetings_alert():
discord.send_message(os.environ["DISCORD_ALERTS_CHANNEL_ID"], {
"content": f"Meeting **{meeting}** does not have presentation slides added yet!"
})

@shared_task
def get_commits():
semester = Semester.objects.latest("start_date")
for project in semester.projects.filter(is_approved=True).all():
for repo in project.repositories.all():
commits = {}
result = github.get_repository_commits(github.client_factory(), repo.url, semester.start_date.strftime('%Y-%m-%dT%H:%M:%S.%f%z'))
for branch in result["repository"]["refs"]["edges"]:
for commit in branch["node"]["target"]["history"]["edges"]:
if (commit["node"]["committer"]["user"] != None):
commits[commit["node"]["commitUrl"]] = {
"committer": commit["node"]["committer"]["user"]["login"],
"additions": commit["node"]["additions"],
"deletions": commit["node"]["deletions"]
}
cache.set(f"repo_commits_{repo.url}", commits, 60 * 60 * 24)
58 changes: 58 additions & 0 deletions portal/templates/portal/commits/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{% extends "portal/base.html" %}
{% load portal_extras %}

{% block ogp %}
<meta property="og:title" content="Commits | RCOS IO" />
<meta property="og:type" content="website" />
<meta property="og:url" content="{{ request.build_absolute_uri }}" />
<meta property="og:description" content="Commits | Rensselaer Center for Open Source" />
<meta property="og:image" content="https://raw.githubusercontent.com/rcos/rcos-branding/master/img/lockup-red.png" />
{% endblock %}

{% block title %}
Commits | RCOS IO
{% endblock %}

{% block content %}
<section class="section">
<div class="container">
<h1 class="title">Commits</h1>

{% for url, commit in commits.items %}
{% if commit.committer == username %}
<div>
<a href="{{ url }}">{{ url }}</a>
<span class="has-text-success-dark">+{{ commit.additions }}</span>
<span class="has-text-danger">-{{ commit.deletions }}</span>
</div>
{% endif %}
{% endfor %}
{% if commits.items|length > 0 %}
<br />
<button id="copy-commits" onclick="CopyCommits()" class="button">Copy</button>
{% else %}
<div>No Commits Available.</div>
{% endif %}
</div>
</section>

{{ commits|json_script:"commits" }}
<script data-username="{{ username }}">
const data = document.currentScript.dataset;
const username = data.username;
const commits = JSON.parse(
document.getElementById('commits').textContent
);
function CopyCommits() {
let commits_str = ""
for (let url in commits) {
if (commits[url].committer == username) {
commits_str += url + "\n"
}
}
console.log(commits_str)
navigator.clipboard.writeText(commits_str)
}
</script>

{% endblock %}
1 change: 1 addition & 0 deletions portal/templates/portal/includes/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

{% if request.user.is_authenticated and request.user.is_approved %}
{% include "portal/includes/navbaritem.html" with view_name="small_groups_index" display="Small Groups" %}
{% include "portal/includes/navbaritem.html" with view_name="commits_index" display="Commits" %}
{% endif %}
{% include "portal/includes/navbaritem.html" with view_name="organizations_index" display="Organizations" %}
{% include "portal/includes/navbaritem.html" with view_name="handbook" display="Handbook" %}
Expand Down
7 changes: 7 additions & 0 deletions portal/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from portal.views.mentors import MentorApplicationView, mentor_applications_index
from portal.views.organizations import organizations_index
from portal.views.small_groups import SmallGroupIndexView, small_group_detail
from portal.views.commits import commits_index

from .views.auth import (
discord_flow_callback,
Expand Down Expand Up @@ -144,4 +145,10 @@
import_google_form_projects,
name="import_projects",
),
# Commits
path(
"commits/",
commits_index,
name="commits_index"
)
]
27 changes: 27 additions & 0 deletions portal/views/commits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@


from django.http import HttpRequest, HttpResponse
from django.template.response import TemplateResponse
from django.shortcuts import get_object_or_404
from django.contrib.auth.decorators import login_required
from portal.services import github
from django.core.cache import cache;
from portal.tasks import get_commits;

@login_required
def commits_index(request: HttpRequest) -> HttpResponse:
active_enrollment = request.user.get_active_enrollment()
commits = {}

if active_enrollment is not None:
repos = active_enrollment.project.repositories.all()
for repo in repos:
repo_commits = cache.get(f"repo_commits_{repo.url}")
if repo_commits:
for url in repo_commits:
commits[url] = repo_commits[url]

return TemplateResponse(request, "portal/commits/index.html", {
"commits": commits,
"username": request.user.github_username
})
14 changes: 14 additions & 0 deletions rcos_io/celery.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os

from celery import Celery
from celery.schedules import crontab
from celery.signals import worker_ready

# Set the default Django settings module for the 'celery' program.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rcos_io.settings")
Expand All @@ -15,3 +17,15 @@

# Load task modules from all registered Django apps.
app.autodiscover_tasks()

app.conf.beat_schedule = {
'get_commits': {
'task': 'portal.tasks.get_commits',
'schedule': crontab(minute=0, hour=0)
},
}

@worker_ready.connect
def startup(sender, **kwargs):
with sender.app.connection() as conn:
sender.app.send_task("portal.tasks.get_commits", connection=conn)