hue-portal-backend-v2 / backend /scripts /organize_markdowns.sh
davidtran999's picture
Push full code from hue-portal-backend folder
519b145
#!/bin/bash
# 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