pdftract/scripts/validate-corpus.sh
jedarden a0c3ebb237 feat(bf-5for4): design manifest schema for grep-corpus
- Add manifest.csv with header row: filename,source_url,page_count,file_size,checksum,license
- Update README.md with comprehensive schema documentation and field descriptions
- Schema supports provenance tracking, integrity verification, and license compliance

Closes bf-5for4
2026-07-05 12:21:25 -04:00

214 lines
5.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# validate-corpus.sh - Validate the grep-corpus benchmark corpus
#
# This script validates the corpus by:
# - Verifying all files in manifest exist
# - Checking file counts and sizes match targets
# - Validating checksums
# - Reporting on corpus quality metrics
#
# Usage:
# cd /home/coding/pdftract
# ./scripts/validate-corpus.sh
set -eo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
CORPUS_DIR="$PROJECT_ROOT/tests/fixtures/grep-corpus/corpus"
MANIFEST="$PROJECT_ROOT/tests/fixtures/grep-corpus/manifest.csv"
# Create a temp file for non-comment manifest data (avoid SIGPIPE with process substitution)
MANIFEST_TMP=$(mktemp)
trap "rm -f '$MANIFEST_TMP'" EXIT
sed '/^#/d' "$MANIFEST" > "$MANIFEST_TMP"
# Targets
TARGET_COUNT=1000
MIN_SIZE_MB=50
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[✓]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if corpus directory exists
if [[ ! -d "$CORPUS_DIR" ]]; then
log_error "Corpus directory not found: $CORPUS_DIR"
exit 1
fi
# Check if manifest exists
if [[ ! -f "$MANIFEST" ]]; then
log_error "Manifest file not found: $MANIFEST"
exit 1
fi
log_info "Validating grep-corpus at $CORPUS_DIR"
echo ""
# Count entries in manifest
MANIFEST_COUNT=$(grep -c '.' "$MANIFEST_TMP" || echo "0")
log_info "Manifest entries: $MANIFEST_COUNT"
# Count actual PDF files
ACTUAL_COUNT=$(find "$CORPUS_DIR" -name "*.pdf" -type f | wc -l)
log_info "Actual PDF files: $ACTUAL_COUNT"
# Get total size
TOTAL_SIZE_MB=$(du -sm "$CORPUS_DIR" 2>/dev/null | cut -f1 || echo "0")
log_info "Total corpus size: ${TOTAL_SIZE_MB} MB"
echo ""
log_info "=== Validation Results ==="
# Validate file count
errors=0
warnings=0
if [[ $ACTUAL_COUNT -lt $TARGET_COUNT ]]; then
log_error "File count below target: $ACTUAL_COUNT < $TARGET_COUNT"
((errors++))
else
log_success "File count meets target: $ACTUAL_COUNT$TARGET_COUNT"
fi
# Validate size
if [[ $TOTAL_SIZE_MB -lt $MIN_SIZE_MB ]]; then
log_error "Corpus size below target: ${TOTAL_SIZE_MB} MB < ${MIN_SIZE_MB} MB"
((errors++))
else
log_success "Corpus size meets target: ${TOTAL_SIZE_MB} MB ≥ ${MIN_SIZE_MB} MB"
fi
# Validate manifest entries
echo ""
log_info "Validating manifest entries..."
missing_files=0
invalid_checksums=0
processed=0
# Read manifest and validate each file
while IFS=',' read -r filename source_url page_count file_size checksum license; do
# Skip empty lines
[[ -z "$filename" ]] && continue
((processed++))
pdf_file="$CORPUS_DIR/$filename"
# Check if file exists
if [[ ! -f "$pdf_file" ]]; then
log_warn "Missing file: $filename"
((missing_files++))
continue
fi
# Validate file size
actual_size=$(stat -c%s "$pdf_file" 2>/dev/null || echo "0")
if [[ "$actual_size" != "$file_size" ]]; then
log_warn "Size mismatch for $filename: manifest=$file_size, actual=$actual_size"
((warnings++))
fi
# Validate checksum
if [[ "$checksum" != "unknown" ]]; then
actual_checksum=$(sha256sum "$pdf_file" 2>/dev/null | awk '{print $1}')
if [[ "$actual_checksum" != "$checksum" ]]; then
log_error "Checksum mismatch for $filename"
((invalid_checksums++))
((errors++))
fi
fi
# Progress indicator
if [[ $((processed % 200)) -eq 0 ]]; then
echo "Processed $processed entries..."
fi
done < "$MANIFEST_TMP"
echo ""
# Report missing files
if [[ $missing_files -gt 0 ]]; then
log_error "Missing files: $missing_files"
((errors++))
else
log_success "All manifest files exist"
fi
# Report invalid checksums
if [[ $invalid_checksums -gt 0 ]]; then
log_error "Invalid checksums: $invalid_checksums"
else
log_success "All checksums valid"
fi
# Quality metrics
echo ""
log_info "=== Corpus Quality Metrics ==="
# Calculate average file size
if [[ $ACTUAL_COUNT -gt 0 ]]; then
avg_size=$((TOTAL_SIZE_MB * 1024 / ACTUAL_COUNT))
log_info "Average file size: ${avg_size} KB"
fi
# Check for page count data
files_with_pages=0
while IFS=',' read -r filename source_url page_count file_size checksum license; do
[[ -z "$filename" ]] && continue
if [[ -n "$page_count" && "$page_count" != "0" ]]; then
((files_with_pages++))
fi
done < "$MANIFEST_TMP"
log_info "Files with page count data: $files_with_pages / $MANIFEST_COUNT"
# Check source distribution
echo ""
log_info "Source distribution:"
echo "$MANIFEST_DATA" | cut -d',' -f2 | sort | uniq -c | sort -rn | while read -r count source; do
printf " %-30s %d files\n" "$source" "$count"
done
# Check license distribution
echo ""
log_info "License distribution:"
echo "$MANIFEST_DATA" | cut -d',' -f6 | sort | uniq -c | sort -rn | while read -r count license; do
printf " %-30s %d files\n" "$license" "$count"
done
# Final summary
echo ""
log_info "=== Summary ==="
if [[ $errors -eq 0 ]]; then
log_success "✓ Corpus validation passed"
log_success "Files: $ACTUAL_COUNT, Size: ${TOTAL_SIZE_MB} MB, Warnings: $warnings"
exit 0
else
log_error "✗ Corpus validation failed"
log_error "Errors: $errors, Warnings: $warnings"
exit 1
fi