3.8.0: «database is locked» y los 753 que no se movieron, arreglados; fechas raras, escaneo profundo, buscar, otros discos, banco por fecha y compilar en el estudio
- Tras «Aplicar» en una carpeta grande el inventario se ponía al día con la base tomada más de diez minutos y «Buscar clips» se rendía con «database is locked». Ahora las preguntas al disco van antes y en paralelo y lo de dentro de cada carpeta se busca por índice: 1,3 s para 2.493 movimientos, mismas filas de archivos que antes y 183 carpetas que antes se perdían. La lectura de clips reintenta si la base está ocupada. - Las fotos de «Alta» y «Baja» chocaban al juntarse en una carpeta: las subcarpetas con sentido se conservan, el plan avisa antes de dos archivos iguales en una fila, y un número entre dos palabras («Fiesta 6 Años») ya no se quita. - Los fallos de «Aplicar» se agrupan por motivo y dicen qué hacer; el banco de material se explica en llano. - Fechas raras (el reloj de una cámara), escaneo profundo, buscar en todos los clientes, traer lo que el cliente tiene en otros discos, el banco por temas o por fecha, y «Calcular de nuevo» más rápido. - Mover reintenta un momento si el archivo está abierto un instante (lectura de clips, antivirus). - .github/workflows/publicar.yml para compilar y publicar desde un PC del estudio; las pruebas devuelven error si algo falla. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Este commit está contenido en:
@@ -402,6 +402,77 @@ class Motor(unittest.TestCase):
|
||||
self.assertTrue(d["niveles"] >= 3 and "Lugares" in [h["nombre"] for h in d["hijos"]], d)
|
||||
self.assertIn("banco", [a["clave"] for a in O.acciones(raiz)["acciones"]])
|
||||
|
||||
def test_alta_y_baja_no_chocan(self):
|
||||
"""Vía Sumapaz: cada evento guardaba la misma foto en «Alta» y en «Baja»; el plan las juntaba en una
|
||||
sola carpeta y 753 no se movieron. Las subcarpetas con sentido se conservan."""
|
||||
raiz = cliente("AltaBaja")
|
||||
for sub, tam in (("Alta", 8192), ("Baja", 2048)):
|
||||
for n in ("IMG_5154.jpg", "IMG_5155.jpg"):
|
||||
mk(raiz, f"Fotos varias/2022/Enero/Evento recaudadoras/{sub}/{n}", 2022, 1, 2, tam)
|
||||
mk(raiz, "Fotos varias/2022/Diciembre/Fiesta 6 Años/IMG_3135.jpg", 2022, 12, 14)
|
||||
antes = nombres(raiz)
|
||||
p = plan(raiz)
|
||||
self.assertFalse([f for f in p["filas"] if "OJO" in f["motivo"]], [f["motivo"] for f in p["filas"]])
|
||||
por = {}
|
||||
for f in p["filas"]:
|
||||
for a in f["archivos"] or []:
|
||||
por[a] = f["destino_rel"]
|
||||
alta = [d for a, d in por.items() if os.sep + "Alta" + os.sep in a]
|
||||
baja = [d for a, d in por.items() if os.sep + "Baja" + os.sep in a]
|
||||
self.assertTrue(alta and baja and all(d.endswith("Alta") for d in alta) and all(d.endswith("Baja") for d in baja), por)
|
||||
self.assertTrue(any("Fiesta 6 Años" in d for d in por.values()), por)
|
||||
aplicar(raiz, p) # sin errores: ninguna foto choca con su gemela
|
||||
self.assertEqual(nombres(raiz), antes)
|
||||
|
||||
def test_reloj_de_una_camara(self):
|
||||
"""Una cámara con el reloj sin poner: se corrige con la fecha real del primer clip, sigue corregida al
|
||||
volver a escanear, el plan la lleva a su mes de verdad, y se deshace."""
|
||||
raiz = cliente("Reloj")
|
||||
mk(raiz, "Vía Mujeres/Drone/DJI_0001.MP4", 2009, 1, 19, 4096)
|
||||
mk(raiz, "Vía Mujeres/Drone/DJI_0002.MP4", 2009, 1, 20, 4096)
|
||||
plan(raiz) # escanea
|
||||
r = O.relojes(raiz)
|
||||
self.assertEqual([(g["fuente"], g["n"], g["desde"]) for g in r["grupos"]], [("DJI", 2, "2009-01-19")], r)
|
||||
c = O.corregir_reloj(raiz, r["grupos"][0]["dir"], "DJI", "2024-03-12")
|
||||
self.assertTrue(c.get("ok") and c["n"] == 2, c)
|
||||
con = O.db()
|
||||
fechas = sorted((x["fecha"][:10], x["fecha_origen"]) for x in con.execute("SELECT fecha, fecha_origen FROM archivos WHERE raiz = ? AND nombre LIKE 'DJI_%'", (raiz,)))
|
||||
con.close()
|
||||
self.assertEqual(fechas, [("2024-03-12", "corregida"), ("2024-03-13", "corregida")])
|
||||
p = plan(raiz) # vuelve a escanear: la corrección se mantiene
|
||||
self.assertFalse(O.relojes(raiz)["grupos"])
|
||||
self.assertTrue(any("2024" in f["destino_rel"] and "03- Marzo" in f["destino_rel"] for f in p["filas"]), [f["destino_rel"] for f in p["filas"]])
|
||||
d = O.deshacer_reloj(raiz, O.relojes(raiz)["corregidos"][0]["grupo"])
|
||||
self.assertTrue(d.get("ok"), d)
|
||||
self.assertEqual(O.relojes(raiz)["grupos"][0]["desde"], "2009-01-19")
|
||||
# y buscar la encuentra, con tilde o sin ella
|
||||
b = O.buscar("via mujeres dji_0002")
|
||||
self.assertEqual([x["nombre"] for x in b["resultados"]], ["DJI_0002.MP4"], b)
|
||||
self.assertEqual(O.buscar("x").get("error"), "escribe al menos dos letras")
|
||||
|
||||
def test_banco_por_fecha(self):
|
||||
"""El banco también se puede ordenar por fecha: a la 00_Grabacion de su mes, con el tema de etiqueta."""
|
||||
raiz = cliente("BancoFecha")
|
||||
mk(raiz, "Archivo (No tocar)/Lugares/Peajes/Drone/DJI_20240312101500_0001_D.MP4", 2024, 3, 12)
|
||||
mk(raiz, "Archivo (No tocar)/Lugares/Peajes/Final.mp4", 2024, 4, 2)
|
||||
O.CONFIG.setdefault("bancos", {})[os.path.normpath(raiz).lower()] = {O.R.norm("Archivo (No tocar)"): "fecha"}
|
||||
self.addCleanup(lambda: O.CONFIG["bancos"].pop(os.path.normpath(raiz).lower(), None))
|
||||
p = plan(raiz)
|
||||
self.assertEqual([b["modo"] for b in p["bancos"]], ["fecha"])
|
||||
banco = [f for f in p["filas"] if f["paso"] == "banco"]
|
||||
self.assertEqual([f["destino_rel"] for f in banco], [rel("2024", "03- Marzo", "00_Grabacion", "12- Martes", "Peajes", "OSMO")])
|
||||
self.assertFalse(banco[0]["marcado"])
|
||||
|
||||
def test_otros_discos(self):
|
||||
"""El mismo cliente en otros discos: se ofrece traerlo, sin contar las carpetas ya registradas."""
|
||||
raiz = cliente("Repartido")
|
||||
previo = dict(O._DISCOS_CACHE)
|
||||
self.addCleanup(lambda: (O._DISCOS_CACHE.clear(), O._DISCOS_CACHE.update(previo)))
|
||||
O._DISCOS_CACHE.update(ts=time.time(), datos={"discos": [{"letra": "K:", "unc": r"\\nas\Entregas", "nombre": ""}],
|
||||
"clientes": {"Repartido": [r"K:\Repartido viejo", raiz]}})
|
||||
s = O.otros_sitios(raiz)
|
||||
self.assertEqual([(x["ruta"], x["unc"]) for x in s], [(r"K:\Repartido viejo", r"\\nas\Entregas")])
|
||||
|
||||
def test_el_convertidor_no_repite(self):
|
||||
"""22 clips se habían convertido dos veces y algunos tres («…_10bit_10bit_10bit.mp4»)."""
|
||||
raiz = cliente("Convertidos")
|
||||
@@ -1092,6 +1163,7 @@ class Convertidor(unittest.TestCase):
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
r = unittest.main(verbosity=2, exit=False)
|
||||
finally:
|
||||
shutil.rmtree(TMP, ignore_errors=True)
|
||||
sys.exit(0 if r.result.wasSuccessful() else 1) # la compilación del estudio se detiene si algo falla
|
||||
|
||||
Referencia en una nueva incidencia
Block a user