2.3.0: proyectos por programa, carpetas vacías y mapa
Tres cosas para la reorganización, pedidas juntas.
Proyectos por programa: la plantilla pasa de 01_Proyectos/{nombre} a
01_Proyectos/{app}/{nombre}, con {app} = el programa que abre el archivo
(R.APPS_PROYECTO, R.app_proyecto). Lo que ya viene en su propia carpeta se
queda con el nombre de esa carpeta; lo que se separa es lo suelto. Las
plantillas guardadas se migran solas si nadie las tocó. Entran extensiones
que caían en «otro»: .sesx, .c4d, .blend, .motn, .aepx, .aet, .prel, .idml,
.psdt, .afpub.
Carpetas vacías: carpetas_vacias() las encuentra desde el inventario y las
comprueba en el disco; borrar_vacias() las quita. Es lo único que la app
borra, y solo porque no hay nada dentro: cada una queda en la bitácora como
rmdir y Deshacer las vuelve a crear con su nombre y en el inventario. No
entra en «Hacer las N de una vez». 29 huecos en David Reyes, 40 en N:.
Mapa: pestaña nueva con el árbol que producen las plantillas de verdad
(mapa_estructura, GET /api/estructura). Al tocar una carpeta dice qué guarda
—derivado de ROL_A_CUBETA y ROL_QUE—, cuánto hay hoy, y deja cambiar el
destino ahí mismo. POST /api/plantillas pasa a fusionar cubeta a cubeta:
guardar una sola plantilla borraba las otras diecinueve.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Este commit está contenido en:
50
probar.py
50
probar.py
@@ -46,6 +46,15 @@ class Reglas(unittest.TestCase):
|
||||
self.assertEqual(R.detectar_anio("2026"), 2026)
|
||||
self.assertIsNone(R.detectar_anio("2026 - agosto"))
|
||||
|
||||
def test_app_proyecto(self):
|
||||
self.assertEqual(R.app_proyecto(".prproj"), "Premiere")
|
||||
self.assertEqual(R.app_proyecto(".AEP"), "After Effects")
|
||||
self.assertEqual(R.app_proyecto(".drp"), "DaVinci Resolve")
|
||||
self.assertEqual(R.app_proyecto(".psd"), "Photoshop")
|
||||
self.assertIsNone(R.app_proyecto(".mp4"))
|
||||
# la plantilla de proyectos separa por programa
|
||||
self.assertIn("{app}", R.PLANTILLAS["contenido"]["PROYECTOS"])
|
||||
|
||||
def test_basura_y_proxies(self):
|
||||
self.assertTrue(R.es_basura_archivo("Thumbs.db"))
|
||||
self.assertTrue(R.es_basura_archivo(".DS_Store"))
|
||||
@@ -205,6 +214,47 @@ class Servidor(unittest.TestCase):
|
||||
c, res = self.post("/api/deshacer", {"lote": res["lote"]})
|
||||
self.assertEqual(c, 200)
|
||||
self.assertEqual(antes, self._arbol())
|
||||
# 4b. el mapa dice dónde va a acabar cada cosa, con las plantillas de verdad
|
||||
est = self.get("/api/estructura" + q)
|
||||
proy = next(c for c in est["cubetas"] if c["clave"] == "PROYECTOS")
|
||||
self.assertIn("{app}", proy["plantilla"])
|
||||
self.assertTrue(proy["guarda"])
|
||||
nombres = []
|
||||
|
||||
def bajar(n):
|
||||
nombres.append(n["nombre"])
|
||||
for h in n["hijos"]:
|
||||
bajar(h)
|
||||
bajar(est["arbol"])
|
||||
self.assertIn("00_Grabacion", nombres)
|
||||
self.assertIn("Premiere", nombres)
|
||||
# guardar UNA plantilla no puede llevarse por delante las demás
|
||||
antes_pl = len([c for c in est["cubetas"] if c["plantilla"]])
|
||||
self.assertEqual(self.post("/api/plantillas", {"plantillas": {"contenido": {"MUSICA": "_Recursos/Musica/{sub}"}}})[0], 200)
|
||||
self.assertEqual(len([c for c in self.get("/api/estructura" + q)["cubetas"] if c["plantilla"]]), antes_pl)
|
||||
|
||||
# 4c. carpetas vacías: se encuentran, se borran sin tocar un archivo y deshacer las devuelve
|
||||
for rel in ("Huecos/uno", "Huecos/dos", "Sola"):
|
||||
os.makedirs(os.path.join(self.raiz, rel.replace("/", os.sep)), exist_ok=True)
|
||||
self.post("/api/escanear", {"raiz": self.raiz})
|
||||
self.esperar_escaneo()
|
||||
v = self.get("/api/vacias" + q)
|
||||
rels = {c["rel"] for c in v["carpetas"]}
|
||||
self.assertIn("Huecos", rels) # el tope, no cada hija por separado
|
||||
self.assertIn("Sola", rels)
|
||||
self.assertNotIn(os.path.join("Huecos", "uno"), rels)
|
||||
arbol_antes = self._arbol()
|
||||
c, res = self.post("/api/vacias/borrar", {"raiz": self.raiz, "rutas": [x["ruta"] for x in v["carpetas"]]})
|
||||
self.assertEqual(c, 200)
|
||||
self.assertEqual(res["errores"], [])
|
||||
self.assertEqual(res["borradas"], 4) # Huecos con sus dos hijas, y Sola
|
||||
self.assertFalse(os.path.isdir(os.path.join(self.raiz, "Sola")))
|
||||
self.assertEqual(arbol_antes, self._arbol()) # ni un archivo tocado
|
||||
c, _ = self.post("/api/deshacer", {"lote": res["lote"]})
|
||||
self.assertEqual(c, 200)
|
||||
self.assertTrue(os.path.isdir(os.path.join(self.raiz, "Huecos", "uno")))
|
||||
self.post("/api/vacias/borrar", {"raiz": self.raiz, "rutas": [x["ruta"] for x in v["carpetas"]]})
|
||||
|
||||
# 5. desconectar con datos; la segunda vez ya no existe
|
||||
c, d = self.post("/api/raiz/quitar", {"raiz": self.raiz, "datos": True})
|
||||
self.assertEqual(c, 200)
|
||||
|
||||
Referencia en una nueva incidencia
Block a user