Spaces:
Sleeping
Sleeping
| # Script tự động tổ chức các file markdown theo ngày tạo và category | |
| # Chạy: bash backend/scripts/organize_markdowns.sh [--dry-run] [--backup] | |
| set -e | |
| # Parse arguments | |
| DRY_RUN=false | |
| BACKUP=false | |
| for arg in "$@"; do | |
| case $arg in | |
| --dry-run) | |
| DRY_RUN=true | |
| shift | |
| ;; | |
| --backup) | |
| BACKUP=true | |
| shift | |
| ;; | |
| *) | |
| ;; | |
| esac | |
| done | |
| PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" | |
| REPORTS_DIR="${PROJECT_ROOT}/tài nguyên/báo cáo" | |
| DOCS_DIR="${PROJECT_ROOT}/backend/docs" | |
| echo "📁 Tổ chức file markdown theo ngày và category..." | |
| if [ "$DRY_RUN" = true ]; then | |
| echo "🔍 DRY-RUN MODE: Chỉ preview, không di chuyển file" | |
| fi | |
| echo "" | |
| # Hàm xác định ngày tạo file | |
| get_file_creation_date() { | |
| local file="$1" | |
| local git_date="" | |
| local birth_date="" | |
| local mod_date="" | |
| local meta_date="" | |
| local final_date="" | |
| # Git history | |
| if command -v git &> /dev/null && [ -d "${PROJECT_ROOT}/.git" ]; then | |
| git_date=$(cd "$PROJECT_ROOT" && git log --diff-filter=A --format="%ai" -- "$file" 2>/dev/null | tail -1 | cut -d' ' -f1) | |
| if [ -z "$git_date" ] || [[ ! "$git_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then | |
| git_date="" | |
| fi | |
| fi | |
| # File birth time | |
| if [[ "$OSTYPE" == "darwin"* ]]; then | |
| birth_date=$(stat -f "%SB" -t "%Y-%m-%d" "$file" 2>/dev/null || stat -f "%Sm" -t "%Y-%m-%d" -B "$file" 2>/dev/null || echo "") | |
| if [ -z "$birth_date" ]; then | |
| birth_date=$(stat -f "%Sm" -t "%Y-%m-%d" "$file" 2>/dev/null || echo "") | |
| fi | |
| else | |
| birth_date=$(stat -c "%y" "$file" 2>/dev/null | cut -d' ' -f1 || echo "") | |
| fi | |
| # File modified time | |
| if [[ "$OSTYPE" == "darwin"* ]]; then | |
| mod_date=$(stat -f "%Sm" -t "%Y-%m-%d" "$file" 2>/dev/null || echo "") | |
| else | |
| mod_date=$(stat -c "%y" "$file" 2>/dev/null | cut -d' ' -f1 || echo "") | |
| fi | |
| # Metadata trong file | |
| if [ -f "$file" ]; then | |
| meta_date=$(grep -iE "(created|date|ngày):\s*[0-9]{4}-[0-9]{2}-[0-9]{2}" "$file" 2>/dev/null | head -1 | grep -oE "[0-9]{4}-[0-9]{2}-[0-9]{2}" | head -1 || echo "") | |
| fi | |
| # Sử dụng ngày đầu tiên tìm được | |
| if [ -n "$git_date" ]; then | |
| final_date="$git_date" | |
| elif [ -n "$birth_date" ]; then | |
| final_date="$birth_date" | |
| elif [ -n "$mod_date" ]; then | |
| final_date="$mod_date" | |
| elif [ -n "$meta_date" ]; then | |
| final_date="$meta_date" | |
| else | |
| final_date=$(date +"%Y-%m-%d") | |
| fi | |
| # Validate format | |
| if [[ ! "$final_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then | |
| final_date=$(date +"%Y-%m-%d") | |
| fi | |
| echo "$final_date" | |
| } | |
| # Hàm detect category từ tên file và nội dung | |
| detect_category() { | |
| local file="$1" | |
| local filename=$(basename "$file" | tr '[:upper:]' '[:lower:]') | |
| local category="" | |
| # Ưu tiên 1: Metadata trong file | |
| if [ -f "$file" ]; then | |
| meta_category=$(grep -iE "category:\s*[a-z]+" "$file" 2>/dev/null | head -1 | grep -oE "category:\s*([a-z_]+)" | cut -d: -f2 | tr -d ' ' || echo "") | |
| if [ -n "$meta_category" ]; then | |
| echo "$meta_category" | |
| return | |
| fi | |
| fi | |
| # Ưu tiên 2: Tên file | |
| declare -A category_keywords | |
| category_keywords["database"]="database|postgresql|mysql|mongodb|redis|db_|_db|sql" | |
| category_keywords["backend"]="backend|api|server|django|flask|fastapi|be_|_be|endpoint" | |
| category_keywords["frontend"]="frontend|ui|react|vue|angular|component|fe_|_fe|interface" | |
| category_keywords["devops"]="devops|docker|kubernetes|ci/cd|deploy|jenkins|terraform" | |
| category_keywords["ml"]="ml|ai|model|training|neural|tensorflow|pytorch|embedding" | |
| category_keywords["plan"]="plan|roadmap|planning|strategy|milestone" | |
| category_keywords["setup"]="setup|config|install|installation|guide|tutorial" | |
| local max_matches=0 | |
| for cat in "${!category_keywords[@]}"; do | |
| matches=$(echo "$filename" | grep -oiE "${category_keywords[$cat]}" | wc -l | tr -d ' ') | |
| if [ "$matches" -gt "$max_matches" ]; then | |
| max_matches=$matches | |
| category="$cat" | |
| fi | |
| done | |
| # Ưu tiên 3: Nội dung file (100 dòng đầu) | |
| if [ -z "$category" ] && [ -f "$file" ]; then | |
| local content=$(head -100 "$file" | tr '[:upper:]' '[:lower:]') | |
| max_matches=0 | |
| for cat in "${!category_keywords[@]}"; do | |
| matches=$(echo "$content" | grep -oiE "${category_keywords[$cat]}" | wc -l | tr -d ' ') | |
| if [ "$matches" -gt "$max_matches" ]; then | |
| max_matches=$matches | |
| category="$cat" | |
| fi | |
| done | |
| fi | |
| # Fallback | |
| if [ -z "$category" ]; then | |
| category="general" | |
| fi | |
| echo "$category" | |
| } | |
| # Hàm tạo folder và README nếu chưa có | |
| ensure_category_folder() { | |
| local date_folder="$1" | |
| local category="$2" | |
| local category_folder="${date_folder}/${category}" | |
| if [ ! -d "$category_folder" ]; then | |
| if [ "$DRY_RUN" = false ]; then | |
| mkdir -p "$category_folder" | |
| echo "✅ Đã tạo folder: $category_folder" | |
| # Tạo README.md trong folder category | |
| cat > "${category_folder}/README.md" << EOF | |
| # Tài liệu ${category} - $(basename "$date_folder") | |
| Folder này chứa các file markdown về **${category}** được tạo trong ngày $(basename "$date_folder"). | |
| ## Danh sách file | |
| $(find "$category_folder" -maxdepth 1 -name "*.md" ! -name README.md -type f | sed 's|.*/||' | sort | sed 's/^/- /') | |
| ## Category | |
| ${category} | |
| ## Ngày tạo | |
| $(basename "$date_folder") | |
| EOF | |
| else | |
| echo "🔍 [DRY-RUN] Sẽ tạo folder: $category_folder" | |
| fi | |
| fi | |
| } | |
| # Hàm di chuyển file markdown | |
| move_markdown() { | |
| local source_file="$1" | |
| local target_dir="$2" | |
| local filename=$(basename "$source_file") | |
| local target_file="${target_dir}/${filename}" | |
| if [ -f "$target_file" ]; then | |
| if cmp -s "$source_file" "$target_file" 2>/dev/null; then | |
| echo "⚠️ File trùng nội dung: $target_file (xóa file gốc)" | |
| if [ "$DRY_RUN" = false ]; then | |
| rm "$source_file" | |
| fi | |
| return 1 | |
| else | |
| echo "⚠️ File đã tồn tại nhưng khác nội dung: $target_file" | |
| return 1 | |
| fi | |
| fi | |
| if [ "$BACKUP" = true ] && [ "$DRY_RUN" = false ]; then | |
| backup_dir="${PROJECT_ROOT}/.backup/markdowns/$(date +%Y%m%d_%H%M%S)" | |
| mkdir -p "$backup_dir" | |
| cp "$source_file" "${backup_dir}/${filename}" | |
| fi | |
| if [ "$DRY_RUN" = false ]; then | |
| mv "$source_file" "$target_file" | |
| echo "✅ Đã di chuyển: $filename → $target_dir" | |
| else | |
| echo "🔍 [DRY-RUN] Sẽ di chuyển: $filename → $target_dir" | |
| fi | |
| return 0 | |
| } | |
| # Xử lý các file trong backend/docs | |
| if [ -d "$DOCS_DIR" ]; then | |
| echo "🔍 Tìm file markdown trong: $DOCS_DIR" | |
| find "$DOCS_DIR" -maxdepth 1 -name "*.md" -type f | while read -r file; do | |
| if [ -f "$file" ]; then | |
| filename=$(basename "$file") | |
| echo "" | |
| echo "📄 Xử lý: $filename" | |
| # Xác định ngày tạo | |
| date_created=$(get_file_creation_date "$file") | |
| echo " 📅 Ngày: $date_created" | |
| # Detect category | |
| category=$(detect_category "$file") | |
| echo " 📂 Category: $category" | |
| date_folder="${REPORTS_DIR}/${date_created}" | |
| ensure_category_folder "$date_folder" "$category" | |
| # Di chuyển file | |
| move_markdown "$file" "${date_folder}/${category}" | |
| fi | |
| done | |
| fi | |
| # Tìm các file markdown lộn xộn khác | |
| echo "" | |
| echo "🔍 Tìm file markdown lộn xộn khác..." | |
| EXCLUDE_DIRS=( | |
| "node_modules" | |
| "ops" | |
| "chatbot/training" | |
| ".git" | |
| ".venv" | |
| "__pycache__" | |
| ".cursor" | |
| "tài nguyên/báo cáo" | |
| ".backup" | |
| ) | |
| find "$PROJECT_ROOT" -name "*.md" -type f | while read -r file; do | |
| skip=false | |
| for exclude in "${EXCLUDE_DIRS[@]}"; do | |
| if [[ "$file" == *"$exclude"* ]]; then | |
| skip=true | |
| break | |
| fi | |
| done | |
| if [[ "$file" == *"tài nguyên/báo cáo"* ]]; then | |
| skip=true | |
| fi | |
| if [ "$skip" = true ]; then | |
| continue | |
| fi | |
| filename=$(basename "$file") | |
| echo "" | |
| echo "📄 Xử lý: $filename" | |
| # Xác định ngày tạo | |
| date_created=$(get_file_creation_date "$file") | |
| echo " 📅 Ngày: $date_created" | |
| # Detect category | |
| category=$(detect_category "$file") | |
| echo " 📂 Category: $category" | |
| date_folder="${REPORTS_DIR}/${date_created}" | |
| ensure_category_folder "$date_folder" "$category" | |
| # Di chuyển file | |
| move_markdown "$file" "${date_folder}/${category}" | |
| done | |
| echo "" | |
| echo "✅ Hoàn tất tổ chức file markdown!" | |
| echo "" | |
| echo "📊 Thống kê:" | |
| for date_folder in "${REPORTS_DIR}"/20*; do | |
| if [ -d "$date_folder" ]; then | |
| date_name=$(basename "$date_folder") | |
| echo " 📁 $date_name:" | |
| for category_folder in "${date_folder}"/*; do | |
| if [ -d "$category_folder" ]; then | |
| category_name=$(basename "$category_folder") | |
| count=$(find "$category_folder" -maxdepth 1 -name "*.md" ! -name README.md -type f | wc -l | tr -d ' ') | |
| if [ "$count" -gt 0 ]; then | |
| echo " └─ $category_name: $count file(s)" | |
| fi | |
| fi | |
| done | |
| fi | |
| done | |
| if [ "$DRY_RUN" = true ]; then | |
| echo "" | |
| echo "💡 Chạy lại không có --dry-run để thực sự di chuyển file" | |
| fi | |