Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Proyecto 2: Organizador de Archivos por Tipo

🧭 Navegación:

📁 El problema: El caos digital

¿Te resulta familiar tener una carpeta de descargas llena de archivos de todo tipo? ¿O un escritorio tan abarrotado de documentos que es imposible encontrar lo que buscas? El desorden digital es un problema común que afecta nuestra productividad y eficiencia.

Mantener los archivos organizados manualmente es:

  • Tedioso y repetitivo
  • Propenso a errores
  • Consumidor de tiempo
  • Fácil de postergar

La solución es crear un sistema automatizado que organice los archivos según su tipo, moviendo cada uno a la carpeta correspondiente.

🎯 Objetivo del proyecto

Desarrollaremos un script de Python que:

  1. Analice una carpeta desordenada (como Descargas o Escritorio)
  2. Identifique el tipo de cada archivo según su extensión
  3. Cree carpetas organizadas por categorías (Documentos, Imágenes, Videos, etc.)
  4. Mueva cada archivo a la carpeta correspondiente
  5. Genere un informe de la organización realizada
  6. Pueda ejecutarse manualmente o programarse para correr periódicamente

📋 Planificación del sistema

Componentes principales:

  1. Análisis: Escanear la carpeta y clasificar los archivos
  2. Categorización: Definir categorías y asignar extensiones a cada una
  3. Organización: Crear carpetas y mover archivos
  4. Informes: Generar resumen de las acciones realizadas
  5. Configuración: Permitir personalizar el comportamiento

Bibliotecas que utilizaremos:

import os               # Para operaciones con el sistema de archivos
import shutil           # Para mover archivos
import datetime         # Para registrar fechas en informes
import logging          # Para registrar eventos
import json             # Para manejar configuraciones
import argparse         # Para procesar argumentos de línea de comandos

💻 Implementación paso a paso

Paso 1: Definir categorías y extensiones

Primero, crearemos un mapeo entre extensiones de archivo y categorías:

# file_categories.py

# Mapeo de extensiones a categorías
FILE_CATEGORIES = {
    # Documentos
    "Documentos": [
        # Documentos de texto
        "pdf", "doc", "docx", "txt", "rtf", "odt", 
        # Hojas de cálculo
        "xls", "xlsx", "csv", "ods",
        # Presentaciones
        "ppt", "pptx", "odp",
        # Otros documentos
        "md", "epub", "mobi"
    ],
    
    # Imágenes
    "Imagenes": [
        "jpg", "jpeg", "png", "gif", "bmp", "svg", 
        "tiff", "webp", "ico", "raw", "psd", "ai"
    ],
    
    # Audio
    "Audio": [
        "mp3", "wav", "ogg", "flac", "aac", "wma", 
        "m4a", "mid", "midi"
    ],
    
    # Video
    "Videos": [
        "mp4", "avi", "mkv", "mov", "wmv", "flv", 
        "webm", "m4v", "mpg", "mpeg", "3gp"
    ],
    
    # Archivos comprimidos
    "Comprimidos": [
        "zip", "rar", "7z", "tar", "gz", "bz2", 
        "xz", "iso"
    ],
    
    # Código y programación
    "Codigo": [
        "py", "js", "html", "css", "java", "c", "cpp", 
        "h", "cs", "php", "rb", "go", "rs", "swift",
        "json", "xml", "yaml", "yml", "sql", "sh", "bat"
    ],
    
    # Ejecutables e instaladores
    "Ejecutables": [
        "exe", "msi", "apk", "dmg", "deb", "rpm"
    ]
}

# Función para obtener la categoría de un archivo según su extensión
def get_file_category(filename):
    """
    Determina la categoría de un archivo basado en su extensión.
    
    Args:
        filename: Nombre del archivo a categorizar
        
    Returns:
        str: Nombre de la categoría o 'Otros' si no coincide con ninguna
    """
    # Obtener la extensión (sin el punto)
    extension = filename.split('.')[-1].lower() if '.' in filename else ""
    
    # Buscar la categoría correspondiente
    for category, extensions in FILE_CATEGORIES.items():
        if extension in extensions:
            return category
    
    # Si no coincide con ninguna categoría conocida
    return "Otros"

