From 4b20d6a48c1088ebb29c0b5b80146c57440a95bc Mon Sep 17 00:00:00 2001 From: JisanAR Date: Tue, 31 Dec 2024 04:57:57 +0600 Subject: [PATCH] Add project detail view and update project list template for navigation --- blt/urls.py | 2 + .../templates/projects/project_detail.html | 310 ++++++++++++++++++ website/templates/projects/project_list.html | 30 +- website/views/project.py | 71 +++- 4 files changed, 398 insertions(+), 15 deletions(-) create mode 100644 website/templates/projects/project_detail.html diff --git a/blt/urls.py b/blt/urls.py index b9f0efa19..bd7306675 100644 --- a/blt/urls.py +++ b/blt/urls.py @@ -179,6 +179,7 @@ ProjectBadgeView, ProjectDetailView, ProjectListView, + ProjectsDetailView, ProjectView, blt_tomato, create_project, @@ -799,6 +800,7 @@ name="similarity_scan", ), path("projects/create/", create_project, name="create_project"), + path("projects//", ProjectsDetailView.as_view(), name="projects_detail"), ] if settings.DEBUG: diff --git a/website/templates/projects/project_detail.html b/website/templates/projects/project_detail.html new file mode 100644 index 000000000..8129a6ac1 --- /dev/null +++ b/website/templates/projects/project_detail.html @@ -0,0 +1,310 @@ +{% extends "base.html" %} +{% load humanize %} +{% load static %} +{% block title %}{{ project.name }} - Project Details{% endblock %} +{% block content %} + {% include "includes/sidenav.html" %} +
+ + + +
+ {% if project.organization %} +
+ +
+ + + + + + + + +
+
+
+ {% if project.organization.logo %} + {{ project.organization.name }} + {% else %} +
+ {{ project.organization.name|slice:":1"|upper }} +
+ {% endif %} +
+
Organization
+

{{ project.organization.name }}

+ {% if project.organization.description %} +

{{ project.organization.description }}

+ {% endif %} +
+
+
+
+ {% else %} +
+
+
Organization
+

This is an independent project not associated with any organization

+
+
+ {% endif %} +
+ +
+
+ + {% if project.organization %} +
+ {% if project.organization.logo %} + {{ project.organization.name }} + {% endif %} + {{ project.organization.name }} +
+ {% endif %} +
+ {% if project.logo %} + {{ project.name }} + {% else %} +
+ {{ project.name|slice:":1"|upper }} +
+ {% endif %} +
+

{{ project.name }}

+

{{ project.description }}

+ +
+ {% if project.url %} + + + + + + Website + + {% endif %} + {% if project.twitter %} + + + + + Twitter + + {% endif %} + {% if project.facebook %} + + + + + Facebook + + {% endif %} +
+
+
+
+ +
+
+
+
Total Stars
+
{{ repo_metrics.total_stars|default:"0"|intcomma }}
+
+
+
Total Forks
+
{{ repo_metrics.total_forks|default:"0"|intcomma }}
+
+
+
Total Issues
+
{{ repo_metrics.total_issues|default:"0"|intcomma }}
+
+
+
Contributors
+
{{ repo_metrics.total_contributors|default:"0"|intcomma }}
+
+
+
Total Commits
+
{{ repo_metrics.total_commits|default:"0"|intcomma }}
+
+
+
Open PRs
+
{{ repo_metrics.total_prs|default:"0"|intcomma }}
+
+
+
+
+ + {% if organization and show_org_details %} +
+

Organization Details

+
+ {% if organization.logo %} + {{ organization.name }} + {% endif %} +
+

{{ organization.name }}

+ {% if organization.description %}

{{ organization.description }}

{% endif %} +
+
+
+ {% endif %} + +
+

Associated Repositories

+
+ {% for repo in repositories %} +
+
+
+

{{ repo.name }}

+ {% if repo.is_main %} + Main + {% elif repo.is_wiki %} + Wiki + {% endif %} +
+ {% if repo.description %}

{{ repo.description }}

