Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "gitfetch"
version = "1.0.17"
version = "1.0.18"
description = "A neofetch-style CLI tool for GitHub statistics"
readme = "README.md"
requires-python = ">=3.8"
Expand Down
37 changes: 21 additions & 16 deletions src/gitfetch/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@
from datetime import datetime
from .config import ConfigManager


class DisplayFormatter:
"""Formats and displays GitHub stats in a neofetch-style layout."""

def __init__(self,config_manager: ConfigManager):
def __init__(self, config_manager: ConfigManager):
"""Initialize the display formatter."""
self.terminal_width = shutil.get_terminal_size().columns
self.enable_color = sys.stdout.isatty()
self.colors = config_manager.get_colors()

def display(self, username: str, user_data: Dict[str, Any],
stats: Dict[str, Any],spaced=True) -> None:
stats: Dict[str, Any], spaced=True) -> None:
"""
Display GitHub statistics in neofetch style.

Expand All @@ -35,13 +36,13 @@ def display(self, username: str, user_data: Dict[str, Any],

if layout == 'minimal':
# Only show contribution graph
self._display_minimal(username, stats,spaced)
self._display_minimal(username, stats, spaced)
elif layout == 'compact':
# Show graph and key info
self._display_compact(username, user_data, stats,spaced)
self._display_compact(username, user_data, stats, spaced)
else:
# Full layout with all sections
self._display_full(username, user_data, stats,spaced)
self._display_full(username, user_data, stats, spaced)

print() # Empty line at the end

Expand All @@ -68,11 +69,11 @@ def _display_minimal(self, username: str, stats: Dict[str, Any], spaced=True) ->
print(line)

def _display_compact(self, username: str, user_data: Dict[str, Any],
stats: Dict[str, Any], spaced= True) -> None:
"""Display graph and minimal info side-by-side."""
stats: Dict[str, Any], spaced=True) -> None:
"""Display graph and minimal info side-by-side (no languages)."""
contrib_graph = stats.get('contribution_graph', [])
recent_weeks = self._get_recent_weeks(contrib_graph)
graph_width = max(40, (self.terminal_width - 10) // 2)
graph_width = max(40, (self.terminal_width - 40) // 2)
graph_lines = self._get_contribution_graph_lines(
contrib_graph,
username,
Expand All @@ -84,7 +85,6 @@ def _display_compact(self, username: str, user_data: Dict[str, Any],
info_lines = self._format_user_info_compact(user_data, stats)
achievements = self._build_achievements(recent_weeks)

# Combine sections
right_side = list(info_lines)
if achievements:
right_side.append("")
Expand All @@ -96,7 +96,6 @@ def _display_compact(self, username: str, user_data: Dict[str, Any],
graph_part = (graph_lines[i] if i < len(graph_lines) else "")
graph_len = self._display_width(graph_part)
padding = " " * max(0, graph_width - graph_len)

info_part = (right_side[i] if i < len(right_side) else "")
print(f"{graph_part}{padding} {info_part}")

Expand All @@ -116,11 +115,17 @@ def _display_full(self, username: str, user_data: Dict[str, Any],
pull_request_lines = self._format_pull_requests(stats)
issue_lines = self._format_issues(stats)

section_columns = [
pull_request_lines,
issue_lines,
]

# Only show PR/Issue columns if they fit side by side, otherwise show neither
section_columns = []
if pull_request_lines and issue_lines:
pr_width = max((self._display_width(line)
for line in pull_request_lines), default=0)
issue_width = max((self._display_width(line)
for line in issue_lines), default=0)
total_width = pr_width + issue_width + len(" ") # gap
if total_width <= graph_width:
section_columns = [pull_request_lines, issue_lines]
# Do not show only one column; only show both if they fit
combined_sections = self._combine_section_grid(
section_columns, width_limit=graph_width
)
Expand All @@ -132,7 +137,7 @@ def _display_full(self, username: str, user_data: Dict[str, Any],
language_lines = self._format_languages(stats)

right_side = list(info_lines)
if language_lines:
if language_lines and self.terminal_width >= 120:
right_side.append("")
right_side.extend(language_lines)

Expand Down
21 changes: 18 additions & 3 deletions src/gitfetch/fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,28 @@ def _calculate_language_stats(self, repos: list) -> Dict[str, float]:
Returns:
Dictionary mapping language names to percentages
"""
language_counts: Dict[str, int] = {}
from collections import defaultdict

# First pass: collect all language occurrences with their casing
language_occurrences: Dict[str, Dict[str, int]] = defaultdict(
lambda: defaultdict(int))

for repo in repos:
language = repo.get('language')
if language:
language_counts[language] = language_counts.get(
language, 0) + 1
# Group by lowercase name, but keep track of different casings
normalized = language.lower()
language_occurrences[normalized][language] += 1

# Second pass: choose canonical casing (most frequent) and sum counts
language_counts: Dict[str, int] = {}

for normalized, casings in language_occurrences.items():
# Find the most common casing
canonical_name = max(casings.items(), key=lambda x: x[1])[0]
# Sum all occurrences for this language
total_count = sum(casings.values())
language_counts[canonical_name] = total_count

# Calculate percentages
total = sum(language_counts.values())
Expand Down