Paso 2: Crear la clase principal del organizador

# file_organizer.py

import os
import shutil
import logging
import datetime
from file_categories import get_file_category

class FileOrganizer:
    """Clase para organizar archivos por tipo."""
    
    def __init__(self, source_dir, organize_directories=False):
        """
        Inicializa el organizador de archivos.
        
        Args:
            source_dir: Directorio a organizar
            organize_directories: Si True, también organiza subdirectorios
        """
        self.source_dir = os.path.abspath(source_dir)
        self.organize_directories = organize_directories
        self.stats = {
            "total_files": 0,
            "organized_files": 0,
            "skipped_files": 0,
            "categories": {}
        }
        
        # Configurar logging
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler(os.path.join(self.source_dir, "organizer_log.txt")),
                logging.StreamHandler()
            ]
        )
        self.logger = logging.getLogger()
        
    def organize(self):
        """Organiza los archivos en el directorio fuente."""
        self.logger.info(f"Iniciando organización de archivos en: {self.source_dir}")
        
        # Verificar que el directorio existe
        if not os.path.exists(self.source_dir):
            self.logger.error(f"El directorio {self.source_dir} no existe.")
            return False
        
        # Obtener lista de archivos (no directorios)
        items = os.listdir(self.source_dir)
        self.stats["total_files"] = len(items)
        
        # Procesar cada elemento
        for item_name in items:
            item_path = os.path.join(self.source_dir, item_name)
            
            # Saltar el archivo de log
            if item_name == "organizer_log.txt":
                continue
                
            # Verificar si es un directorio
            if os.path.isdir(item_path):
                if self.organize_directories:
                    # Si queremos organizar directorios, tratarlos como archivos
                    self._process_item(item_path)
                else:
                    self.logger.info(f"Saltando directorio: {item_name}")
                    self.stats["skipped_files"] += 1
            else:
                # Procesar archivo
                self._process_item(item_path)
        
        # Generar informe
        self._generate_report()
        
        return True
    
    def _process_item(self, item_path):
        """
        Procesa un archivo o directorio y lo mueve a la categoría correspondiente.
        
        Args:
            item_path: Ruta completa al elemento a procesar
        """
        item_name = os.path.basename(item_path)
        
        # Determinar categoría
        category = get_file_category(item_name)
        
        # Crear directorio de destino si no existe
        dest_dir = os.path.join(self.source_dir, category)
        if not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
            self.logger.info(f"Creado directorio: {category}")
        
        # Ruta de destino
        dest_path = os.path.join(dest_dir, item_name)
        
        # Verificar si ya existe un archivo con el mismo nombre
        if os.path.exists(dest_path):
            # Añadir timestamp al nombre para evitar sobreescritura
            name, ext = os.path.splitext(item_name)
            timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            new_name = f"{name}_{timestamp}{ext}"
            dest_path = os.path.join(dest_dir, new_name)
            self.logger.warning(f"Archivo ya existe, renombrando a: {new_name}")
        
        try:
            # Mover el archivo
            shutil.move(item_path, dest_path)
            self.logger.info(f"Movido: {item_name} -> {category}/{os.path.basename(dest_path)}")
            
            # Actualizar estadísticas
            self.stats["organized_files"] += 1
            if category not in self.stats["categories"]:
                self.stats["categories"][category] = 0
            self.stats["categories"][category] += 1
            
        except Exception as e:
            self.logger.error(f"Error al mover {item_name}: {str(e)}")
            self.stats["skipped_files"] += 1
    
    def _generate_report(self):
        """Genera un informe de la organización realizada."""
        report = [
            "=" * 50,
            "INFORME DE ORGANIZACIÓN DE ARCHIVOS",
            "=" * 50,
            f"Fecha: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
            f"Directorio: {self.source_dir}",
            "-" * 50,
            f"Total de elementos: {self.stats['total_files']}",
            f"Archivos organizados: {self.stats['organized_files']}",
            f"Elementos omitidos: {self.stats['skipped_files']}",
            "-" * 50,
            "Distribución por categorías:"
        ]
        
        # Añadir estadísticas por categoría
        for category, count in self.stats["categories"].items():
            report.append(f"- {category}: {count} archivos")
        
        report.extend([
            "=" * 50,
            "Organización completada con éxito.",
            "=" * 50
        ])
        
        # Guardar informe en archivo
        report_path = os.path.join(self.source_dir, "organizacion_informe.txt")
        with open(report_path, "w", encoding="utf-8") as f:
            f.write("\n".join(report))
        
        self.logger.info(f"Informe generado: {report_path}")
        
        # También mostrar en consola
        print("\n".join(report))

