Capítulo 3: Gestor de Tareas CLI

Por: SiempreListo
pythonzigcliintermedio

Gestor de Tareas

Un sistema CRUD completo para gestionar tareas con persistencia en JSON.

Conceptos que Aprenderás

  • Lectura/escritura de archivos
  • Serialización JSON
  • Estructuras de datos complejas
  • Patrón Command

Python

import json
from pathlib import Path
from dataclasses import dataclass, asdict
from typing import Optional
from datetime import datetime

ARCHIVO = Path("tareas.json")

@dataclass
class Tarea:
    id: int
    titulo: str
    completada: bool = False
    creada: str = ""

    def __post_init__(self):
        if not self.creada:
            self.creada = datetime.now().isoformat()

class GestorTareas:
    def __init__(self):
        self.tareas: list[Tarea] = []
        self.cargar()

    def cargar(self):
        if ARCHIVO.exists():
            data = json.loads(ARCHIVO.read_text())
            self.tareas = [Tarea(**t) for t in data]

    def guardar(self):
        ARCHIVO.write_text(json.dumps([asdict(t) for t in self.tareas], indent=2))

    def agregar(self, titulo: str) -> Tarea:
        id_nuevo = max((t.id for t in self.tareas), default=0) + 1
        tarea = Tarea(id=id_nuevo, titulo=titulo)
        self.tareas.append(tarea)
        self.guardar()
        return tarea

    def completar(self, id: int) -> Optional[Tarea]:
        for t in self.tareas:
            if t.id == id:
                t.completada = True
                self.guardar()
                return t
        return None

    def eliminar(self, id: int) -> bool:
        inicial = len(self.tareas)
        self.tareas = [t for t in self.tareas if t.id != id]
        if len(self.tareas) < inicial:
            self.guardar()
            return True
        return False

    def listar(self):
        for t in self.tareas:
            estado = "[x]" if t.completada else "[ ]"
            print(f"{t.id}. {estado} {t.titulo}")

def main():
    gestor = GestorTareas()
    comandos = {
        'add': lambda: gestor.agregar(input("Título: ")),
        'done': lambda: gestor.completar(int(input("ID: "))),
        'del': lambda: gestor.eliminar(int(input("ID: "))),
        'list': gestor.listar,
    }

    while True:
        cmd = input("\nComando (add/done/del/list/exit): ").lower()
        if cmd == 'exit':
            break
        if cmd in comandos:
            comandos[cmd]()

if __name__ == "__main__":
    main()

Zig

const std = @import("std");
const json = std.json;

const Tarea = struct {
    id: u32,
    titulo: []const u8,
    completada: bool,
};

const GestorTareas = struct {
    tareas: std.ArrayList(Tarea),
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator) GestorTareas {
        return .{
            .tareas = std.ArrayList(Tarea).init(allocator),
            .allocator = allocator,
        };
    }

    pub fn agregar(self: *GestorTareas, titulo: []const u8) !void {
        const id = if (self.tareas.items.len > 0)
            self.tareas.items[self.tareas.items.len - 1].id + 1
        else
            1;

        try self.tareas.append(.{
            .id = id,
            .titulo = try self.allocator.dupe(u8, titulo),
            .completada = false,
        });
    }

    pub fn completar(self: *GestorTareas, id: u32) bool {
        for (self.tareas.items) |*t| {
            if (t.id == id) {
                t.completada = true;
                return true;
            }
        }
        return false;
    }

    pub fn listar(self: *GestorTareas) !void {
        const stdout = std.io.getStdOut().writer();
        for (self.tareas.items) |t| {
            const estado: []const u8 = if (t.completada) "[x]" else "[ ]";
            try stdout.print("{d}. {s} {s}\n", .{ t.id, estado, t.titulo });
        }
    }
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var gestor = GestorTareas.init(allocator);
    const stdin = std.io.getStdIn().reader();
    const stdout = std.io.getStdOut().writer();
    var buf: [256]u8 = undefined;

    while (true) {
        try stdout.print("\nComando (add/done/list/exit): ", .{});
        const cmd = stdin.readUntilDelimiter(&buf, '\n') catch break;

        if (std.mem.eql(u8, cmd, "exit")) break;

        if (std.mem.eql(u8, cmd, "add")) {
            try stdout.print("Título: ", .{});
            const titulo = stdin.readUntilDelimiter(&buf, '\n') catch continue;
            try gestor.agregar(titulo);
        } else if (std.mem.eql(u8, cmd, "list")) {
            try gestor.listar();
        }
    }
}

Diferencias Clave

AspectoPythonZig
MemoriaAutomáticaManual con allocator
JSONBuilt-instd.json
Listaslist[]ArrayList

Ejercicios

  1. Agrega fechas de vencimiento
  2. Implementa prioridades (alta/media/baja)
  3. Agrega filtros por estado

← Anterior | Siguiente →