#!/bin/bash # -*- coding: utf-8 -*- # Timestamp: 2026-02-09 # File: scripts/shell/check_project.sh # Purpose: Validate project structure, naming conventions, and references before compilation # Usage: check_project.sh [-h|--help] set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' CYAN='\033[0;36m' DIM='\033[0;90m' BOLD='\033[1m' NC='\033[0m' # Counters PASS_COUNT=0 WARN_COUNT=0 FAIL_COUNT=0 # Help if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then echo "Usage: $(basename "$0") [-h|--help]" echo "" echo "Validate project structure and naming conventions before compilation." echo "" echo "Checks:" echo " 1. Required content files exist" echo " 2. Figure naming conventions (must start with [0-9]+_)" echo " 3. Table naming conventions (must start with [0-9]+_)" printf " 4. Caption content (\\\\caption{} required)\n" echo " 5. Bibliography files" printf " 6. Cross-references (\\\\ref{fig:*}, \\\\ref{tab:*})\n" echo " 7. Float reference order (figures/tables referenced in sequence)" printf " 8. References & citations (\\\\ref{}, \\\\cite{}, \\\\label{})\n" echo "" echo "Naming Rules:" echo " Figures: 01_name.{jpg,png,tif,svg,...} + 01_name.tex (caption)" echo " Panels: 01a_name.jpg (NO caption file for panels)" echo " Tables: 01_name.csv + 01_name.tex (caption)" echo " Bibtex: 00_shared/bib_files/*.bib (bibliography.bib is reserved)" exit 0 fi cd "$PROJECT_ROOT" # -------------------------------------------------------------------------- # Helpers # -------------------------------------------------------------------------- log_pass() { echo -e " ${GREEN}[PASS]${NC} $1" PASS_COUNT=$((PASS_COUNT + 1)) } log_warn() { echo -e " ${YELLOW}[WARN]${NC} $1" WARN_COUNT=$((WARN_COUNT + 1)) } log_fail() { echo -e " ${RED}[FAIL]${NC} $1" FAIL_COUNT=$((FAIL_COUNT + 1)) } log_detail() { echo -e " ${DIM}$1${NC}" } # -------------------------------------------------------------------------- # 1. Required content files # -------------------------------------------------------------------------- check_required_files() { local issues=() local shared_files=( "00_shared/title.tex" "00_shared/authors.tex" "00_shared/keywords.tex" "00_shared/journal_name.tex" ) local manuscript_files=( "01_manuscript/base.tex" "01_manuscript/contents/abstract.tex" "01_manuscript/contents/introduction.tex" "01_manuscript/contents/methods.tex" "01_manuscript/contents/results.tex" "01_manuscript/contents/discussion.tex" ) for f in "${shared_files[@]}" "${manuscript_files[@]}"; do if [ ! -f "$f" ]; then issues+=("missing: $f") fi done if [ ${#issues[@]} -eq 0 ]; then log_pass "Required content files" else log_fail "Required content files" for i in "${issues[@]}"; do log_detail "$i" done fi } # -------------------------------------------------------------------------- # 2. Figure naming validation # -------------------------------------------------------------------------- check_figures() { local doc_dir="$1" local doc_label="$2" local fig_dir="$doc_dir/contents/figures/caption_and_media" if [ ! -d "$fig_dir" ]; then log_pass "Figure naming ($doc_label) - no figures directory" return fi local issues=() local media_exts="tif tiff jpg jpeg png svg mmd pptx pdf" # Check media files that DON'T match required pattern for f in "$fig_dir"/*; do [ -e "$f" ] || continue [ -d "$f" ] && continue # skip directories local fname fname=$(basename "$f") local ext="${fname##*.}" # Skip .tex files (checked separately), hidden files, templates [[ "$fname" == *.tex ]] && continue [[ "$fname" == .* ]] && continue [[ "$fname" == templates ]] && continue # Check if extension is a media type local is_media=false for mext in $media_exts; do [[ "$ext" == "$mext" ]] && is_media=true && break done $is_media || continue # Must start with digits if ! [[ "$fname" =~ ^[0-9] ]]; then issues+=("$fname: does not start with digits (will be ignored by compilation)") fi done # Check main figures have caption files (skip panels) for f in "$fig_dir"/[0-9]*; do [ -e "$f" ] || continue [ -d "$f" ] && continue local fname fname=$(basename "$f") local ext="${fname##*.}" [[ "$ext" == "tex" ]] && continue local is_media=false for mext in $media_exts; do [[ "$ext" == "$mext" ]] && is_media=true && break done $is_media || continue local base="${fname%.*}" # Skip panels (they must NOT have captions) if [[ "$base" =~ ^[0-9]+[a-zA-Z]_ ]]; then # Check if panel incorrectly has a caption if [ -f "$fig_dir/${base}.tex" ]; then issues+=("$base.tex: panel caption file (will be auto-deleted during compilation)") fi continue fi # Main figure: must have caption if [ ! -f "$fig_dir/${base}.tex" ]; then issues+=("$fname: missing caption file ${base}.tex") fi done # Check for orphaned caption files (tex with no media) for f in "$fig_dir"/[0-9]*.tex; do [ -e "$f" ] || continue local fname fname=$(basename "$f") local base="${fname%.tex}" # Skip panel patterns [[ "$base" =~ ^[0-9]+[a-zA-Z]_ ]] && continue local has_media=false for mext in $media_exts; do [ -f "$fig_dir/${base}.${mext}" ] && has_media=true && break done if ! $has_media; then issues+=("$fname: orphaned caption (no matching media file)") fi done if [ ${#issues[@]} -eq 0 ]; then log_pass "Figure naming ($doc_label)" else log_warn "Figure naming ($doc_label)" for i in "${issues[@]}"; do log_detail "$i" done fi } # -------------------------------------------------------------------------- # 3. Table naming validation # -------------------------------------------------------------------------- check_tables() { local doc_dir="$1" local doc_label="$2" local tbl_dir="$doc_dir/contents/tables/caption_and_media" if [ ! -d "$tbl_dir" ]; then log_pass "Table naming ($doc_label) - no tables directory" return fi local issues=() # Check CSV/XLSX files that DON'T match required pattern for f in "$tbl_dir"/*.{csv,xlsx,xls}; do [ -e "$f" ] || continue local fname fname=$(basename "$f") if ! [[ "$fname" =~ ^[0-9] ]]; then issues+=("$fname: does not start with digits (will be ignored by compilation)") fi done # Check data files have caption files for f in "$tbl_dir"/[0-9]*.{csv,xlsx,xls}; do [ -e "$f" ] || continue local fname fname=$(basename "$f") local base="${fname%.*}" if [ ! -f "$tbl_dir/${base}.tex" ]; then issues+=("$fname: missing caption file ${base}.tex") fi done # Check for orphaned caption files for f in "$tbl_dir"/[0-9]*.tex; do [ -e "$f" ] || continue local fname fname=$(basename "$f") local base="${fname%.tex}" local has_data=false for dext in csv xlsx xls; do [ -f "$tbl_dir/${base}.${dext}" ] && has_data=true && break done if ! $has_data; then issues+=("$fname: orphaned caption (no matching CSV/XLSX)") fi done if [ ${#issues[@]} -eq 0 ]; then log_pass "Table naming ($doc_label)" else log_warn "Table naming ($doc_label)" for i in "${issues[@]}"; do log_detail "$i" done fi } # -------------------------------------------------------------------------- # 4. Caption content validation # -------------------------------------------------------------------------- check_captions() { local doc_dir="$1" local doc_label="$2" local issues=() # Figure captions local fig_dir="$doc_dir/contents/figures/caption_and_media" if [ -d "$fig_dir" ]; then for f in "$fig_dir"/[0-9]*.tex; do [ -e "$f" ] || continue local fname fname=$(basename "$f") local base="${fname%.tex}" # Skip panels [[ "$base" =~ ^[0-9]+[a-zA-Z]_ ]] && continue if ! grep -q '\\caption{' "$f" 2>/dev/null; then issues+=("figures/$fname: missing \\caption{}") fi done fi # Table captions local tbl_dir="$doc_dir/contents/tables/caption_and_media" if [ -d "$tbl_dir" ]; then for f in "$tbl_dir"/[0-9]*.tex; do [ -e "$f" ] || continue local fname fname=$(basename "$f") if ! grep -q '\\caption{' "$f" 2>/dev/null; then issues+=("tables/$fname: missing \\caption{}") fi done fi if [ ${#issues[@]} -eq 0 ]; then log_pass "Caption content ($doc_label)" else log_warn "Caption content ($doc_label)" for i in "${issues[@]}"; do log_detail "$i" done fi } # -------------------------------------------------------------------------- # 5. Bibliography validation # -------------------------------------------------------------------------- check_bibliography() { local bib_dir="00_shared/bib_files" local issues=() if [ ! -d "$bib_dir" ]; then log_fail "Bibliography files" log_detail "directory not found: $bib_dir" return fi # Count non-reserved bib files local source_count=0 for f in "$bib_dir"/*.bib; do [ -e "$f" ] || continue local fname fname=$(basename "$f") [[ "$fname" == "bibliography.bib" ]] && continue [[ "$fname" == "merged_all.bib" ]] && continue [[ "$fname" == "enriched_all.bib" ]] && continue [[ "$fname" == "enriched_all_v2.bib" ]] && continue source_count=$((source_count + 1)) done local total_bib=0 for f in "$bib_dir"/*.bib; do [ -e "$f" ] || continue total_bib=$((total_bib + 1)) done if [ "$total_bib" -eq 0 ]; then issues+=("no .bib files found in $bib_dir") elif [ "$source_count" -eq 0 ] && [ -f "$bib_dir/bibliography.bib" ]; then # Only bibliography.bib exists - that's fine for a template or simple project : fi if [ ${#issues[@]} -eq 0 ]; then log_pass "Bibliography files ($total_bib file(s))" else log_fail "Bibliography files" for i in "${issues[@]}"; do log_detail "$i" done fi } # -------------------------------------------------------------------------- # 6. Cross-reference check (sourced from module) # -------------------------------------------------------------------------- # shellcheck disable=SC1091 source "$SCRIPT_DIR/modules/check_crossrefs.sh" # -------------------------------------------------------------------------- # 7-8. Python-based checks (float order, references/citations) # -------------------------------------------------------------------------- run_python_check() { local script="$1" [ -f "$script" ] && command -v python3 &>/dev/null || return 0 local output msg clean output=$(python3 "$script" "$PROJECT_ROOT" 2>&1) || true while IFS= read -r line; do # Strip ANSI codes from Python output before re-wrapping clean=$(printf '%s' "$line" | sed 's/\x1b\[[0-9;]*m//g') case "$clean" in *"[PASS]"*) msg="${clean##*\[PASS\] }" log_pass "$msg" ;; *"[FAIL]"*) msg="${clean##*\[FAIL\] }" log_fail "$msg" ;; *"[WARN]"*) msg="${clean##*\[WARN\] }" log_warn "$msg" ;; *"[INFO]"*) msg="${clean##*\[INFO\] }" echo -e " ${DIM}[INFO] ${msg}${NC}" ;; " "*) msg="${clean#"${clean%%[! ]*}"}" log_detail "$msg" ;; esac done <<<"$output" } # -------------------------------------------------------------------------- # Main # -------------------------------------------------------------------------- echo "" echo -e "${CYAN}${BOLD}=== SciTeX Writer: Project Check ===${NC}" echo "" check_required_files # Check each document type for doc_dir in 01_manuscript 02_supplementary; do doc_label="${doc_dir#[0-9]*_}" if [ -d "$doc_dir/contents" ]; then check_figures "$doc_dir" "$doc_label" check_tables "$doc_dir" "$doc_label" check_captions "$doc_dir" "$doc_label" check_crossrefs "$doc_dir" "$doc_label" fi done check_bibliography run_python_check "$SCRIPT_DIR/../python/check_float_order.py" run_python_check "$SCRIPT_DIR/../python/check_references.py" # Summary echo "" echo -e "${BOLD}Summary:${NC} ${GREEN}${PASS_COUNT} passed${NC}, ${YELLOW}${WARN_COUNT} warnings${NC}, ${RED}${FAIL_COUNT} errors${NC}" if [ "$FAIL_COUNT" -gt 0 ]; then echo "" echo -e "${RED}Fix errors before compilation.${NC}" exit 1 elif [ "$WARN_COUNT" -gt 0 ]; then echo "" echo -e "${YELLOW}Warnings may cause issues during compilation.${NC}" exit 0 else echo "" echo -e "${GREEN}All checks passed.${NC}" exit 0 fi ###% EOF