Paso 3: Script principal con interfaz de línea de comandos

# organize_files.py

import argparse
import os
from file_organizer import FileOrganizer

def main():
    """Función principal del programa."""
    # Configurar el parser de argumentos
    parser = argparse.ArgumentParser(
        description="Organizador automático de archivos por tipo"
    )
    
    parser.add_argument(
        "directory", 
        nargs="?",
        default=os.getcwd(),
        help="Directorio a organizar (por defecto: directorio actual)"
    )
    
    parser.add_argument(
        "-d", "--dirs",
        action="store_true",
        help="Organizar también subdirectorios"
    )
    
    # Parsear argumentos
    args = parser.parse_args()
    
    # Verificar que el directorio existe
    if not os.path.exists(args.directory):
        print(f"Error: El directorio '{args.directory}' no existe.")
        return 1
    
    # Crear y ejecutar el organizador
    organizer = FileOrganizer(args.directory, args.dirs)
    success = organizer.organize()
    
    return 0 if success else 1

if __name__ == "__main__":
    exit(main())

🚀 Uso del sistema

Uso básico

Para organizar el directorio actual:

python organize_files.py

Para organizar un directorio específico:

python organize_files.py /ruta/a/mi/carpeta/desordenada

Para incluir subdirectorios en la organización:

python organize_files.py -d /ruta/a/mi/carpeta

Automatización periódica

En Windows:

Puedes crear una tarea programada para ejecutar el script periódicamente:

  1. Abre el Programador de tareas
  2. Crea una nueva tarea básica
  3. Configúrala para que se ejecute diariamente o semanalmente
  4. Apunta al script con los parámetros deseados

En Linux:

Puedes usar cron para programar la ejecución:

# Editar crontab
crontab -e

# Añadir una línea para ejecutar el script todos los viernes a las 8 PM
0 20 * * 5 python /ruta/a/organize_files.py /ruta/a/descargas

🔍 Comprueba tu comprensión

  1. ¿Qué sucedería si dos archivos con el mismo nombre pertenecen a la misma categoría?
  2. ¿Cómo modificarías el código para manejar archivos sin extensión?
  3. ¿Qué estrategia usarías para organizar archivos basándote en su contenido en lugar de su extensión?
  4. ¿Cómo adaptarías el sistema para permitir configuraciones personalizadas de categorías?

🛠️ Ideas para mejoras

  • Configuración personalizada: Permitir al usuario definir sus propias categorías y extensiones
  • Modo simulación: Mostrar qué cambios se harían sin realizar movimientos reales
  • Organización por fecha: Agrupar archivos por fecha de creación o modificación
  • Detección inteligente: Usar el contenido del archivo para determinar su tipo, no solo la extensión
  • Interfaz gráfica: Crear una GUI para facilitar el uso
  • Integración con el sistema: Añadir opciones al menú contextual del explorador de archivos
  • Filtros avanzados: Permitir incluir o excluir archivos según patrones

📝 Resumen

En este proyecto, has creado un sistema de organización de archivos que:

  • Clasifica automáticamente archivos según su tipo
  • Crea una estructura de carpetas ordenada
  • Maneja conflictos de nombres de archivo
  • Genera informes detallados de las acciones realizadas
  • Puede ejecutarse desde la línea de comandos con opciones configurables

Este organizador no solo te ayudará a mantener tu espacio digital ordenado, sino que también te ha permitido aplicar conceptos importantes de Python como el manejo de archivos y directorios, la creación de clases, el procesamiento de argumentos de línea de comandos y la generación de informes.

En el próximo proyecto, aprenderemos a extraer información de sitios web con web scraping.