gateway/scripts/script_stats_get_codelines.py
2026-01-21 10:34:42 +01:00

142 lines
No EOL
4.9 KiB
Python

#!/usr/bin/env python3
# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Script to count code lines in a folder and its subfolders.
Reports lines per file for Python, JavaScript, and CSS files,
and calculates the total lines of code.
"""
import os
import argparse
from pathlib import Path
from typing import Dict, List, Tuple
# File extensions to count
FILE_TYPES = {
'python': ['.py'],
'javascript': ['.js', '.jsx', '.ts', '.tsx'],
'css': ['.css', '.scss', '.sass', '.less'],
'html': ['.htm','.html']
}
def count_lines(file_path: str) -> int:
"""Count the number of non-empty lines in a file."""
try:
with open(file_path, 'r', encoding='utf-8') as file:
# Count non-empty lines (strip whitespace)
return sum(1 for line in file if line.strip())
except UnicodeDecodeError:
# Skip files that can't be decoded as UTF-8
print(f"Warning: Could not read {file_path} as text. Skipping.")
return 0
except Exception as e:
print(f"Error reading {file_path}: {e}")
return 0
def get_file_type(file_path: str) -> str:
"""Determine the file type based on extension."""
extension = os.path.splitext(file_path)[1].lower()
for file_type, extensions in FILE_TYPES.items():
if extension in extensions:
return file_type
return "other"
def scan_directory(directory: str, exclude_dirs: List[str] = None) -> Tuple[Dict[str, List[Tuple[str, int]]], Dict[str, int]]:
"""
Scan a directory and its subdirectories for code files and count lines.
Args:
directory: The directory to scan
exclude_dirs: List of directory names to exclude (e.g., ['node_modules', '.git'])
Returns:
Tuple containing:
- Dictionary mapping file types to lists of (file_path, line_count) tuples
- Dictionary mapping file types to total line counts
"""
if exclude_dirs is None:
exclude_dirs = ['node_modules', '.git', 'venv', 'env', '__pycache__', '.vscode', '.idea']
# Initialize results
files_by_type = {file_type: [] for file_type in FILE_TYPES.keys()}
files_by_type["other"] = []
totals_by_type = {file_type: 0 for file_type in FILE_TYPES.keys()}
totals_by_type["other"] = 0
# Walk through directory
for root, dirs, files in os.walk(directory):
# Skip excluded directories
dirs[:] = [d for d in dirs if d not in exclude_dirs]
for file in files:
file_path = os.path.join(root, file)
file_type = get_file_type(file_path)
# Count lines if it's a type we're interested in
if file_type in files_by_type:
line_count = count_lines(file_path)
# Add to files list
files_by_type[file_type].append((file_path, line_count))
# Add to total
totals_by_type[file_type] += line_count
return files_by_type, totals_by_type
def main():
"""Main function."""
parser = argparse.ArgumentParser(description='Count code lines in a directory.')
parser.add_argument('directory', type=str, nargs='?', default='.',
help='Directory to scan (default: current directory)')
parser.add_argument('--exclude', type=str, nargs='+',
default=['node_modules', '.git', 'venv', 'env', '__pycache__', '.vscode', '.idea'],
help='Directories to exclude')
args = parser.parse_args()
directory = args.directory
exclude_dirs = args.exclude
print(f"Scanning directory: {os.path.abspath(directory)}")
print(f"Excluding directories: {', '.join(exclude_dirs)}")
print("\nCounting lines of code...\n")
# Scan directory
files_by_type, totals_by_type = scan_directory(directory, exclude_dirs)
# Calculate total lines
total_lines = sum(totals_by_type.values())
# Print results by file type
for file_type in sorted(files_by_type.keys()):
if file_type == "other" and not files_by_type["other"]:
continue
files = files_by_type[file_type]
if not files:
continue
print(f"\n{file_type.upper()} FILES ({totals_by_type[file_type]} total lines):")
print("-" * 80)
# Sort files by line count (descending)
for file_path, line_count in sorted(files, key=lambda x: x[1], reverse=True):
relative_path = os.path.relpath(file_path, directory)
print(f"{relative_path}: {line_count} lines")
# Print summary
print("\nSUMMARY:")
print("-" * 80)
for file_type in sorted(totals_by_type.keys()):
if totals_by_type[file_type] > 0:
print(f"{file_type.upper()}: {totals_by_type[file_type]} lines")
print("-" * 80)
print(f"TOTAL LINES OF CODE: {total_lines}")
if __name__ == '__main__':
main()