diff --git a/website/templates/projects/repo_detail.html b/website/templates/projects/repo_detail.html index 36c55a5f5..1a3b911c3 100644 --- a/website/templates/projects/repo_detail.html +++ b/website/templates/projects/repo_detail.html @@ -1,7 +1,9 @@ {% extends "base.html" %} {% load humanize %} {% load static %} -{% block title %}{{ repo.name }} - Repo Details{% endblock %} +{% block title %} + {{ repo.name }} - Repo Details +{% endblock title %} {% block content %} {% include "includes/sidenav.html" %} @@ -95,18 +97,26 @@

{{ repo.name }}

Standard Repository {% endif %} - {% if repo.tags.all %} -
- Tags: - {% for tag in repo.tags.all %} - {{ tag.name }} - {% endfor %} +
+ Tags: + {% for tag in repo.tags.all %} + {{ tag.name }} + {% endfor %} +
+
- {% endif %} +
Created {{ repo.created|date:"M d, Y" }}
-
Updated {{ repo.last_updated|naturaltime }}
+
Updated {{ repo.last_updated|naturaltime }}
@@ -120,7 +130,7 @@

{{ repo.name }}

-

{{ repo.stars|intcomma }}

+

{{ repo.stars|intcomma }}

@@ -129,7 +139,7 @@

{{ repo.name }}

-

{{ repo.forks|intcomma }}

+

{{ repo.forks|intcomma }}

@@ -141,7 +151,7 @@

{{ repo.name }}

-

{{ repo.watchers|intcomma }}

+

{{ repo.watchers|intcomma }}

@@ -152,7 +162,7 @@

{{ repo.name }}

-

{{ repo.network_count|intcomma }}

+

{{ repo.network_count|intcomma }}

@@ -163,7 +173,7 @@

{{ repo.name }}

-

{{ repo.subscribers_count|intcomma }}

+

{{ repo.subscribers_count|intcomma }}

@@ -174,14 +184,26 @@

{{ repo.name }}

-

- - - - Activity Metrics -

+
+

+ + + + Activity Metrics +

+
+ +
+
@@ -202,17 +224,17 @@

Issues

Open - {{ repo.open_issues|intcomma }} + {{ repo.open_issues|intcomma }}
Closed - {{ repo.closed_issues|intcomma }} + {{ repo.closed_issues|intcomma }}
Total - {{ repo.total_issues|intcomma }} + {{ repo.total_issues|intcomma }}
@@ -235,7 +257,10 @@

Pull Requests

-
{{ repo.open_pull_requests|intcomma }}
+
+ {{ repo.open_pull_requests|intcomma }} +
Open PRs
@@ -259,7 +284,7 @@

Commits

-
{{ repo.commit_count|intcomma }}
+
{{ repo.commit_count|intcomma }}
Total Commits
@@ -268,7 +293,8 @@

Commits

viewBox="0 0 20 20"> - Last: {{ repo.last_commit_date|date:"M d, Y" }} + Last: + {{ repo.last_commit_date|date:"M d, Y" }}
@@ -279,14 +305,24 @@

Commits

-

- - - - Community -

+
+

+ + + + Community +

+ +
@@ -357,14 +393,24 @@

Top Contributors

-

- - - - Technical Overview -

+
+

+ + + + Technical Overview +

+ +
@@ -524,4 +570,128 @@

