Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@Yajush-afk
Copy link
Contributor

@Yajush-afk Yajush-afk commented Oct 14, 2025

Fix GitHub Badge Issue - Improve Chart Visualization and Data Range

Issue: the github badge is not looking right fixes #3226

Description

This PR addresses several issues with the GitHub badge charts for both RepoBadgeView and ProjectBadgeView:

  • Extended chart from 7 days to 30 days for better trend visibility
  • Fixed bar spacing and width calculations to properly display 30 bars
  • Added visual indicators for zero-visit days using faint bars
  • Prevented text overlap with proper margin and positioning
  • Improved performance by replacing gradient rendering with solid rectangles

Modified Files:

  • website/views/project.py - Updated both RepoBadgeView and ProjectBadgeView classes

Key Improvements:

  • 30-day data window: Changed from seven_days_ago to thirty_days_ago
  • Consistent bar spacing: Fixed width calculation using chart_width / 32 for 30 bars
  • Zero-day handling: Added faint bars (#fcbab3) for days with no visits
  • Text positioning: Reserved text_height = 50px to prevent title overlap
  • Unique visit counting: Added distinct=True for accurate IP counting
  • Performance optimization: Replaced pixel-by-pixel gradient with solid rectangles

Before/After

Before: Inconsistent spacing, overlapping, random bar size, 7-day window, missing zero-day indicators (image shared)

image

After: Professional 30-bar chart with proper spacing, no overlapping, consistent bar size and zero-day visualization (image shared)

image

Impact

This fix significantly improves the visual quality and usefulness of GitHub badges:

  • Better trend analysis with 30-day historical data
  • Professional appearance with consistent spacing
  • Complete data visualization showing both active and inactive periods
  • Enhanced performance with optimized rendering

Checklist

  • Code follows project style guidelines
  • Changes work for both RepoBadgeView and ProjectBadgeView
  • All edge cases handled (zero visits, high traffic, etc.)
  • No breaking changes introduced
  • Performance improvements implemented

Additional Notes

The changes maintain backward compatibility while significantly improving the badge quality. The 30-day window provides more meaningful trend data for repository activity tracking.

Summary by CodeRabbit

  • New Features
    • Badges now show 30-day unique visits counted by distinct IPs (replacing the 7-day window).
    • Charts display all 30 days; zero-visit days shown as faint bars in a fixed 30-bar layout.
    • Reserved title area with adjusted bar widths, spacing, and grid offsets.
    • Total views text is smaller, centered, and repositioned to avoid overlap.
    • Improved font fallback and minor visual refinements; cache behavior unchanged.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 14, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Updated ProjectBadgeView and RepoBadgeView to compute 30‑day unique‑visit badges (distinct IPs), include zero‑visit days in the 30‑day sequence, and adjust PNG chart rendering (fixed 30‑bar layout, reserved title area, grid/bar/text placement, and font fallback). No-cache headers unchanged; class docstrings added.

Changes

Cohort / File(s) Summary of Changes
Badge views: 30-day unique visits and rendering updates
website/views/project.py
Switched aggregation window from 7 to 30 days; count distinct IPs per day; build full 30‑day series including zero days; fixed 30‑bar layout (bar_width, bar_spacing) and reserved top title area; shifted grid vertically; draw proportional bars for non‑zero days and faint zero‑day bars; adjusted total‑views text size/centering/position; improved font loading fallback; added class docstrings; preserved no‑cache headers.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant V as Project/RepoBadgeView
  participant DB as Visits Store
  participant R as Chart Renderer

  C->>V: GET /badge.png
  activate V

  V->>DB: Query visits for last 30 days
  DB-->>V: Visit records (timestamps, IPs)

  V->>V: Aggregate distinct IPs per day, fill zeros to 30-day sequence

  V->>R: Prepare chart data (30 bars, title area, spacing)
  R-->>V: PNG image (bars, grid, totals, fonts with fallback)

  V-->>C: 200 OK (PNG, Cache-Control: no-cache)
  deactivate V
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title β€œFix/GitHub badge issue #3226” references an issue and a badge fix but does not clearly convey the primary change of extending the chart to a 30-day window with updated visuals, and the slash punctuation makes it less readable as a concise summary. Please rename the title to explicitly describe the main change, for example β€œExtend GitHub and project badge charts to 30-day unique visit view with zero-day bars,” so that the summary is clear and self-contained.
βœ… Passed checks (1 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • πŸ“ Generate docstrings
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a 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

Comment @coderabbitai help to get the list of available commands and usage tips.

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

🧹 Nitpick comments (1)
website/views/project.py (1)

1898-1985: Consider extracting common badge generation logic.

The badge generation code in RepoBadgeView is nearly identical to ProjectBadgeView (lines 169-253). Both classes implement the same chart rendering logic with only minor differences (project vs. repo objects).

Consider extracting the common badge generation logic into a shared helper method or base class to improve maintainability and reduce duplication. This would make future updates (like color schemes or layout changes) easier to apply consistently.

Example approach:

  • Create a _generate_badge_image helper method that accepts visit data and total count
  • Both view classes call this helper with their respective data
  • Reduces ~100 lines of duplicated code
πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 82ad67c and 7111411.

πŸ“’ Files selected for processing (1)
  • website/views/project.py (7 hunks)
🧰 Additional context used
πŸͺ› Ruff (0.14.0)
website/views/project.py

203-203: Local variable text_color is assigned to but never used

Remove assignment to unused variable text_color

(F841)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
πŸ”‡ Additional comments (6)
website/views/project.py (6)

123-125: LGTM! Clear documentation.

The class docstring accurately describes the badge's purpose and key feature (30-day unique visits with zero-day visualization).


206-220: LGTM! Chart dimensions correctly adjusted for 30 bars.

The bar width and spacing calculations properly accommodate 30 bars:

  • bar_width = chart_width / 32 accounts for 30 bars plus margin space
  • bar_spacing = chart_width / 30 provides even distribution

227-242: LGTM! Zero-day visualization implemented correctly.

The faint bar color (#fcbab3) effectively indicates days with zero visits, preventing confusion about missing data.


243-253: LGTM! Text positioning prevents overlap.

Centering the title at y=15 with reserved space (text_height=50) ensures no overlap with the chart bars.


1851-1853: LGTM! Clear documentation.

The class docstring accurately describes the badge's purpose, matching ProjectBadgeView's documentation style.


169-192: Verify IP model and unique address counting
Confirm that the IP model defines an address field and that Count("address", distinct=True) correctly counts unique daily visitors. For example, run:

rg -nP 'class\s+IP' -C3
rg -n 'address' -C2

@nitinawari
Copy link
Contributor

fix the pre-commit check

@Yajush-afk
Copy link
Contributor Author

fix the pre-commit check

yes doing that, thankyou for the suggestion

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

🧹 Nitpick comments (3)
website/views/project.py (3)

122-265: Consider extracting common badge generation logic.

Both ProjectBadgeView and RepoBadgeView contain nearly identical code (~140 lines each) with only minor differences (model type and field names). This duplication violates the DRY principle and creates maintenance burden.

Consider extracting the common badge generation logic into a helper function or base class:

class BaseBadgeView(APIView):
    """Base class for generating visit badges."""
    
    model_class = None  # Override in subclass
    count_field = None  # Override in subclass
    
    def get_client_ip(self, request):
        # ... (current implementation) ...
    
    def generate_badge(self, obj, request):
        # ... (common badge generation logic) ...
    
    def get(self, request, slug):
        obj = get_object_or_404(self.model_class, slug=slug)
        # ... (IP tracking logic) ...
        return self.generate_badge(obj, request)

class ProjectBadgeView(BaseBadgeView):
    """Generates a 30-day unique visits badge PNG for a Project."""
    model_class = Project
    count_field = 'project_visit_count'

class RepoBadgeView(BaseBadgeView):
    """Generates a 30-day unique visits badge PNG for a Repo."""
    model_class = Repo
    count_field = 'repo_visit_count'

This refactor would eliminate duplication, reduce the risk of divergence, and simplify future maintenance.

Also applies to: 1850-1998


145-145: Clarify or remove unclear comment.

The comment "(original case-sensitive lookup)" is unclear. It doesn't explain why this is noted or what alternative was considered. If case-sensitivity was intentionally preserved, consider clarifying why; otherwise, remove the comment.


206-221: Consider extracting magic numbers as constants.

Several hardcoded values reduce code maintainability:

  • 28 (font size)
  • 15 (text y position)
  • 50 (text height)
  • 40 (margin)
  • 32, 30 (bar calculations)

Consider extracting these as class-level constants:

class ProjectBadgeView(APIView):
    # Badge dimensions
    BADGE_WIDTH = 600
    BADGE_HEIGHT = 200
    MARGIN = 40
    TEXT_HEIGHT = 50
    FONT_SIZE = 28
    TEXT_Y_POSITION = 15
    
    # Bar calculations
    BAR_COUNT = 30
    BAR_WIDTH_DIVISOR = 32  # 30 bars + 2 for margins
    
    # Colors
    BAR_COLOR = "#e05d44"
    FAINT_COLOR = "#fcbab3"
    GRID_COLOR = "#eeeeee"

This would make the code more maintainable and self-documenting.

Also applies to: 1936-1951

πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 7111411 and b4a9318.

πŸ“’ Files selected for processing (1)
  • website/views/project.py (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
πŸ”‡ Additional comments (6)
website/views/project.py (6)

123-125: LGTM! Clear docstrings added.

The docstrings accurately describe the badge generation functionality and the zero-day visualization feature.

Also applies to: 1851-1853


170-178: LGTM! 30-day window and unique IP counting implemented correctly.

The changes properly extend the tracking window to 30 days and use distinct=True to count unique IP addresses per day rather than total visits. This aligns with the PR objectives and provides more meaningful visitor metrics.

Also applies to: 1899-1907


183-193: LGTM! Zero-visit days correctly included in sequence.

The logic properly builds a complete 30-day sequence by iterating through each day and defaulting to 0 visits when no data exists. The calculation today - timedelta(days=29-i) correctly generates dates from 30 days ago to today in chronological order.

Also applies to: 1912-1923


206-221: LGTM! Chart layout and bar calculations are correct.

The reserved text_height = 50 prevents title-bar overlap, and the bar width/spacing calculations (chart_width/32 and chart_width/30 respectively) correctly render 30 bars with consistent spacing. The narrower bar width creates visual gaps between bars, which appears intentional for clarity.

Also applies to: 1936-1951


227-241: LGTM! Zero-day visualization implemented effectively.

The bar rendering correctly handles both cases: proportional bars for non-zero visits and minimal faint bars (#fcbab3 color) for zero-visit days. This provides clear visual feedback for the complete 30-day period.

Also applies to: 1957-1971


243-253: LGTM! Text positioning prevents overlap.

The centered text at y=15 with font size 28 fits comfortably within the reserved text_height=50 area. Bars begin at y=90 (margin + text_height), ensuring no overlap with the title.

Also applies to: 1973-1985

@Yajush-afk Yajush-afk changed the title Fix/GitHub badge issue 3226 Fix/GitHub badge issue #3226 Oct 14, 2025
@DonnieBLT DonnieBLT requested a review from Copilot October 15, 2025 06:20
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Addresses the GitHub badge rendering issues by moving from a 7-day to a 30-day view and improving chart layout and clarity.

  • Extends data window to 30 days and counts distinct IPs per day.
  • Reworks bar sizing/spacing and adds faint bars for zero-visit days to avoid gaps.
  • Repositions title text and simplifies rendering (solid rectangles instead of gradients) for better performance.

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

🧹 Nitpick comments (1)
website/views/project.py (1)

196-265: Consider extracting shared chart rendering logic to reduce duplication.

The chart rendering logic (lines 196-265 and 1926-1998) is nearly identical between ProjectBadgeView and RepoBadgeView, with only minor differences in the visit count field accessed. Consider extracting a shared helper function (e.g., build_badge_image(dates, counts, total_views, width, height)) to improve maintainability and reduce the risk of future drift between the two implementations. Additionally, promoting magic numbers like CHART_DAYS = 30, BAR_WIDTH_DIVISOR = 32, BAR_SPACING_DIVISOR = 30, and TEXT_HEIGHT = 50 to module-level constants would improve readability.

Example refactoring:

# Module-level constants
CHART_DAYS = 30
BAR_WIDTH_DIVISOR = 32
BAR_SPACING_DIVISOR = 30
TEXT_HEIGHT = 50
BAR_COLOR = "#e05d44"
GRID_COLOR = "#eeeeee"
FAINT_BAR_COLOR = "#fcbab3"

def build_badge_image(dates, counts, total_views, width=600, height=200):
    """
    Build a badge image with a 30-day chart.
    
    Args:
        dates: List of dates
        counts: List of visit counts per date
        total_views: Total view count to display
        width: Image width in pixels
        height: Image height in pixels
    
    Returns:
        BytesIO buffer containing the PNG image
    """
    img = Image.new("RGB", (width, height), color="white")
    draw = ImageDraw.Draw(img)
    
    margin = 40
    chart_width = width - 2 * margin
    chart_height = height - 2 * margin - TEXT_HEIGHT
    
    # Calculate max count and bar dimensions
    max_count = max(counts) if counts and max(counts) > 0 else 1
    bar_width = chart_width / BAR_WIDTH_DIVISOR
    bar_spacing = chart_width / BAR_SPACING_DIVISOR
    
    # Draw grid lines
    for i in range(5):
        y = margin + TEXT_HEIGHT + (chart_height * i) // 4
        draw.line([(margin, y), (width - margin, y)], fill=GRID_COLOR)
    
    # Draw bars
    if dates and counts:
        for i, count in enumerate(counts):
            x1 = margin + (i * bar_spacing)
            x2 = x1 + bar_width
            if count > 0:
                bar_height = (count / max_count) * chart_height
                y1 = height - margin - bar_height
                y2 = height - margin
                draw.rectangle([(x1, y1), (x2, y2)], fill=BAR_COLOR)
            else:
                faint_color = FAINT_BAR_COLOR
                y1 = y2 = height - margin - 2
                draw.rectangle([(x1, y1), (x2, y2 + 1)], fill=faint_color)
    
    # Draw title text
    try:
        font = ImageFont.truetype("DejaVuSans.ttf", 28)
    except OSError:
        font = ImageFont.load_default()
    
    text = f"Total Views: {total_views}"
    text_bbox = draw.textbbox((0, 0), text, font=font)
    text_width = text_bbox[2] - text_bbox[0]
    text_x = (width - text_width) // 2
    text_y = 15
    draw.text((text_x, text_y), text, font=font, fill=BAR_COLOR)
    
    # Save to buffer
    buffer = BytesIO()
    img.save(buffer, format="PNG", optimize=True, compress_level=9)
    buffer.seek(0)
    return buffer

Then both views would simply call:

buffer = build_badge_image(dates, counts, total_views)

Also applies to: 1926-1998

πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between b4a9318 and c4a100c.

πŸ“’ Files selected for processing (1)
  • website/views/project.py (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
πŸ”‡ Additional comments (12)
website/views/project.py (12)

123-125: LGTM!

The docstring clearly describes the badge's purpose and behavior.


170-193: LGTM! 30-day window and unique IP counting are correct.

The calculation correctly retrieves 30 days of data (today minus 29 days through today) and properly counts distinct IPs. The zero-day padding logic ensures all 30 days are represented in the chart.


202-220: LGTM! Chart layout calculations are correct.

The reserved title space (50px), bar width (chart_width/32), and bar spacing (chart_width/30) calculations properly accommodate 30 bars with appropriate margins.


222-241: LGTM! Grid and bar rendering logic is correct.

The grid lines are properly offset for the reserved title space, and the bar rendering correctly handles both non-zero days (proportional bars) and zero days (faint 2px bars).


243-253: LGTM! Text rendering is correct.

The text is properly centered, positioned at the top within the reserved space, and uses an appropriate font size.


257-257: LGTM! PNG optimization is correct.

Using optimize=True and compress_level=9 are appropriate for PNG format and improve performance as intended.


1851-1853: LGTM!

The docstring clearly describes the badge's purpose and behavior for repositories.


1899-1923: LGTM! 30-day window and unique IP counting are correct.

The calculation correctly retrieves 30 days of data and properly counts distinct IPs, mirroring the ProjectBadgeView implementation.


1936-1951: LGTM! Chart layout calculations are correct.

The layout calculations properly mirror the ProjectBadgeView implementation with appropriate spacing and margins.


1952-1971: LGTM! Grid and bar rendering logic is correct.

The rendering logic properly handles both non-zero and zero-day bars, consistent with the ProjectBadgeView implementation.


1973-1985: LGTM! Text rendering is correct.

The text is properly centered and positioned to avoid overlapping with the chart bars.


1989-1989: LGTM! PNG optimization is correct.

The PNG save options properly use optimize=True and compress_level=9 for efficient output.

@Yajush-afk
Copy link
Contributor Author

@DonnieBLT this CI/CD Optimized / Run Tests (pull_request) test failure is caused by Selenium not finding an expected HTML element in the test DOM, not by any of the backend code. Please look into it.

@DonnieBLT DonnieBLT merged commit 2d9030d into OWASP-BLT:main Oct 15, 2025
19 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

the github badge is not looking right

3 participants