-
-
Notifications
You must be signed in to change notification settings - Fork 264
Add button to download health stats PDF #1756
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
Merged
arkid15r
merged 20 commits into
OWASP:main
from
ahmedxgouda:dashboard/pdf-overview-client
Jul 30, 2025
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
cf4b4d7
Add API endpoint
ahmedxgouda 49e01b1
Add frontend button and fetch method
ahmedxgouda f899442
Fix spelling
ahmedxgouda 731ca29
Add metrics route
ahmedxgouda 2c1ac07
Update tests
ahmedxgouda 6cb5f4d
Remove old urls.py
ahmedxgouda 17979fe
Update code
ahmedxgouda c6d93d7
Merge branch 'main' into dashboard/pdf-overview-client
ahmedxgouda d50304f
Dynamically generated file
ahmedxgouda df438b4
Update code
ahmedxgouda 69e6b8d
Update tests
ahmedxgouda b096464
Update with views
ahmedxgouda bfbede1
Add require GET
ahmedxgouda b622177
Update REST
ahmedxgouda 0b669ba
Merge branch 'main' into dashboard/pdf-overview-client
ahmedxgouda 858f35c
Update code
arkid15r aefbe91
Merge branch 'main' into dashboard/pdf-overview-client
ahmedxgouda e2faead
Apply suggestions
ahmedxgouda 48f1e31
Update code
arkid15r b8b37fc
Merge branch 'main' into dashboard/pdf-overview-client
arkid15r File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
16 changes: 16 additions & 0 deletions
16
backend/apps/owasp/api/internal/views/project_health_metrics.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| """Views for OWASP project health metrics.""" | ||
|
|
||
| from django.http import FileResponse | ||
| from django.views.decorators.http import require_GET | ||
|
|
||
| from apps.owasp.utils.pdf import generate_metrics_overview_pdf | ||
|
|
||
|
|
||
| @require_GET | ||
| def generate_overview_pdf(_request): | ||
| """Generate a PDF overview of OWASP project health metrics.""" | ||
| return FileResponse( | ||
| generate_metrics_overview_pdf(), | ||
| as_attachment=True, | ||
| filename="owasp_project_health_metrics_overview.pdf", | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| """URLs for OWASP project health metrics.""" | ||
|
|
||
| from django.urls import path | ||
|
|
||
| from apps.owasp.api.internal.views.project_health_metrics import generate_overview_pdf | ||
|
|
||
| urlpatterns = [ | ||
| path( | ||
| "project-health-metrics/overview/pdf/", | ||
| generate_overview_pdf, | ||
| name="project_health_metrics_overview_pdf", | ||
| ), | ||
| ] |
79 changes: 0 additions & 79 deletions
79
backend/apps/owasp/management/commands/owasp_generate_project_health_metrics_overview_pdf.py
This file was deleted.
Oops, something went wrong.
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| """PDF generation for OWASP project health metrics.""" | ||
|
|
||
| from io import BytesIO | ||
|
|
||
| from django.utils import timezone | ||
| from reportlab.pdfgen.canvas import Canvas | ||
| from reportlab.platypus import Table, TableStyle | ||
|
|
||
| from apps.owasp.models.project_health_metrics import ProjectHealthMetrics | ||
|
|
||
|
|
||
| def generate_metrics_overview_pdf() -> BytesIO: | ||
| """Generate a PDF overview of project health metrics. | ||
|
|
||
| Returns: | ||
| BytesIO: PDF content as bytes. | ||
|
|
||
| """ | ||
| metrics_stats = ProjectHealthMetrics.get_stats() | ||
|
|
||
| buffer = BytesIO() | ||
| canvas = Canvas(buffer) | ||
| canvas.setFont("Helvetica", 12) | ||
| canvas.setTitle("OWASP Project Health Metrics Overview") | ||
| canvas.drawCentredString(300, 800, "OWASP Project Health Metrics Overview") | ||
|
|
||
| table_data = ( | ||
| ("Metric", "Value"), | ||
| ("Healthy Projects", f"{metrics_stats.projects_count_healthy}"), | ||
| ("Unhealthy Projects", f"{metrics_stats.projects_count_unhealthy}"), | ||
| ("Need Attention Projects", f"{metrics_stats.projects_count_need_attention}"), | ||
| ( | ||
| "Average Score", | ||
| f"{metrics_stats.average_score:.2f}" | ||
| if metrics_stats.average_score is not None | ||
| else "N/A", | ||
| ), | ||
| ("Total Contributors", f"{metrics_stats.total_contributors:,}"), | ||
| ("Total Forks", f"{metrics_stats.total_forks:,}"), | ||
| ("Total Stars", f"{metrics_stats.total_stars:,}"), | ||
| ( | ||
| "Healthy Projects Percentage", | ||
| f"{metrics_stats.projects_percentage_healthy:.2f}%", | ||
| ), | ||
| ( | ||
| "Need Attention Projects Percentage", | ||
| f"{metrics_stats.projects_percentage_need_attention:.2f}%", | ||
| ), | ||
| ( | ||
| "Unhealthy Projects Percentage", | ||
| f"{metrics_stats.projects_percentage_unhealthy:.2f}%", | ||
| ), | ||
| ) | ||
|
|
||
| table = Table( | ||
| table_data, | ||
| colWidths="*", | ||
| style=TableStyle( | ||
| ( | ||
| ("BACKGROUND", (0, 0), (-1, 0), "lightgrey"), | ||
| ("TEXTCOLOR", (0, 0), (-1, 0), "black"), | ||
| ("ALIGN", (0, 0), (-1, -1), "CENTER"), | ||
| ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), | ||
| ("BOTTOMPADDING", (0, 0), (-1, 0), 5), | ||
| ("BACKGROUND", (0, 1), (-1, -1), "white"), | ||
| ) | ||
| ), | ||
| ) | ||
| table.wrapOn(canvas, 400, 600) | ||
| table.drawOn(canvas, 100, 570) | ||
| canvas.drawCentredString( | ||
| 300, 100, f"Generated on: {timezone.now().strftime('%Y-%m-%d %H:%M:%S')}" | ||
| ) | ||
| canvas.showPage() | ||
| canvas.save() | ||
| buffer.seek(0) | ||
| return buffer |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 0 additions & 64 deletions
64
...apps/owasp/management/commands/owasp_generate_project_health_metrics_overview_pdf_test.py
This file was deleted.
Oops, something went wrong.
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| """Test cases for OWASP project health metrics pdf generation.""" | ||
|
|
||
| from unittest.mock import patch | ||
|
|
||
| from apps.owasp.api.internal.nodes.project_health_stats import ProjectHealthStatsNode | ||
| from apps.owasp.utils.pdf import generate_metrics_overview_pdf | ||
|
|
||
|
|
||
| class TestGenerateMetricsPDF: | ||
| @patch("apps.owasp.utils.pdf.ProjectHealthMetrics.get_stats") | ||
| @patch("apps.owasp.utils.pdf.Canvas") | ||
| @patch("apps.owasp.utils.pdf.Table") | ||
| @patch("apps.owasp.utils.pdf.TableStyle") | ||
| @patch("apps.owasp.utils.pdf.BytesIO") | ||
| def test_generate_overview_pdf( | ||
| self, | ||
| mock_bytes_io, | ||
| mock_table_style, | ||
| mock_table, | ||
| mock_canvas, | ||
| mock_get_stats, | ||
| ): | ||
| """Test that the command executes without errors.""" | ||
| metrics_stats = ProjectHealthStatsNode( | ||
| projects_count_healthy=10, | ||
| projects_count_unhealthy=5, | ||
| projects_count_need_attention=3, | ||
| average_score=75.0, | ||
| total_contributors=150, | ||
| total_forks=200, | ||
| total_stars=300, | ||
| projects_percentage_healthy=66.67, | ||
| projects_percentage_need_attention=20.00, | ||
| projects_percentage_unhealthy=13.33, | ||
| monthly_overall_scores=[], | ||
| monthly_overall_scores_months=[], | ||
| ) | ||
| table_data = ( | ||
| ("Metric", "Value"), | ||
| ("Healthy Projects", f"{metrics_stats.projects_count_healthy}"), | ||
| ("Unhealthy Projects", f"{metrics_stats.projects_count_unhealthy}"), | ||
| ("Need Attention Projects", f"{metrics_stats.projects_count_need_attention}"), | ||
| ( | ||
| "Average Score", | ||
| f"{metrics_stats.average_score:.2f}" | ||
| if metrics_stats.average_score is not None | ||
| else "N/A", | ||
| ), | ||
| ("Total Contributors", f"{metrics_stats.total_contributors:,}"), | ||
| ("Total Forks", f"{metrics_stats.total_forks:,}"), | ||
| ("Total Stars", f"{metrics_stats.total_stars:,}"), | ||
| ( | ||
| "Healthy Projects Percentage", | ||
| f"{metrics_stats.projects_percentage_healthy:.2f}%", | ||
| ), | ||
| ( | ||
| "Need Attention Projects Percentage", | ||
| f"{metrics_stats.projects_percentage_need_attention:.2f}%", | ||
| ), | ||
| ( | ||
| "Unhealthy Projects Percentage", | ||
| f"{metrics_stats.projects_percentage_unhealthy:.2f}%", | ||
| ), | ||
| ) | ||
| mock_get_stats.return_value = metrics_stats | ||
| generate_metrics_overview_pdf() | ||
| mock_bytes_io.assert_called_once() | ||
| mock_canvas.assert_called_once_with(mock_bytes_io.return_value) | ||
| canvas = mock_canvas.return_value | ||
| mock_table.assert_called_once_with( | ||
| table_data, colWidths="*", style=mock_table_style.return_value | ||
| ) | ||
| mock_table_style.assert_called_once() | ||
| mock_table.return_value.wrapOn.assert_called_once_with(canvas, 400, 600) | ||
| mock_table.return_value.drawOn.assert_called_once_with(canvas, 100, 570) | ||
| canvas.showPage.assert_called_once() | ||
| canvas.save.assert_called_once() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.