-{% endblock %} + +{% endblock content %} +{% block after_js %} + +{% endblock after_js %} diff --git a/website/views/project.py b/website/views/project.py index d9be1b995..9b5682185 100644 --- a/website/views/project.py +++ b/website/views/project.py @@ -933,3 +933,226 @@ def get_context_data(self, **kwargs): ] return context + + def post(self, request, *args, **kwargs): + def get_issue_count(full_name, query, headers): + search_url = f"https://api.github.com/search/issues?q=repo:{full_name}+{query}" + resp = requests.get(search_url, headers=headers) + if resp.status_code == 200: + return resp.json().get("total_count", 0) + return 0 + + repo = self.get_object() + section = request.POST.get("section") + + if section == "basic": + try: + # Get GitHub API token + github_token = getattr(settings, "GITHUB_TOKEN", None) + if not github_token: + return JsonResponse( + {"status": "error", "message": "GitHub token not configured"}, status=500 + ) + + # Extract owner/repo from GitHub URL + match = re.match(r"https://github.com/([^/]+)/([^/]+)/?", repo.repo_url) + if not match: + return JsonResponse( + {"status": "error", "message": "Invalid repository URL"}, status=400 + ) + + owner, repo_name = match.groups() + api_url = f"https://api.github.com/repos/{owner}/{repo_name}" + + # Make GitHub API request + headers = { + "Authorization": f"token {github_token}", + "Accept": "application/vnd.github.v3+json", + } + response = requests.get(api_url, headers=headers) + + if response.status_code == 200: + data = response.json() + + # Update repo with fresh data + repo.stars = data.get("stargazers_count", 0) + repo.forks = data.get("forks_count", 0) + repo.watchers = data.get("watchers_count", 0) + repo.open_issues = data.get("open_issues_count", 0) + repo.network_count = data.get("network_count", 0) + repo.subscribers_count = data.get("subscribers_count", 0) + repo.last_updated = parse_datetime(data.get("updated_at")) + repo.save() + + return JsonResponse( + { + "status": "success", + "message": "Basic information updated successfully", + "data": { + "stars": repo.stars, + "forks": repo.forks, + "watchers": repo.watchers, + "network_count": repo.network_count, + "subscribers_count": repo.subscribers_count, + "last_updated": naturaltime(repo.last_updated).replace( + "\xa0", " " + ), # Fix unicode space + }, + } + ) + else: + return JsonResponse( + {"status": "error", "message": f"GitHub API error: {response.status_code}"}, + status=response.status_code, + ) + + except requests.RequestException as e: + return JsonResponse( + { + "status": "error", + "message": "Network error: A network error occurred. Please try again later.", + }, + status=503, + ) + except requests.HTTPError as e: + return JsonResponse( + { + "status": "error", + "message": "A GitHub API error occurred. Please try again later.", + }, + status=e.response.status_code, + ) + except ValueError as e: + return JsonResponse( + {"status": "error", "message": "There was an error processing your data."}, + status=400, + ) + + elif section == "metrics": + try: + github_token = getattr(settings, "GITHUB_TOKEN", None) + if not github_token: + return JsonResponse( + {"status": "error", "message": "GitHub token not configured"}, status=500 + ) + + match = re.match(r"https://github.com/([^/]+)/([^/]+)/?", repo.repo_url) + if not match: + return JsonResponse( + {"status": "error", "message": "Invalid repository URL"}, status=400 + ) + + # Extract owner and repo from API call + owner, repo_name = match.groups() + api_url = f"https://api.github.com/repos/{owner}/{repo_name}" + headers = { + "Authorization": f"token {github_token}", + "Accept": "application/vnd.github.v3+json", + } + response = requests.get(api_url, headers=headers) + + if response.status_code != 200: + return JsonResponse( + {"status": "error", "message": "Failed to fetch repository data"}, + status=500, + ) + + repo_data = response.json() + full_name = repo_data.get("full_name") + default_branch = repo_data.get("default_branch") + if not full_name: + return JsonResponse( + {"status": "error", "message": "Could not get repository full name"}, + status=500, + ) + + full_name = full_name.replace(" ", "+") + + # get the total commit + url = f"https://api.github.com/repos/{full_name}/commits" + params = {"per_page": 1, "page": 1} + response = requests.get(url, headers=headers, params=params) + if response.status_code == 200: + if "Link" in response.headers: + links = response.headers["Link"] + last_page = 1 + for link in links.split(","): + if 'rel="last"' in link: + last_page = int(link.split("&page=")[1].split(">")[0]) + commit_count = last_page + else: + commits = response.json() + total_commits = len(commits) + commit_count = total_commits + else: + commit_count = 0 + + # Get open issues and PRs + open_issues = get_issue_count(full_name, "type:issue+state:open", headers) + closed_issues = get_issue_count(full_name, "type:issue+state:closed", headers) + open_pull_requests = get_issue_count(full_name, "type:pr+state:open", headers) + total_issues = open_issues + closed_issues + + if ( + repo.open_issues != open_issues + or repo.closed_issues != closed_issues + or repo.total_issues != total_issues + or repo.open_pull_requests != open_pull_requests + or repo.commit_count != commit_count + ): + # Update repository metrics + repo.open_issues = open_issues + repo.closed_issues = closed_issues + repo.total_issues = total_issues + repo.open_pull_requests = open_pull_requests + repo.commit_count = commit_count + + commits_url = f"{api_url}/commits?sha={default_branch}&per_page=1" + commits_response = requests.get(commits_url, headers=headers) + if commits_response.status_code == 200: + commit_data = commits_response.json() + if commit_data: + date_str = commit_data[0]["commit"]["committer"]["date"] + repo.last_commit_date = parse_datetime(date_str) + repo.save() + + return JsonResponse( + { + "status": "success", + "message": "Activity metrics updated successfully", + "data": { + "open_issues": repo.open_issues, + "closed_issues": repo.closed_issues, + "total_issues": repo.total_issues, + "open_pull_requests": repo.open_pull_requests, + "commit_count": repo.commit_count, + "last_commit_date": repo.last_commit_date.strftime("%b %d, %Y") + if repo.last_commit_date + else "", + }, + } + ) + + except requests.RequestException as e: + return JsonResponse( + { + "status": "error", + "message": "Network error: A network error occurred. Please try again later.", + }, + status=503, + ) + except requests.HTTPError as e: + return JsonResponse( + { + "status": "error", + "message": "A GitHub API error occurred. Please try again later.", + }, + status=e.response.status_code, + ) + except ValueError as e: + return JsonResponse( + {"status": "error", "message": "There was an error processing your data."}, + status=400, + ) + + return super().post(request, *args, **kwargs)