Utility to Help Enforce Naming Conventions in File Archives

I used Thaura to write this. It traverses directories and lists files that don’t match a simple naming convention. The naming conventions are applied based on config files in each directory.

#!/usr/bin/env python3
"""
tree_validator.py
A tool to validate directory naming conventions based on _naming.ini configurations.

This tool descends directory trees, reads _naming.ini files, and applies
the correct validation rules for each directory.
"""

import os
import re
import sys
from pathlib import Path
from typing import List, Pattern

# Pre-compiled regex patterns for better performance
DATE_REGEX: Pattern = re.compile(r'^\d{4}-\d{2}(-\d{2})? ')
YEAR_REGEX: Pattern = re.compile(r'^\d{4} ')
VERSION_REGEX: Pattern = re.compile(r'.* v\d+(-\d+)*(-\d+)*.*$')
PREFIX_REGEX: Pattern = re.compile(r'^\d+ |[A-Z].* ')

def get_files_in_directory(dir_path: Path) -> List[Path]:
    """Get all files and directories in a directory, excluding hidden files and _naming.ini."""
    files = []
    try:
        for item in dir_path.iterdir():
            if item.name == '_naming.ini' or item.name.startswith('.'):
                continue
            files.append(item)
    except PermissionError:
        pass
    return files

def validate_directory(dir_path: Path) -> None:
    """Validate a directory based on its _naming.ini configuration."""
    naming_file = dir_path / '_naming.ini'
    
    if not naming_file.exists():
        return
    
    try:
        config_lines = naming_file.read_text().strip().split('\n')
    except (IOError, UnicodeDecodeError):
        return
    
    for config_line in config_lines:
        config_line = config_line.strip().upper()
        if not config_line:
            continue
            
        files = get_files_in_directory(dir_path)
        
        if config_line == "DATE_PREFIXED":
            invalid_files = [f for f in files if not DATE_REGEX.match(f.name)]
        elif config_line == "YEAR_PREFIXED":
            invalid_files = [f for f in files if not YEAR_REGEX.match(f.name)]
        elif config_line == "VERSION_SUFFIXED":
            invalid_files = [f for f in files if not VERSION_REGEX.match(f.name)]
        elif config_line == "PREFIXED":
            invalid_files = [f for f in files if not PREFIX_REGEX.match(f.name)]
        else:
            continue
            
        for invalid_file in invalid_files:
            print(str(invalid_file))

def traverse_directory(root_path: Path) -> None:
    """Recursively traverse directory tree and validate each directory."""
    for dir_path in root_path.rglob('*'):
        if dir_path.is_dir():
            validate_directory(dir_path)

def main() -> None:
    """Main entry point."""
    if len(sys.argv) != 2:
        print("Usage: tree_validator <directory>")
        print("Validates directory tree based on _naming.ini configurations")
        print("\nTo have a directory's contents checked make a file named")
        print("_naming.ini in the directory, and set the contents to one of")
        print("the following strings: PREFIXED, DATE_PREFIXED, YEAR_PREFIXED,")
        print("VERSION_SUFFIXED.")
        print()
        print("Version numbers are in the format v10-1-2, v10-1, or v6.")
        print("It's similar to semver, but with dashes, not dots."
        sys.exit(1)
    
    target_dir = Path(sys.argv[1])
    
    if not target_dir.is_dir():
        print(f"Directory not found: {target_dir}")
        sys.exit(1)
    
    traverse_directory(target_dir)

if __name__ == "__main__":
    main()
Code language: Python (python)

This is a script to generate test data:

#!/bin/bash
# create_test_files.sh
# Creates test files for validation testing

create_test_dir() {
    local dir="$1"
    local naming_type="$2"
    
    mkdir -p "$dir"
    
    # Create _naming.ini
    echo "$naming_type" > "$dir/_naming.ini"
    
    # Create test files based on naming convention
    case "$naming_type" in
        "DATE_PREFIXED")
            touch "$dir/2023-01-15 Valid File.txt"
            touch "$dir/2023-02 Another Valid.jpg"
            touch "$dir/2023-03-01 Third Valid.pdf"
            touch "$dir/invalid-name.txt"
            touch "$dir/2023-13 Invalid Month.txt"
            touch "$dir/2023-00 Invalid Month.txt"
            touch "$dir/2023-05-32 Invalid Day.txt"
            ;;
        "YEAR_PREFIXED")
            touch "$dir/2023 Valid File.txt"
            touch "$dir/2024 Another Valid.jpg"
            touch "$dir/1999 Old File.pdf"
            touch "$dir/invalid-name.txt"
            touch "$dir/abc123.txt"
            touch "$dir/23-short.txt"
            ;;
        "VERSION_SUFFIXED")
            touch "$dir/Project v1-2-3.txt"
            touch "$dir/Document v10-5-1.jpg"
            touch "$dir/Config v2-0-0.pdf"
            touch "$dir/invalid-name.txt"
            touch "$dir/Project v1-2.txt"
            touch "$dir/Project v1-2-3-4.txt"
            ;;
        "PREFIXED")
            touch "$dir/123 Valid File.txt"
            touch "$dir/ABC Another Valid.jpg"
            touch "$dir/456 Third Valid.pdf"
            touch "$dir/invalid-name.txt"
            touch "$dir/file1.txt"
            touch "$dir/123file.txt"
            ;;
    esac
}

create_test_tree() {
    local root="$1"
    
    # Create directories with different naming conventions
    create_test_dir "$root/project1" "DATE_PREFIXED"
    create_test_dir "$root/project2" "YEAR_PREFIXED"
    create_test_dir "$root/project3" "VERSION_SUFFIXED"
    create_test_dir "$root/project4" "PREFIXED"
    
    # Create nested directory with same convention
    create_test_dir "$root/project1/subdir" "DATE_PREFIXED"
    create_test_dir "$root/project2/subdir" "YEAR_PREFIXED"
    
    # Create some files in root without naming convention
    touch "$root/standalone.txt"
    touch "$root/another_file.jpg"
}

if [[ $# -eq 0 ]]; then
    echo "Usage: $0 <directory>"
    echo "Creates test files for validation testing"
    exit 1
fi

target_dir="$1"

if [[ -d "$target_dir" ]]; then
    echo "Directory already exists: $target_dir"
    exit 1
fi

create_test_tree "$target_dir"
echo "Test files created in: $target_dir"
Code language: PHP (php)