Skip to content

Commit d1bebb3

Browse files
Add button to download health stats PDF (#1756)
* Add API endpoint * Add frontend button and fetch method * Fix spelling * Add metrics route * Update tests * Remove old urls.py * Update code * Dynamically generated file * Update code * Update tests * Update with views Signed-off-by: Ahmed Gouda <[email protected]> * Add require GET * Update REST * Update code * Apply suggestions * Update code --------- Signed-off-by: Ahmed Gouda <[email protected]> Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: Arkadii Yakovets <[email protected]>
1 parent a15ab00 commit d1bebb3

File tree

14 files changed

+250
-149
lines changed

14 files changed

+250
-149
lines changed

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ Before contributing, ensure you have the following installed:
3939

4040
#### `NEXT_PUBLIC_API_URL`
4141

42-
- **Description**: The base URL for the application's REST API.
43-
- **Example Value**: `https://nest.owasp.org/api/`
44-
- **Usage**: Used by frontend components to make REST API calls.
42+
- **Description**: The base URL for the application's internal API.
43+
- **Example Value**: `https://nest.owasp.org/`
44+
- **Usage**: Used by frontend components to make API calls.
4545

4646
#### `NEXT_PUBLIC_CSRF_URL`
4747

backend/apps/owasp/api/internal/views/__init__.py

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""Views for OWASP project health metrics."""
2+
3+
from django.http import FileResponse
4+
from django.views.decorators.http import require_GET
5+
6+
from apps.owasp.utils.pdf import generate_metrics_overview_pdf
7+
8+
9+
@require_GET
10+
def generate_overview_pdf(_request):
11+
"""Generate a PDF overview of OWASP project health metrics."""
12+
return FileResponse(
13+
generate_metrics_overview_pdf(),
14+
as_attachment=True,
15+
filename="owasp_project_health_metrics_overview.pdf",
16+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""URLs for OWASP project health metrics."""
2+
3+
from django.urls import path
4+
5+
from apps.owasp.api.internal.views.project_health_metrics import generate_overview_pdf
6+
7+
urlpatterns = [
8+
path(
9+
"project-health-metrics/overview/pdf/",
10+
generate_overview_pdf,
11+
name="project_health_metrics_overview_pdf",
12+
),
13+
]

backend/apps/owasp/management/commands/owasp_generate_project_health_metrics_overview_pdf.py

Lines changed: 0 additions & 79 deletions
This file was deleted.

backend/apps/owasp/utils/__init__.py

Whitespace-only changes.

backend/apps/owasp/utils/pdf.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""PDF generation for OWASP project health metrics."""
2+
3+
from io import BytesIO
4+
5+
from django.utils import timezone
6+
from reportlab.pdfgen.canvas import Canvas
7+
from reportlab.platypus import Table, TableStyle
8+
9+
from apps.owasp.models.project_health_metrics import ProjectHealthMetrics
10+
11+
12+
def generate_metrics_overview_pdf() -> BytesIO:
13+
"""Generate a PDF overview of project health metrics.
14+
15+
Returns:
16+
BytesIO: PDF content as bytes.
17+
18+
"""
19+
metrics_stats = ProjectHealthMetrics.get_stats()
20+
21+
buffer = BytesIO()
22+
canvas = Canvas(buffer)
23+
canvas.setFont("Helvetica", 12)
24+
canvas.setTitle("OWASP Project Health Metrics Overview")
25+
canvas.drawCentredString(300, 800, "OWASP Project Health Metrics Overview")
26+
27+
table_data = (
28+
("Metric", "Value"),
29+
("Healthy Projects", f"{metrics_stats.projects_count_healthy}"),
30+
("Unhealthy Projects", f"{metrics_stats.projects_count_unhealthy}"),
31+
("Need Attention Projects", f"{metrics_stats.projects_count_need_attention}"),
32+
(
33+
"Average Score",
34+
f"{metrics_stats.average_score:.2f}"
35+
if metrics_stats.average_score is not None
36+
else "N/A",
37+
),
38+
("Total Contributors", f"{metrics_stats.total_contributors:,}"),
39+
("Total Forks", f"{metrics_stats.total_forks:,}"),
40+
("Total Stars", f"{metrics_stats.total_stars:,}"),
41+
(
42+
"Healthy Projects Percentage",
43+
f"{metrics_stats.projects_percentage_healthy:.2f}%",
44+
),
45+
(
46+
"Need Attention Projects Percentage",
47+
f"{metrics_stats.projects_percentage_need_attention:.2f}%",
48+
),
49+
(
50+
"Unhealthy Projects Percentage",
51+
f"{metrics_stats.projects_percentage_unhealthy:.2f}%",
52+
),
53+
)
54+
55+
table = Table(
56+
table_data,
57+
colWidths="*",
58+
style=TableStyle(
59+
(
60+
("BACKGROUND", (0, 0), (-1, 0), "lightgrey"),
61+
("TEXTCOLOR", (0, 0), (-1, 0), "black"),
62+
("ALIGN", (0, 0), (-1, -1), "CENTER"),
63+
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
64+
("BOTTOMPADDING", (0, 0), (-1, 0), 5),
65+
("BACKGROUND", (0, 1), (-1, -1), "white"),
66+
)
67+
),
68+
)
69+
table.wrapOn(canvas, 400, 600)
70+
table.drawOn(canvas, 100, 570)
71+
canvas.drawCentredString(
72+
300, 100, f"Generated on: {timezone.now().strftime('%Y-%m-%d %H:%M:%S')}"
73+
)
74+
canvas.showPage()
75+
canvas.save()
76+
buffer.seek(0)
77+
return buffer

backend/settings/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from apps.core.api.internal.algolia import algolia_search
1515
from apps.core.api.internal.csrf import get_csrf_token
1616
from apps.core.api.internal.status import get_status
17+
from apps.owasp.api.internal.views.urls import urlpatterns as owasp_urls
1718
from apps.slack.apps import SlackConfig
1819
from settings.api.v1 import api as api_v1
1920
from settings.graphql import schema
@@ -24,6 +25,7 @@
2425
path("graphql/", csrf_protect(GraphQLView.as_view(schema=schema, graphiql=settings.DEBUG))),
2526
path("api/v1/", api_v1.urls),
2627
path("a/", admin.site.urls),
28+
path("owasp/", include(owasp_urls)),
2729
path("status/", get_status),
2830
path("", include("apps.sitemap.urls")),
2931
]

backend/tests/apps/owasp/management/commands/owasp_generate_project_health_metrics_overview_pdf_test.py

Lines changed: 0 additions & 64 deletions
This file was deleted.

backend/tests/apps/owasp/utils/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)