Skip to content

Implement charts for health metrics #1633

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

Open
wants to merge 42 commits into
base: main
Choose a base branch
from

Conversation

ahmedxgouda
Copy link
Collaborator

@ahmedxgouda ahmedxgouda commented Jun 18, 2025

Resolves #1622

Screencast.from.2025-06-20.19-36-54.mp4

Copy link
Contributor

coderabbitai bot commented Jun 18, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a Health Metrics section on the Project Details page, displaying trends and key repository health indicators with interactive charts.
    • Added visualizations for issues, pull requests, stars, forks, and activity timelines using new chart components.
    • Health metrics badge now appears in the project details header for quick status overview.
  • Bug Fixes

    • None.
  • Tests

    • Added unit and end-to-end tests to verify the display and accuracy of the health metrics section.
  • Chores

    • Updated dependencies to include ApexCharts and its React wrapper for enhanced charting capabilities.
    • Expanded custom dictionary for improved spell checking.

Summary by CodeRabbit

  • New Features

    • Added a comprehensive Health Metrics section to the Project Details page, featuring visualizations for issues, pull requests, stars, forks, and activity trends.
    • Introduced interactive charts for health metrics, including trend lines and radial charts for days since last commit and last release.
    • Health Metrics data now includes detailed project health indicators and requirement thresholds.
  • Bug Fixes

    • None.
  • Tests

    • Added unit and end-to-end tests to verify the display and correctness of the Health Metrics section and its visual components.
  • Chores

    • Updated dependencies to include ApexCharts and its React wrapper for chart rendering.
    • Extended dictionaries and type definitions to support new health metrics data.

Walkthrough

This change introduces a comprehensive project health visualization section to the Project page. It adds new backend fields and properties for health requirements, extends the frontend data model, integrates ApexCharts for chart rendering, implements reusable chart components, updates the UI to display health metrics and trends, and includes new unit and end-to-end tests.

Changes

Files / Grouped Files Change Summary
backend/apps/owasp/models/project_health_metrics.py, backend/apps/owasp/graphql/nodes/project_health_metrics.py Add properties and GraphQL fields for last commit/release day requirements in project health metrics.
backend/tests/apps/owasp/graphql/nodes/project_health_metrics_test.py Update tests to cover new GraphQL fields for health requirements.
frontend/package.json, cspell/custom-dict.txt Add ApexCharts and its React wrapper as dependencies; update custom dictionary for "apexcharts".
frontend/src/server/queries/projectQueries.ts Extend GraphQL query to fetch healthMetrics with detailed fields (last 30 days).
frontend/src/types/healthMetrics.ts Add new type definitions for health metrics and chart series.
frontend/src/types/card.ts, frontend/src/types/project.ts Extend type definitions to support health metrics data in project and card props.
frontend/src/components/GradientRadialChart.tsx, frontend/src/components/LineChart.tsx Introduce reusable chart components for radial and line charts using ApexCharts.
frontend/src/components/HealthMetrics.tsx Add HealthMetrics component to aggregate and display all project health charts and metrics.
frontend/src/components/CardDetailsPage.tsx, frontend/src/app/projects/[projectKey]/page.tsx Integrate HealthMetrics into the Project Details UI, passing healthMetrics data as a prop.
frontend/tests/e2e/pages/ProjectDetails.spec.ts, frontend/tests/unit/pages/ProjectDetails.test.tsx Add end-to-end and unit tests to verify health metrics section rendering and content.
frontend/tests/unit/data/mockProjectDetailsData.ts Update mock data to include healthMetrics for testing.

Assessment against linked issues