{% endif %} + +
+
+ + {{ repo.stars|intcomma }} +
+
+ 🍴 + {{ repo.forks|intcomma }} +
+
+ 🐛 + {{ repo.open_issues|intcomma }} +
+
+ 👥 + {{ repo.contributor_count|intcomma }} +
+
+ +
+ {% if repo.primary_language %} +
+ + + + Primary Language: {{ repo.primary_language }} +
+ {% endif %} + {% if repo.license %} +
+ + + + License: {{ repo.license }} +
+ {% endif %} + {% if repo.last_commit_date %} +
+ + + + Last Commit: {{ repo.last_commit_date|date:"M d, Y" }} +
+ {% endif %} + {% if repo.release_name %} +
+ + + + Latest Release: {{ repo.release_name }} + {% if repo.release_datetime %}({{ repo.release_datetime|date:"M d, Y" }}){% endif %} +
+ {% endif %} +
+ +
+
Updated {{ repo.last_updated|naturaltime }}
+
+ {% if repo.homepage_url %} + + + + + Website + + {% endif %} + + + + + View on GitHub + +
+
+
+
+ {% empty %} +
+

No repositories found for this project.

+
+ {% endfor %} +
+
+ +
+

Project Timeline

+
+
+ + + + Created: {{ created_date.full }} ({{ created_date.relative }}) +
+
+ + + + Last Updated: {{ modified_date.full }} ({{ modified_date.relative }}) +
+
+
+
+{% endblock content %} diff --git a/website/templates/projects/project_list.html b/website/templates/projects/project_list.html index 0b585f590..11c588703 100644 --- a/website/templates/projects/project_list.html +++ b/website/templates/projects/project_list.html @@ -150,20 +150,24 @@

Filter Projects

- {% if project.logo %} - {{ project.name }} - {% else %} -
- {{ project.name|slice:":1"|upper }} -
- {% endif %} + + {% if project.logo %} + {{ project.name }} + {% else %} +
+ {{ project.name|slice:":1"|upper }} +
+ {% endif %} +
-

{{ project.name }}

-

{{ project.description|truncatechars:200 }}

+ +

{{ project.name }}

+

{{ project.description|truncatechars:200 }}

+
diff --git a/website/views/project.py b/website/views/project.py index 34355d29b..f2bb55509 100644 --- a/website/views/project.py +++ b/website/views/project.py @@ -15,15 +15,16 @@ from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required, user_passes_test +from django.contrib.humanize.templatetags.humanize import naturaltime from django.core.exceptions import ValidationError from django.core.validators import URLValidator -from django.db.models import Count, Q +from django.db.models import Count, Q, Sum from django.db.models.functions import TruncDate from django.http import HttpResponse, JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.utils.dateparse import parse_datetime from django.utils.text import slugify -from django.utils.timezone import now +from django.utils.timezone import localtime, now from django.views.decorators.http import require_http_methods from django.views.generic import DetailView, ListView from django_filters.views import FilterView @@ -763,3 +764,69 @@ def get_issue_count(full_name, query): }, status=403, ) + + +class ProjectsDetailView(DetailView): + model = Project + template_name = "projects/project_detail.html" + context_object_name = "project" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + project = self.get_object() + + # Get all repositories associated with the project + repositories = ( + Repo.objects.select_related("project") + .filter(project=project) + .prefetch_related("tags", "contributor") + ) + + # Calculate aggregate metrics + repo_metrics = repositories.aggregate( + total_stars=Sum("stars"), + total_forks=Sum("forks"), + total_issues=Sum("total_issues"), + total_contributors=Sum("contributor_count"), + total_commits=Sum("commit_count"), + total_prs=Sum("open_pull_requests"), + ) + + # Format dates for display + created_date = localtime(project.created) + modified_date = localtime(project.modified) + + # Add computed context + context.update( + { + "repositories": repositories, + "repo_metrics": repo_metrics, + "created_date": { + "full": created_date.strftime("%B %d, %Y"), + "relative": naturaltime(created_date), + }, + "modified_date": { + "full": modified_date.strftime("%B %d, %Y"), + "relative": naturaltime(modified_date), + }, + "show_org_details": self.request.user.is_authenticated + and ( + project.organization + and ( + self.request.user == project.organization.admin + or project.organization.managers.filter(id=self.request.user.id).exists() + ) + ), + } + ) + + # Add organization context if it exists + if project.organization: + context["organization"] = project.organization + + return context + + def get(self, request, *args, **kwargs): + response = super().get(request, *args, **kwargs) + + return response