Objective (Issue #1622) Addressed Explanation
Add project health section with prominent score display
Display Issues Health Trend (open_issues_count, unassigned_issues_count, line chart)
Display Unanswered Issues Trend (open_issues_count, unanswered_issues_count, line chart)
Display Activity Trend (open_pull_requests_count, line chart)
Display Community Size (stars_count, forks_count, line charts)
Display Days Since Last Commit / Last Release (radial progress bar, color thresholds)
Use ApexCharts for rendering, ensure responsive design, reuse chart components
Data represents daily snapshots for the last 30 days

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Possibly related PRs

Suggested reviewers

  • kasya
  • arkid15r

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a32bb1 and a2233b4.

📒 Files selected for processing (2)
  • frontend/__tests__/e2e/pages/ProjectDetails.spec.ts (1 hunks)
  • frontend/__tests__/unit/pages/ProjectDetails.test.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/tests/e2e/pages/ProjectDetails.spec.ts
  • frontend/tests/unit/pages/ProjectDetails.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: CodeQL (javascript-typescript)
  • GitHub Check: CodeQL (python)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Post Copyable Unit Tests in Comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
frontend/src/types/card.ts (1)

41-42: Synchronise prop name with caller

If you adopt the earlier renaming suggestion, remember to rename the prop here:

-  healthMetricsData?: HealthMetricsProps[]
+  healthMetrics?: HealthMetricsProps[]
🧹 Nitpick comments (13)
frontend/src/app/projects/[projectKey]/page.tsx (2)

91-91: Keep TODOs actionable or create an issue

The inline TODO is easy to forget once the PR is merged. Prefer converting this into a GitHub issue (referencing the line) or tracking it in a ticket so it does not slip through the cracks.


93-99: Prop-name drift between caller and callee

You pass healthMetricsData={project.healthMetrics}, whereas the parent object uses healthMetrics and the type in Project mirrors that. Maintaining two names for the same payload (healthMetrics vs. healthMetricsData) increases cognitive load and can lead to accidental typos.

Consider normalising on a single prop/property name (healthMetrics) throughout:

-      healthMetricsData={project.healthMetrics}
+      healthMetrics={project.healthMetrics}

and update DetailsCardProps accordingly.

frontend/src/types/project.ts (1)

22-23: Make the collection read-only

Health metrics should be an immutable snapshot for the UI layer. Declare the property as a read-only tuple to prevent accidental in-place mutation by consumer components:

-  healthMetrics?: HealthMetricsProps[]
+  readonly healthMetrics?: readonly HealthMetricsProps[]

Helps TypeScript narrow immutability errors early.

backend/tests/apps/owasp/graphql/nodes/project_test.py (1)

47-51: Assertion could miss List wrapper

field.type.of_type works when type is List[ProjectHealthMetricsNode]; if the schema later changes to a non-list field, this test will silently pass. A stricter assertion:

assert getattr(field.type, "of_type", None) is ProjectHealthMetricsNode
assert getattr(field.type, "_of_type", None) is None  # guards against nested lists

or use Strawberry’s is_list_of_type.

frontend/src/components/CardDetailsPage.tsx (2)

17-17: Consider lazy-loading heavy chart bundle

HealthMetricsCharts likely pulls in ApexCharts (~100 kB). Importing it eagerly increases the initial JS payload for every details page, even those that are not projects. A simple React.lazy / dynamic import() keeps non-project pages lighter.

-import HealthMetricsCharts from 'components/HealthMetricsCharts'
+const HealthMetricsCharts = React.lazy(
+  () => import('components/HealthMetricsCharts'),
+)

Remember to wrap the usage in <Suspense> (or your app’s equivalent).


35-36: Prop should be optional in the component signature

healthMetricsData is only relevant for projects. Make it optional (healthMetricsData?: …) in DetailsCardProps to avoid forcing callers (e.g. chapter pages) to pass undefined.

backend/tests/apps/owasp/management/commands/owasp_update_project_health_metrics_scores_test.py (2)

11-11: Magic number updated – assert the calculation instead

Hard-coding EXPECTED_SCORE = 34.0 means every future tweak to the scoring formula will require manual test edits. Prefer deriving the expected score from fields_weights to keep the test self-maintaining.


76-81: Assertion covers only score field – include side-effects

You assert bulk_save call and the final score. Consider also asserting that mock_metric.updated_at (or similar timestamp) was modified if the command is expected to bump it, ensuring the full behaviour is protected.

backend/apps/owasp/migrations/0041_merge_20250613_0336.py (1)

1-13: Silence Pylint false-positive for “too few public methods”

Merge migrations are intentionally empty, but Pylint still complains.
A terse pragma keeps CI green without affecting Django:

 from django.db import migrations
 
 
 class Migration(migrations.Migration):
+    # pylint: disable=too-few-public-methods
frontend/src/components/HealthMetricsCharts.tsx (2)

14-17: Reverse the dataset to chart chronological order

ProjectNode.health_metrics returns records ordered descending (-nest_created_at).
When you map directly, Day 1 represents the most recent day and the chart runs right-to-left, which is counter-intuitive.

-const openIssuesCountArray = data.map((item) => item.openIssuesCount)
-const labels = data.map((item, index) => `Day ${index + 1}`)
+const chronological = [...data].reverse()               // oldest → newest
+const openIssuesCountArray = chronological.map((i) => i.openIssuesCount)
+const labels = chronological.map((_, idx) => `Day ${idx + 1}`)

Optionally wrap those computations in useMemo to avoid recalculation on each render.


86-99: Radial charts: one series per chart, not N series

ApexCharts’ radialBar expects series: number[], one numeric value per radial bar.
Passing an array of many values causes a multi-series radial (concentric rings) which may not be what you want for “days since last commit / release”.

If the intent is to display only the latest value, do:

-series={data.map((item) => item.lastCommitDays)}
-labels={labels}
+series={[chronological.at(-1)?.lastCommitDays ?? 0]}
+labels={['Today']}

Adjust similarly for lastReleaseDays.

backend/tests/apps/owasp/graphql/nodes/project_health_metrics_test.py (1)

36-44: Dependence on Strawberry internals is brittle

The tests reach into __strawberry_definition__, an internal implementation detail that may change across Strawberry releases.
A safer approach is to validate the schema via Strawberry’s public API or GraphQL introspection:

schema = strawberry.Schema(query=Query)
introspection = schema._schema  # or use graphql-core introspection

This keeps the test future-proof.

frontend/src/components/GeneralCharts.tsx (1)

20-28: Explicitly set the chart type to avoid silent behavioural changes

ApexCharts currently defaults to line, but being explicit guards against future default changes and improves readability.

-      <Chart
+      <Chart
+        type="line"
         options={{
           xaxis: {
             categories: labels,
           },
         }}
         series={series}
         height={450}
       />
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9c5f5f and 9fcac7b.

⛔ Files ignored due to path filters (1)
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • backend/apps/owasp/graphql/nodes/project.py (2 hunks)
  • backend/apps/owasp/graphql/nodes/project_health_metrics.py (1 hunks)
  • backend/apps/owasp/management/commands/owasp_update_project_health_metrics_scores.py (1 hunks)
  • backend/apps/owasp/migrations/0038_projecthealthmetrics_has_long_open_issues_and_more.py (1 hunks)
  • backend/apps/owasp/migrations/0041_merge_20250613_0336.py (1 hunks)
  • backend/apps/owasp/migrations/0042_remove_projecthealthmetrics_has_no_recent_commits_and_more.py (1 hunks)
  • backend/apps/owasp/migrations/0043_remove_projecthealthmetrics_has_recent_commits_and_more.py (1 hunks)
  • backend/apps/owasp/migrations/0044_merge_20250615_0346.py (1 hunks)
  • backend/apps/owasp/migrations/0045_remove_projecthealthmetrics_has_long_open_issues_and_more.py (1 hunks)
  • backend/apps/owasp/models/project_health_metrics.py (1 hunks)
  • backend/tests/apps/owasp/graphql/nodes/project_health_metrics_test.py (1 hunks)
  • backend/tests/apps/owasp/graphql/nodes/project_test.py (2 hunks)
  • backend/tests/apps/owasp/management/commands/owasp_update_project_health_metrics_scores_test.py (3 hunks)
  • cspell/custom-dict.txt (1 hunks)
  • frontend/package.json (2 hunks)
  • frontend/src/app/projects/[projectKey]/page.tsx (1 hunks)
  • frontend/src/components/CardDetailsPage.tsx (3 hunks)
  • frontend/src/components/GeneralCharts.tsx (1 hunks)
  • frontend/src/components/HealthMetricsCharts.tsx (1 hunks)
  • frontend/src/server/queries/projectQueries.ts (1 hunks)
  • frontend/src/types/card.ts (2 hunks)
  • frontend/src/types/healthMetrics.ts (1 hunks)
  • frontend/src/types/project.ts (2 hunks)
🧰 Additional context used
🪛 Pylint (3.3.7)
backend/apps/owasp/migrations/0043_remove_projecthealthmetrics_has_recent_commits_and_more.py

[refactor] 6-6: Too few public methods (0/2)

(R0903)

backend/apps/owasp/migrations/0041_merge_20250613_0336.py

[refactor] 6-6: Too few public methods (0/2)

(R0903)

backend/apps/owasp/migrations/0042_remove_projecthealthmetrics_has_no_recent_commits_and_more.py

[refactor] 6-6: Too few public methods (0/2)

(R0903)

backend/apps/owasp/migrations/0045_remove_projecthealthmetrics_has_long_open_issues_and_more.py

[refactor] 6-6: Too few public methods (0/2)

(R0903)

backend/apps/owasp/migrations/0044_merge_20250615_0346.py

[refactor] 6-6: Too few public methods (0/2)

(R0903)

backend/tests/apps/owasp/graphql/nodes/project_health_metrics_test.py

[error] 14-14: Class 'ProjectHealthMetricsNode' has no 'strawberry_definition' member

(E1101)


[error] 40-40: Class 'ProjectHealthMetricsNode' has no 'strawberry_definition' member

(E1101)

backend/apps/owasp/migrations/0038_projecthealthmetrics_has_long_open_issues_and_more.py

[refactor] 6-6: Too few public methods (0/2)

(R0903)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (14)
cspell/custom-dict.txt (1)

34-34: Add apexcharts to custom dictionary
This inclusion ensures the spell checker recognizes the new charting library name without false positives.

frontend/package.json (1)

39-39: Include ApexCharts dependencies
Adding apexcharts and its React wrapper in dependencies correctly equips the frontend for rendering health metric charts.

Also applies to: 58-58

backend/apps/owasp/migrations/0038_projecthealthmetrics_has_long_open_issues_and_more.py (1)

12-31: Skip review on auto-generated migration
This migration file is Django-generated schema change code; no manual edits needed.

backend/apps/owasp/models/project_health_metrics.py (1)

26-26: Skip trivial formatting change
The added blank line after forks_count is a harmless style adjustment.

backend/apps/owasp/management/commands/owasp_update_project_health_metrics_scores.py (1)

63-68: Approve refactored bulk_save call formatting
The multi-line argument format enhances readability without altering behavior.

frontend/src/types/project.ts (1)

2-3: Import location looks off

HealthMetricsProps lives in types/healthMetrics, but other domain-level types (Issue, Release, …) live in sibling directories. If healthMetrics.ts ended up in a different folder by mistake, move it next to the other types to keep project structure flat and predictable.

frontend/src/types/card.ts (1)

6-7: Circular dependency risk

Card types now pull HealthMetricsProps, which itself likely references project-level concepts. Watch out for circular type imports (types/healthMetricstypes/projecttypes/card). If you notice slow TS compile or weird “circular structure” warnings, extract shared primitives to a new module (e.g. types/metrics).

backend/apps/owasp/migrations/0045_remove_projecthealthmetrics_has_long_open_issues_and_more.py (1)

11-28: Data loss audit

Dropping four boolean columns permanently deletes historical data. Confirm that:

  1. No downstream analytics or dashboards still rely on them.
  2. A backup/ETL has archived the values if they are useful for future reporting.

If both checks pass, migration looks correct.

backend/apps/owasp/migrations/0042_remove_projecthealthmetrics_has_no_recent_commits_and_more.py (1)

11-21: ```shell
#!/bin/bash

Locate and display the contents of migration 0043 in the owasp app

migration_files=$(fd -e py '0043_.*.py' backend/apps/owasp/migrations)

if [ -z "$migration_files" ]; then
echo "No 0043 migration file found."
exit 1
fi

for file in $migration_files; do
echo "== $file =="
sed -n '1,200p' "$file"
done


</details>
<details>
<summary>backend/apps/owasp/migrations/0043_remove_projecthealthmetrics_has_recent_commits_and_more.py (1)</summary>

`11-21`: ```shell
#!/bin/bash
# List any migration files with “0042” in their name
echo "Migration files containing '0042':"
ls backend/apps/owasp/migrations | grep '0042' || echo "No 0042 files found"

# For each matching file, print its leading content to inspect the field operations
for file in backend/apps/owasp/migrations/*0042*.py; do
  echo
  echo "===== $file ====="
  sed -n '1,200p' "$file"
done
backend/apps/owasp/migrations/0044_merge_20250615_0346.py (1)

6-12: Merge migration is fine but indicates branching pains

The merge itself is harmless, but it’s another hint that the migration chain is oscillating. After consolidating 0042/0043, this merge will likely become unnecessary and can be deleted.

backend/tests/apps/owasp/management/commands/owasp_update_project_health_metrics_scores_test.py (1)

39-54: Weights diverge: double-check with algorithm

The metric weights for open_issues_count, unanswered_issues_count, and unassigned_issues_count are now higher (7). Confirm that the production scoring code uses the same values; otherwise the test will give false confidence.

backend/apps/owasp/graphql/nodes/project.py (1)

39-44: ```shell
#!/bin/bash
set -e

rg "class BulkSaveModel" -A20 -n backend/apps/owasp/models


</details>
<details>
<summary>frontend/src/server/queries/projectQueries.ts (1)</summary>

`15-25`: **Verify camel-casing conventions between backend & frontend**

The query asks for `healthMetrics`, `forksCount`, etc., while the backend resolver defines `health_metrics` / `forks_count`.  
This only works when Strawberry’s `auto_camel_case` flag is enabled project-wide (it is by default, but can be disabled).

Please double-check that:

1. `settings.STRAWBERRY_SETTINGS["auto_camel_case"]` (or equivalent) is not overridden;  
2. No per-module override disables camel-casing.

If camel-case is turned off, this query will fail at runtime.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@ahmedxgouda ahmedxgouda force-pushed the dashboard/frontend-implementation branch from 9fcac7b to c033d35 Compare June 18, 2025 17:02
@ahmedxgouda ahmedxgouda force-pushed the dashboard/frontend-implementation branch from c033d35 to 9623f53 Compare June 18, 2025 17:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c033d35 and 9623f53.

⛔ Files ignored due to path filters (1)
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • cspell/custom-dict.txt (1 hunks)
  • frontend/__tests__/unit/data/mockProjectDetailsData.ts (1 hunks)
  • frontend/__tests__/unit/pages/ProjectDetails.test.tsx (1 hunks)
  • frontend/package.json (2 hunks)
  • frontend/src/app/projects/[projectKey]/page.tsx (1 hunks)
  • frontend/src/components/CardDetailsPage.tsx (3 hunks)
  • frontend/src/components/GeneralCharts.tsx (1 hunks)
  • frontend/src/components/HealthMetricsCharts.tsx (1 hunks)
  • frontend/src/server/queries/projectQueries.ts (1 hunks)
  • frontend/src/types/card.ts (2 hunks)
  • frontend/src/types/healthMetrics.ts (1 hunks)
  • frontend/src/types/project.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/tests/unit/data/mockProjectDetailsData.ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • cspell/custom-dict.txt
  • frontend/src/types/project.ts
  • frontend/src/types/card.ts
  • frontend/src/app/projects/[projectKey]/page.tsx
  • frontend/package.json
  • frontend/src/components/CardDetailsPage.tsx
  • frontend/src/types/healthMetrics.ts
  • frontend/src/server/queries/projectQueries.ts
  • frontend/src/components/HealthMetricsCharts.tsx
  • frontend/src/components/GeneralCharts.tsx
🧰 Additional context used
🪛 GitHub Actions: Run CI/CD
frontend/__tests__/unit/pages/ProjectDetails.test.tsx

[error] 25-25: CSpell: Unknown word 'apexchart' found during spelling check.

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: CodeQL (javascript-typescript)
  • GitHub Check: CodeQL (python)

@ahmedxgouda ahmedxgouda force-pushed the dashboard/frontend-implementation branch from a8afbfe to 88768dc Compare June 18, 2025 19:48
Copy link
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great PoC 👍
We need to play w/ the style a bit and have enough data to visualize.

You can start with addressing any of these:

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!!! I know it's still in draft, but I also have a few comments 👍🏼

@kasya
Copy link
Collaborator

kasya commented Jun 21, 2025

@ahmedxgouda I also just pushed some updates for Radial Chart styling. I realized that gradient fill works not as I expected it to, so switched that to use static gradient background image instead.
Could you review those changes?

Also, I realized that it would be super helpful to have a "legend" of what green and red mean on this scale. Could you try adding something like this to the radial chart (Active and Stale)?
image

Also, if we could pluralize the day(s) in the caption that would be great. So that it would say 1 day and 5 days.
Screenshot 2025-06-20 at 7 15 04 PM
We already have a utility for that 👍🏼

@ahmedxgouda ahmedxgouda requested a review from kasya June 21, 2025 05:01
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
frontend/src/components/GradientRadialChart.tsx (3)

20-38: Simplify checkpoint calculation logic.

The checkpoint calculation can be streamlined as suggested in the previous review. The current approach with separate assignment and fallback is unnecessarily verbose.

Apply this refactor to make the logic more concise:

-  let checkpoint = requirement
-  let showRed = false
-  const stops = [0, 100]
-  const greenColor = '#0ef94e'
-  const redColor = '#f94e0e' // Red color for the end of the gradient
-  const orangeColor = '#f9b90e' // Orange color for the middle of the gradient
-  const colorStops = [
-    {
-      offset: 0,
-      color: greenColor,
-      opacity: 1,
-    },
-  ]
-  if (days > requirement) {
-    checkpoint = days
-    showRed = true
-  }
-  // Ensure checkpoint is at least 1 to avoid division by zero
-  checkpoint = checkpoint || 1
+  const stops = [0, 100]
+  const greenColor = '#0ef94e'
+  const redColor = '#f94e0e'
+  const orangeColor = '#f9b90e'
+  const colorStops = [
+    {
+      offset: 0,
+      color: greenColor,
+      opacity: 1,
+    },
+  ]
+  const showRed = days > requirement
+  const checkpoint = Math.max(days, requirement) || 1

23-25: Consider using theme-aware colors.

The gradient colors are hard-coded and don't adapt to the theme. Consider using CSS custom properties or theme-based color values for better consistency with the overall design system.

Consider moving these to a theme-aware approach:

-  const greenColor = '#0ef94e'
-  const redColor = '#f94e0e' // Red color for the end of the gradient
-  const orangeColor = '#f9b90e' // Orange color for the middle of the gradient
+  const greenColor = theme === 'dark' ? '#0ef94e' : '#22c55e'
+  const redColor = theme === 'dark' ? '#f94e0e' : '#ef4444'
+  const orangeColor = theme === 'dark' ? '#f9b90e' : '#f59e0b'

110-110: Consider making chart height configurable.

The chart height is hard-coded at 400px. Consider making this configurable through props for better reusability.

Add height as an optional prop:

const GradientRadialChart: React.FC<{
  title: string
  icon?: IconProp
  days: number
  requirement: number
+  height?: number
-}> = ({ title, days, icon, requirement }) => {
+}> = ({ title, days, icon, requirement, height = 400 }) => {

Then use it in the Chart component:

-        height={400}
+        height={height}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c34bc2 and 4850a0c.

📒 Files selected for processing (2)
  • frontend/src/components/GradientRadialChart.tsx (1 hunks)
  • frontend/src/components/LineChart.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/LineChart.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Run Code Scan
  • GitHub Check: CodeQL (javascript-typescript)
  • GitHub Check: CodeQL (python)
🔇 Additional comments (1)
frontend/src/components/GradientRadialChart.tsx (1)

1-122: Overall implementation looks solid with proper theme integration.

The component successfully integrates ApexCharts with Next.js theming and provides a clean interface for visualizing health metrics. The dynamic import approach correctly handles SSR issues, and the pluralization utility is properly utilized.

Comment on lines 63 to 112
<Chart
key={theme}
options={{
chart: {
animations: {
enabled: true,
speed: 1000,
},
},
plotOptions: {
radialBar: {
hollow: {
margin: 0,
},
startAngle: -90,
endAngle: 90,
dataLabels: {
show: true,
name: {
show: false,
},
value: {
formatter: () => `${days} ${pluralize(days, 'day', 'days')}`,
color: theme === 'dark' ? '#ececec' : '#1E1E2C',
fontSize: '20px',
show: true,
offsetY: 0,
},
},
track: {
background: theme === 'dark' ? '#1E1E2C' : '#ececec',
margin: 0,
},
},
},
fill: {
type: 'gradient',
gradient: {
shade: theme,
type: 'horizontal',
shadeIntensity: 0.5,
stops: stops,
colorStops: colorStops,
},
},
}}
series={[requirement ? normalizedDays : 0]}
height={400}
type="radialBar"
/>
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add accessibility attributes to the chart.

The chart lacks proper accessibility attributes for screen readers. Consider adding ARIA labels and roles to improve accessibility.

Add accessibility attributes:

      <Chart
+        aria-label={`Health metric chart showing ${days} ${pluralize(days, 'day', 'days')} out of ${requirement} required`}
+        role="img"
        key={theme}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Chart
key={theme}
options={{
chart: {
animations: {
enabled: true,
speed: 1000,
},
},
plotOptions: {
radialBar: {
hollow: {
margin: 0,
},
startAngle: -90,
endAngle: 90,
dataLabels: {
show: true,
name: {
show: false,
},
value: {
formatter: () => `${days} ${pluralize(days, 'day', 'days')}`,
color: theme === 'dark' ? '#ececec' : '#1E1E2C',
fontSize: '20px',
show: true,
offsetY: 0,
},
},
track: {
background: theme === 'dark' ? '#1E1E2C' : '#ececec',
margin: 0,
},
},
},
fill: {
type: 'gradient',
gradient: {
shade: theme,
type: 'horizontal',
shadeIntensity: 0.5,
stops: stops,
colorStops: colorStops,
},
},
}}
series={[requirement ? normalizedDays : 0]}
height={400}
type="radialBar"
/>
<Chart
aria-label={`Health metric chart showing ${days} ${pluralize(days, 'day', 'days')} out of ${requirement} required`}
role="img"
key={theme}
options={{
chart: {
animations: {
enabled: true,
speed: 1000,
},
},
plotOptions: {
radialBar: {
hollow: {
margin: 0,
},
startAngle: -90,
endAngle: 90,
dataLabels: {
show: true,
name: {
show: false,
},
value: {
formatter: () => `${days} ${pluralize(days, 'day', 'days')}`,
color: theme === 'dark' ? '#ececec' : '#1E1E2C',
fontSize: '20px',
show: true,
offsetY: 0,
},
},
track: {
background: theme === 'dark' ? '#1E1E2C' : '#ececec',
margin: 0,
},
},
},
fill: {
type: 'gradient',
gradient: {
shade: theme,
type: 'horizontal',
shadeIntensity: 0.5,
stops: stops,
colorStops: colorStops,
},
},
}}
series={[requirement ? normalizedDays : 0]}
height={400}
type="radialBar"
/>
🤖 Prompt for AI Agents
In frontend/src/components/GradientRadialChart.tsx between lines 63 and 112, the
Chart component lacks accessibility attributes for screen readers. Add
appropriate ARIA attributes such as aria-label describing the chart's purpose
and role="img" to the Chart component's container or root element to improve
accessibility. Ensure these attributes provide meaningful context about the
chart content for assistive technologies.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not available in the Chart component!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🌐 Web query:

react-apexcharts accessibility attributes aria-label role support

💡 Result:

React-ApexCharts is a React wrapper for ApexCharts, a popular charting library. When it comes to accessibility, such as support for ARIA attributes, aria-label, and role, it's important to understand both what ApexCharts offers and how React-ApexCharts exposes or supports those features.

  1. ARIA Attributes in ApexCharts:
    ApexCharts has some built-in support for accessibility:

    • It generates SVG elements with roles and attributes like role="img" by default on the chart container.
    • It allows setting aria-label and other ARIA attributes via its configuration options under the chart key, particularly chart: { foreColor, sparkline, ... }.
    • You can provide aria-label or aria-describedby through options like chart: { toolbar: { autoSelected: 'zoom', show: false }, animations: { enabled: true }, ...} but explicit aria-labels might need to be manually added or customized.
  2. React-ApexCharts Support:

    • Since React-ApexCharts is a thin wrapper, it mostly passes props to ApexCharts.
    • You can add ARIA attributes to the main chart container via props or by customizing the options passed to ApexCharts.
    • The component itself does not have explicit props just for ARIA or accessibility attributes, so you'd typically handle this by customizing the chart options or wrapping the chart in a container div with appropriate accessibility attributes.
  3. Customizing ARIA for React-ApexCharts:

    • You can add an aria-label on the container div wrapping the chart.
    • You can pass accessibility-related options in the ApexCharts options, e.g.:
    const options = {
      chart: {
        id: 'my-chart',
        type: 'line',
        // other chart options
      },
      // ApexCharts does not have a dedicated accessibility config, but you can add custom attributes on render
    };
    
    return (
      <div role="img" aria-label="Sales line chart showing monthly revenue">
        <ReactApexChart options={options} series={series} type="line" height={350} />
      </div>
    );
  4. Checking the latest documentation:

    • It’s a good idea to check ApexCharts official docs and React-ApexCharts GitHub repo for any updates or contributions related to accessibility.

In conclusion:

  • ApexCharts provides some basic ARIA support.
  • React-ApexCharts does not have specialized ARIA props.
  • You can and should add necessary ARIA attributes (like aria-label and role) by wrapping the ReactApexChart component or using React props on the container.
  • For important and complex accessibility needs, consider augmenting the chart with descriptive text or accessible alternatives.

Would you like me to help with a specific React-ApexCharts code example implementing ARIA attributes?


@ahmedxgouda You're absolutely right! The react-apexcharts Chart component doesn't support those attributes directly.

The correct approach would be to wrap the Chart in a container div with accessibility attributes:

<SecondaryCard title={title} icon={icon}>
  <div 
    role="img" 
    aria-label={`Health metric chart showing ${days} ${pluralize(days, 'day', 'days')} out of ${requirement} required`}
  >
    <Chart
      key={theme}
      options={{
        // ... existing options
      }}
      series={[requirement ? normalizedDays : 0]}
      height={400}
      type="radialBar"
    />
  </div>
  <div className="mt-0 flex justify-around">
    <span className="text-md text-gray-800 dark:text-white md:ml-4">Active</span>
    <span className="text-md text-gray-800 dark:text-white md:mr-4">Stale</span>
  </div>
</SecondaryCard>

Thanks for the correction!


✏️ Learnings added
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/GradientRadialChart.tsx:67-116
Timestamp: 2025-06-21T12:21:32.328Z
Learning: The react-apexcharts Chart component does not support ARIA attributes like aria-label and role as direct props. To add accessibility attributes to ApexCharts in React, wrap the Chart component in a container div with the appropriate ARIA attributes.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
frontend/src/components/GradientRadialChart.tsx (1)

67-113: Add accessibility wrapper for the Chart component.

Based on previous discussions, the Chart component doesn't support ARIA attributes directly. You should wrap it in a container div with appropriate accessibility attributes to improve screen reader support.

Apply this accessibility improvement:

<SecondaryCard title={title} icon={icon}>
+  <div 
+    role="img" 
+    aria-label={`Health metric chart showing ${days} ${pluralize(days, 'day', 'days')} out of ${requirement} required`}
+  >
    <Chart
      key={theme}
      options={{
        // ... existing options
      }}
      series={[requirement ? normalizedDays : 0]}
      height={400}
      type="radialBar"
    />
+  </div>
🧹 Nitpick comments (1)
frontend/src/components/GradientRadialChart.tsx (1)

23-25: Consider using theme-based colors for better consistency.

The hardcoded color values don't adapt to the theme. Consider using CSS custom properties or theme-based color variables to ensure consistency with the overall design system.

You could define theme-aware colors:

-  const greenColor = '#0ef94e'
-  const redColor = '#f94e0e' // Red color for the end of the gradient
-  const orangeColor = '#f9b90e' // Orange color for the middle of the gradient
+  const greenColor = theme === 'dark' ? '#0ef94e' : '#22c55e'
+  const redColor = theme === 'dark' ? '#f94e0e' : '#ef4444'
+  const orangeColor = theme === 'dark' ? '#f9b90e' : '#f59e0b'
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af37662 and 2e93833.

⛔ Files ignored due to path filters (1)
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • frontend/package.json (2 hunks)
  • frontend/src/components/GradientRadialChart.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/package.json
🧰 Additional context used
🧠 Learnings (1)
frontend/src/components/GradientRadialChart.tsx (1)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/GradientRadialChart.tsx:67-116
Timestamp: 2025-06-21T12:21:32.328Z
Learning: The react-apexcharts Chart component does not support ARIA attributes like aria-label and role as direct props. To add accessibility attributes to ApexCharts in React, wrap the Chart component in a container div with the appropriate ARIA attributes.
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: CodeQL (javascript-typescript)
  • GitHub Check: CodeQL (python)
🔇 Additional comments (2)
frontend/src/components/GradientRadialChart.tsx (2)

34-38: Good implementation of inline assignment.

The checkpoint assignment and division by zero protection are well implemented. The inline assignment checkpoint = days || 1 effectively handles the edge case while keeping the code concise.


110-110: Series calculation correctly handles domain constraints.

The conditional logic requirement ? normalizedDays : 0 is appropriate given that requirements should never be 0 in this domain. This correctly treats invalid requirements as an edge case.

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahmedxgouda left a couple more comments!
I've also fixed the issue with extra space in firefox and will push the changes shortly.


const HealthMetrics: React.FC<{ data: HealthMetricsProps[] }> = ({ data }) => {
const openIssuesCountArray = data.map((item) => item.openIssuesCount)
const labels = data.map((item, index) => `Day ${index + 1}`)
Copy link
Collaborator

@kasya kasya Jun 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to update resolvers for all of these data points that we use on charts to also return the date. So this should be not just a number, but a date and number of Open issues at that date. So that it would show the date on the tooltip and on the x axis instead of just Day 1 etc.
Same for other data points.
Screenshot 2025-06-21 at 12 31 25 PM

Comment on lines 23 to 30
<h3 className="flex items-center gap-2 text-lg font-semibold text-gray-700 dark:text-gray-200">
<FontAwesomeIcon icon={faHeart} />
Health Metrics
</h3>
<div className="flex-grow border-t border-gray-300"></div>
</div>
<h4 className="mb-4 text-center text-lg font-semibold text-gray-700 dark:text-gray-200">
Score: {data[0].score}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this whole divider and add a score at the top of the page. It should be something like a circle of different background color (based on the score, again from green to red) and the score number.
We want it to be more prominent at the top of the page. It could also be clickable to scroll to the metrics charts lower on the page 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another chart again or just a circle? @kasya

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahmedxgouda I meant that clicking it should scroll to these charts on the project page. Maybe be the first one Issues trend? 🤔 We can update that later on, not necessarily with this PR 👍🏼

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you delete this file if we no longer use it?

const normalizedRequirement = (requirement / checkpoint) * 100

if (showRed) {
const orangeStop = normalizedRequirement / 2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had this case when with 5 days requirement it still showed all green even though technically it should have showed at least orange.
Screenshot 2025-06-21 at 11 24 59 AM
I can see we only set orange if showRed is truthy. Can we change this logic? Also, maybe we should start showing orange at 75% instead of 50%? I'm not sure, but would love to see how that looks with setups when project is almost at stale.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
backend/apps/owasp/graphql/nodes/project_health_metrics.py (1)

31-33: Critical resolver issue remains unresolved from previous review.

The existing resolver methods still return bound method objects instead of actual data values, which will cause serialization failures at runtime.

Also applies to: 41-43, 51-53, 56-58, 66-68

frontend/src/types/healthMetrics.ts (1)

1-14: Type mis-alignment with GraphQL schema persists — missing fields from backend.

The interface is still missing ageDays and lastPullRequestDays fields that exist in the GraphQL schema, which can lead to runtime undefined errors.

Add the missing fields to maintain type safety:

 export type HealthMetricsProps = {
+  ageDays: number
   createdAt: string
   forksCount: number
   lastCommitDays: number
   lastCommitDaysRequirement: number
+  lastPullRequestDays: number
   lastReleaseDays: number
   lastReleaseDaysRequirement: number
   openIssuesCount: number
   openPullRequestsCount: number
   score: number
   starsCount: number
   unassignedIssuesCount: number
   unansweredIssuesCount: number
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d01dbf and de264f7.

📒 Files selected for processing (8)
  • backend/apps/owasp/graphql/nodes/project_health_metrics.py (3 hunks)
  • backend/tests/apps/owasp/graphql/nodes/project_health_metrics_test.py (3 hunks)
  • frontend/src/components/CardDetailsPage.tsx (4 hunks)
  • frontend/src/components/GradientRadialChart.tsx (1 hunks)
  • frontend/src/components/HealthMetrics.tsx (1 hunks)
  • frontend/src/components/LineChart.tsx (1 hunks)
  • frontend/src/server/queries/projectQueries.ts (1 hunks)
  • frontend/src/types/healthMetrics.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • frontend/src/server/queries/projectQueries.ts
  • frontend/src/components/CardDetailsPage.tsx
  • frontend/src/components/HealthMetrics.tsx
  • frontend/src/components/LineChart.tsx
  • backend/tests/apps/owasp/graphql/nodes/project_health_metrics_test.py
  • frontend/src/components/GradientRadialChart.tsx
🧰 Additional context used
🪛 Pylint (3.3.7)
backend/apps/owasp/graphql/nodes/project_health_metrics.py

[error] 38-38: Instance of 'ProjectHealthMetricsNode' has no 'nest_created_at' member

(E1101)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (2)
backend/apps/owasp/graphql/nodes/project_health_metrics.py (1)

35-38: ```shell
#!/bin/bash
set -e

Show GraphQL node definition including created_at resolver

sed -n '1,200p' backend/apps/owasp/graphql/nodes/project_health_metrics.py | sed -n '1,60p'


</details>
<details>
<summary>frontend/src/types/healthMetrics.ts (1)</summary>

`16-19`: **LGTM! Clean chart data type definition.**

The `ApexChartLabelSeries` type is well-structured for chart data visualization.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +60 to +63
@strawberry.field
def last_release_days_requirement(self) -> int:
"""Resolve last release age requirement in days."""
return self.last_release_days_requirement
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical issue: Incorrect resolver implementation.

This new field follows the same problematic pattern, returning the method object instead of the actual integer value.

Apply this fix:

-    @strawberry.field
-    def last_release_days_requirement(self) -> int:
-        """Resolve last release age requirement in days."""
-        return self.last_release_days_requirement
+    @strawberry.field(name="lastReleaseDaysRequirement")
+    def resolve_last_release_days_requirement(self) -> int:
+        """Last release age requirement in days."""
+        from typing import cast
+        return cast(ProjectHealthMetrics, self).last_release_days_requirement
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@strawberry.field
def last_release_days_requirement(self) -> int:
"""Resolve last release age requirement in days."""
return self.last_release_days_requirement
@strawberry.field(name="lastReleaseDaysRequirement")
def resolve_last_release_days_requirement(self) -> int:
"""Last release age requirement in days."""
from typing import cast
return cast(ProjectHealthMetrics, self).last_release_days_requirement
🤖 Prompt for AI Agents
In backend/apps/owasp/graphql/nodes/project_health_metrics.py around lines 60 to
63, the resolver method last_release_days_requirement incorrectly returns the
method itself instead of the integer value. Fix this by returning the actual
attribute or value representing the last release days requirement, not the
method object.

Comment on lines +45 to +48
@strawberry.field
def last_commit_days_requirement(self) -> int:
"""Resolve last commit age requirement in days."""
return self.last_commit_days_requirement
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical issue: Incorrect resolver implementation.

This new field follows the same problematic pattern as the existing resolvers, returning the method object instead of the actual integer value.

Apply this fix:

-    @strawberry.field
-    def last_commit_days_requirement(self) -> int:
-        """Resolve last commit age requirement in days."""
-        return self.last_commit_days_requirement
+    @strawberry.field(name="lastCommitDaysRequirement")
+    def resolve_last_commit_days_requirement(self) -> int:
+        """Last commit age requirement in days."""
+        from typing import cast
+        return cast(ProjectHealthMetrics, self).last_commit_days_requirement
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@strawberry.field
def last_commit_days_requirement(self) -> int:
"""Resolve last commit age requirement in days."""
return self.last_commit_days_requirement
@strawberry.field(name="lastCommitDaysRequirement")
def resolve_last_commit_days_requirement(self) -> int:
"""Last commit age requirement in days."""
from typing import cast
return cast(ProjectHealthMetrics, self).last_commit_days_requirement
🤖 Prompt for AI Agents
In backend/apps/owasp/graphql/nodes/project_health_metrics.py around lines 45 to
48, the resolver method last_commit_days_requirement incorrectly returns the
method object itself instead of the integer value. To fix this, rename the
method to avoid name collision with the attribute or access the attribute via
self with a different name, ensuring the method returns the integer value stored
in the instance attribute rather than the method object.

Copy link

@ahmedxgouda ahmedxgouda requested a review from kasya June 22, 2025 00:59
Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are almost there! 💪🏼
Left a few more requests ⬇️

@@ -1,4 +1,5 @@
export type HealthMetricsProps = {
createdAt: string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great call!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not exactly working as it should regarding the gradient. This is why I feel like static image worked better. Let me add some screenshots to maybe help me explain.
Here are 2 different projects charts for releases:
Screenshot 2025-06-21 at 5 54 38 PM
Screenshot 2025-06-21 at 5 54 49 PM
Based on the second one - the first one should already have yellow/orange color showing on the fill (I marked approx. spot on the full circle chart).

Same for these two - looking at releases at the some spot - the commits chart should have some yellow-ish shade already:
Screenshot 2025-06-21 at 5 52 01 PM

Is this possible with the stops passed in? If not, we'll have to go back to static image.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I don't feel the static image is a good idea.
Let me explain the logic further. We determined two checkpoints, one for the gradient to start going to orange and the other to start going to red. The fill of the gradient and the checkpoints are determined based on mathematical calculations. For example, the orange checkpoint is when the days hits 3/4 of the requirement. The red checkpoint is when the days exceeded the requirement. Any other case, the bar still green.
The bars are scaled based on their requirements. It is not reasonable to compare between two bars with different requirements, because they have different scales. The first two that you provided, not just they have different scales, the second one's scale is twice as the first one's. For the ending checkpoint, we determined that it is either the days or the requirement (just the bigger). So, here we can say that why the second two do not have the same color at the same bar position. They actually don't have the same scale! the second has a scale of 6 -which is the days- and the first has a skill of 5 - which is the requirement.
So, we can say, to resolve the issue that you stated. We need like a formula to get a fixed end checkpoint which does not depend on conditions, although this is so accurate.
The logic can be improved within time, we are close to the ideal approach. I don't think it is a good idea to just stick with the static image. You know, it is static, and may fail to represent the value accurately.
Sorry for this long text, just wanted to explain my thoughts. I'd like to hear your opinion of that of course. Thanks! @kasya

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooh, I finally got where the confusion is coming from for both of us! We were looking at this chart differently.

As I see it - even the full fill circle should not have 6 days as a scale as you mentioned above - it should still just be the requirement (so, 5). But if the project is passed that (say, 10 days) it should just show red, meaning that the project is stale and passed the requirement.

So, to simplify this, the days since last release should not affect the scale of the chart - it should only change the percentage of the "filled" part of the chart.
And that makes more sense to me, because for example, if we look at these 2 charts above - the one for commits has 2 days and 5 requirement. It's filled to approx. where 2 would be on that scale. But then on the left you have the same requirement (5) but the scale is different. I find it confusing tbh. I would that expect them to be different only if the requirement is different, because that's what represents the chart - number of days from active to stale.

Does that makes sense? what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahmedxgouda we are discussing this with @arkid15r! Please do not work on any updates until further notice :) It might be we keep it this way.

import React from 'react'
import type { HealthMetricsProps } from 'types/healthMetrics'
import GradientRadialChart from 'components/GradientRadialChart'
import LineChart from 'components/LineChart'

const HealthMetrics: React.FC<{ data: HealthMetricsProps[] }> = ({ data }) => {
const openIssuesCountArray = data.map((item) => item.openIssuesCount)
const labels = data.map((item, index) => `Day ${index + 1}`)
const labels = data.map((item) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding dates, but they are showing backwards 😅 I think it should be Jun 20th, and then Jun 21st. Could you order them by date (preferably on the backend)?
Screenshot 2025-06-21 at 5 49 53 PM

@@ -49,11 +50,27 @@ const DetailsCard = ({
geolocationData = null,
repositories = [],
}: DetailsCardProps) => {
let scoreStyle = 'bg-green-200 text-green-800'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do it as a circle instead on the right side of the page. It should specify that this is a health score and show a number.
Here's a very rough sketch of what I had in mind!
image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, for line charts - is there a way to customize y axes? I don't think it makes sense to show .5 on these - there can not be half a pull request or half a star.
Screenshot 2025-06-21 at 6 10 38 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Project Health Visualization to Project Page
3 participants