2.5.0: las grabaciones bajan hasta el día de rodaje
El año y el mes salían de la fecha de cada archivo desde 2.2.0, pero dentro
del mes iba todo junto: en Camilo, 13 días de rodaje y 907 archivos en la
misma carpeta.
- Nueva variable {dia} («15- Viernes», como los meses) en las plantillas de
grabación de los cuatro tipos de trabajo, con migración de las guardadas.
- Plan._dias_de_rodaje() cuenta los días distintos por mes y dia_de()
devuelve "" cuando el mes tuvo un solo rodaje: la carpeta del día solo
aparece donde hace falta. David Reyes la necesita en 1 de 8 meses,
Danney en 36 de 57, Camilo en 12 de 26. 186 grupos, cero colisiones.
- Un archivo de cámara suelto también se archiva por su propia fecha:
plan_archivo hacía «if mes: fm = mes» y se saltaba la regla de 2.2.0.
- «¿Dónde va a parar este archivo?» dice también el día.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Este commit está contenido en:
24
README.md
24
README.md
@@ -497,6 +497,30 @@ permite — en ese caso la app lo dice y no deja nada escrito. Se quita con un c
|
|||||||
La carpeta `.organilab` no se escanea ni se ordena, y los `desktop.ini` que pone la app no se cuentan
|
La carpeta `.organilab` no se escanea ni se ordena, y los `desktop.ini` que pone la app no se cuentan
|
||||||
como basura: si no, el primer «apartar basura» se llevaba los colores por delante.
|
como basura: si no, el primer «apartar basura» se llevaba los colores por delante.
|
||||||
|
|
||||||
|
2.5.0, el material de cámara baja hasta el día de rodaje.
|
||||||
|
|
||||||
|
El año y el mes ya salían de la fecha de cada archivo desde la 2.2.0 (Danney se repartía solo en 54
|
||||||
|
meses distintos), pero dentro del mes seguía todo junto: en Camilo, un mes con **13 días de rodaje
|
||||||
|
distintos y 907 archivos** en la misma carpeta. Ahora la plantilla de grabación lleva `{dia}`
|
||||||
|
(`{anio}/{mes}/00_Grabacion/{dia}/{sub}`), con el día en la misma forma que los meses: `15- Viernes`,
|
||||||
|
el número delante para que ordene y el nombre detrás para saber de un vistazo si aquello fue un sábado
|
||||||
|
de rodaje.
|
||||||
|
|
||||||
|
**El día solo aparece cuando hace falta.** `Plan._dias_de_rodaje()` cuenta, mes a mes, en cuántos días
|
||||||
|
distintos se grabó algo en esa carpeta de cliente, y `dia_de()` devuelve cadena vacía cuando el mes
|
||||||
|
tuvo un solo rodaje: entonces la variable se lleva su propio segmento y la carpeta del día desaparece
|
||||||
|
sola. Medido sobre el material real: David Reyes solo la necesita en 1 de sus 8 meses, Danney en 36 de
|
||||||
|
57 y Camilo en 12 de 26. En el plan de los tres clientes salen 186 grupos por día y ninguna colisión.
|
||||||
|
|
||||||
|
**Un archivo de cámara suelto también se archiva por su propia fecha.** Dentro de una carpeta de
|
||||||
|
grabación el archivo ya mandaba sobre el nombre de la carpeta desde la 2.2.0, pero un clip traspapelado
|
||||||
|
fuera de ella se quedaba en el mes que dijera la carpeta: `plan_archivo` hacía `if mes: fm = mes` sin
|
||||||
|
mirar la fecha. Ahora usa la misma regla y el mismo aviso.
|
||||||
|
|
||||||
|
Probado sobre un árbol de ensayo con un marzo de tres rodajes y un mayo de uno solo: marzo se reparte
|
||||||
|
en `03- Martes`, `11- Miércoles` y `19- Jueves`, mayo se queda sin carpeta de día, y aplicar y deshacer
|
||||||
|
dejan el árbol exacto.
|
||||||
|
|
||||||
Probado el 9 sep 2026 sobre una copia de ensayo: plan → aplicar → deshacer → volver a aplicar deja el árbol
|
Probado el 9 sep 2026 sobre una copia de ensayo: plan → aplicar → deshacer → volver a aplicar deja el árbol
|
||||||
exacto; conversión ffmpeg verificada y reemplazo con original a `_Para borrar`. El puente de Resolve conectó
|
exacto; conversión ffmpeg verificada y reemplazo con original a `_Para borrar`. El puente de Resolve conectó
|
||||||
con Resolve Studio 21.1 pero no pudo abrir proyecto porque Resolve estaba en el Gestor de proyectos:
|
con Resolve Studio 21.1 pero no pudo abrir proyecto porque Resolve estaba en el Gestor de proyectos:
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"nombre": "OrganiLAB",
|
"nombre": "OrganiLAB",
|
||||||
"version": "2.4.0",
|
"version": "2.5.0",
|
||||||
"builtAt": "2026-09-17T03:49:29.000Z",
|
"builtAt": "2026-09-17T04:03:44.000Z",
|
||||||
"archivo": "OrganizadorLabstream-portatil-2.4.0.zip",
|
"archivo": "OrganizadorLabstream-portatil-2.5.0.zip",
|
||||||
"size": 198734267,
|
"size": 198736121,
|
||||||
"sha256": "02a9dade711cab28953b8e110d3440e4c2262287354e20b0d99ea00e05198b8f",
|
"sha256": "ef8054268fafbf35dc4e7336160c59195b382fe1f7e1cf9b6b1db6b852791319",
|
||||||
"app": {
|
"app": {
|
||||||
"archivo": "OrganizadorLabstream-app-2.4.0.zip",
|
"archivo": "OrganizadorLabstream-app-2.5.0.zip",
|
||||||
"size": 14914514,
|
"size": 14916362,
|
||||||
"sha256": "fc5e250afb755748b3c8a227780fef44c0b231026087247328e7d723cb87135a",
|
"sha256": "2d0bf45795699476bcf94ccb6f0633d726320e562c9594e0be62d85ae1648941",
|
||||||
"url": "/api/herramientas/OrganizadorLabstream-app-2.4.0.zip"
|
"url": "/api/herramientas/OrganizadorLabstream-app-2.5.0.zip"
|
||||||
},
|
},
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"firma": {
|
"firma": {
|
||||||
"alg": "rsa2048-pkcs1v15-sha256",
|
"alg": "rsa2048-pkcs1v15-sha256",
|
||||||
"valor": "41707cfc81c53d0f2aa3c2cd2180c49fa588beef835d159f4caf60ef9a3e32aefa56a02f29143867eba7902f1ddf71c9a92332911ee617367be5634daff2eb293bf9a11ab668e827e0012159785b7a1d0f37ac8d76ad588f9d1521bf89c0d2539e6a6812d47fb7beae027493f6b53f844eaa655c81415396413031bd8aff59a49a5592e9263af442a7c853e17a63cb6b274e12f88d2066295ed0f6b3a030a938921e513e539b6fbe968348de5618b6421b48d5b72758c8adef2cdaeec768bf75aa45fecd390392e693038a600dd051f62aa47a62910f5ded9601ba0a3f402feb0f9389b3a27786a1b6e7ca5f7f57c21f93e80d09ecd675b9c3200241ccb7aea4"
|
"valor": "4b0649e34b9be12b93f46ca15dfd1ef1e0a87365c45fdebf80a3ebe94b6bc1714f438f6c141d3456f0a41b6ce000444229bc1d4c1f2a2ce3b19dc1f21a36de1c7b2657f342906f2d6cc61eaafdfc7c05f2353778abc09dd74c4b4134974aafcf162b2ceac48e1b14536db8ec58990f6aadfaed7baa4d9a30f39e2ca90ebe8c2755b8026c4ff217c8ebc5b61933a657dec61ba1598089f595639b36e4dc7083597002e70317463714a19c889bae2f926ab9dfd6c1f36c45180608c968886ea2881dfb9d29f42b621e58eb583355e634670ab9424f3446092ae636716d86ebdb42d69e10d4fd24db2580acd4e2a01b14b6f4ba401a26d14d779310527c2d7d087f"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ import actualizador
|
|||||||
asistente.O = sys.modules[__name__]
|
asistente.O = sys.modules[__name__]
|
||||||
actualizador.O = sys.modules[__name__]
|
actualizador.O = sys.modules[__name__]
|
||||||
|
|
||||||
VERSION = "2.4.0"
|
VERSION = "2.5.0"
|
||||||
|
|
||||||
CONGELADO = getattr(sys, "frozen", False)
|
CONGELADO = getattr(sys, "frozen", False)
|
||||||
SIN_VENTANA = getattr(subprocess, "CREATE_NO_WINDOW", 0) # ffmpeg/ffprobe/net sin abrir consolas negras # empaquetado como .exe con PyInstaller
|
SIN_VENTANA = getattr(subprocess, "CREATE_NO_WINDOW", 0) # ffmpeg/ffprobe/net sin abrir consolas negras # empaquetado como .exe con PyInstaller
|
||||||
@@ -264,6 +264,9 @@ for t, d in R.PLANTILLAS.items():
|
|||||||
# los proyectos pasaron a separarse por programa: «01_Proyectos/{nombre}» -> «01_Proyectos/{app}/{nombre}»
|
# los proyectos pasaron a separarse por programa: «01_Proyectos/{nombre}» -> «01_Proyectos/{app}/{nombre}»
|
||||||
if b == "PROYECTOS" and PLANTILLAS[t][b] == p.replace("{app}/", ""):
|
if b == "PROYECTOS" and PLANTILLAS[t][b] == p.replace("{app}/", ""):
|
||||||
PLANTILLAS[t][b] = p
|
PLANTILLAS[t][b] = p
|
||||||
|
# y las grabaciones, por día de rodaje cuando el mes tiene más de uno
|
||||||
|
if b in ("GRABACION", "CELULAR") and PLANTILLAS[t][b] == p.replace("{dia}/", ""):
|
||||||
|
PLANTILLAS[t][b] = p
|
||||||
guardar_json(PLANTILLAS_PATH, PLANTILLAS)
|
guardar_json(PLANTILLAS_PATH, PLANTILLAS)
|
||||||
|
|
||||||
|
|
||||||
@@ -627,6 +630,7 @@ def destino_plantilla(tipo, cubeta, **kw):
|
|||||||
"anio": str(anio) if anio else "",
|
"anio": str(anio) if anio else "",
|
||||||
"mes": R.nombre_mes(mes) if mes else "",
|
"mes": R.nombre_mes(mes) if mes else "",
|
||||||
"nombre": kw.get("nombre", ""),
|
"nombre": kw.get("nombre", ""),
|
||||||
|
"dia": kw.get("dia", ""),
|
||||||
"sub": kw.get("sub", ""),
|
"sub": kw.get("sub", ""),
|
||||||
"rel": kw.get("rel", ""),
|
"rel": kw.get("rel", ""),
|
||||||
"fuente": kw.get("fuente", ""),
|
"fuente": kw.get("fuente", ""),
|
||||||
@@ -665,6 +669,31 @@ class Plan:
|
|||||||
self.sueltos = defaultdict(list) # (cubeta, destino, motivo, confianza) -> archivos
|
self.sueltos = defaultdict(list) # (cubeta, destino, motivo, confianza) -> archivos
|
||||||
self.con = db()
|
self.con = db()
|
||||||
self.avisos = []
|
self.avisos = []
|
||||||
|
self.dias_mes = self._dias_de_rodaje()
|
||||||
|
|
||||||
|
def _dias_de_rodaje(self):
|
||||||
|
"""Para cada mes, los días distintos en que se grabó algo en esta carpeta de cliente.
|
||||||
|
|
||||||
|
Sirve para decidir si el mes necesita carpetas por día. Medido el 16-sep-2026 sobre el material
|
||||||
|
real: David Reyes tiene un solo día de rodaje en 7 de sus 8 meses (ahí la carpeta del día sería
|
||||||
|
estorbo), mientras que Danney tiene 36 meses con varios días y Camilo llega a 13 días distintos
|
||||||
|
en un mismo mes, 907 archivos mezclados en la misma carpeta."""
|
||||||
|
dias = defaultdict(set)
|
||||||
|
for f in self.con.execute(
|
||||||
|
"SELECT fecha FROM archivos WHERE raiz = ? AND es_basura = 0 AND es_proxy = 0 AND rol IN "
|
||||||
|
"('camara_raw','camara_video','camara_sidecar','audio_grabado','celular_video')", (self.raiz,)):
|
||||||
|
f0 = f["fecha"]
|
||||||
|
try:
|
||||||
|
dias[(int(f0[:4]), int(f0[5:7]))].add(f0)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
return dias
|
||||||
|
|
||||||
|
def dia_de(self, fecha, anio, mes):
|
||||||
|
"""El nombre de la carpeta del día, o "" si en ese mes solo se grabó un día: entonces sobra."""
|
||||||
|
if len(self.dias_mes.get((anio, mes), ())) < 2:
|
||||||
|
return ""
|
||||||
|
return R.nombre_dia(fecha)
|
||||||
|
|
||||||
# ---- helpers ----
|
# ---- helpers ----
|
||||||
def rel(self, ruta):
|
def rel(self, ruta):
|
||||||
@@ -1039,7 +1068,7 @@ class Plan:
|
|||||||
hoy = datetime.date.today()
|
hoy = datetime.date.today()
|
||||||
if (ay, am) > (hoy.year, hoy.month):
|
if (ay, am) > (hoy.year, hoy.month):
|
||||||
ay -= 1
|
ay -= 1
|
||||||
grupos[(ay, am, fuente, fconf)].append((f["ruta"], f["bytes"]))
|
grupos[(ay, am, self.dia_de(f["fecha"], ay, am), fuente, fconf)].append((f["ruta"], f["bytes"]))
|
||||||
etiqueta = "" if generico else R.etiqueta_grabacion(nombre)
|
etiqueta = "" if generico else R.etiqueta_grabacion(nombre)
|
||||||
aviso_fecha = ""
|
aviso_fecha = ""
|
||||||
# hace falta que discrepe una parte de verdad: un archivo suelto traspapelado no debe dejar
|
# hace falta que discrepe una parte de verdad: un archivo suelto traspapelado no debe dejar
|
||||||
@@ -1052,13 +1081,16 @@ class Plan:
|
|||||||
self.avisos.append(f"«{self.rel(e.path)}» está dentro de {R.nombre_mes(mes)} de {anio}, pero {cuantos} de "
|
self.avisos.append(f"«{self.rel(e.path)}» está dentro de {R.nombre_mes(mes)} de {anio}, pero {cuantos} de "
|
||||||
f"sus archivos se grabaron en {R.nombre_mes(dm)} de {da}. Se archivan en el mes en que "
|
f"sus archivos se grabaron en {R.nombre_mes(dm)} de {da}. Se archivan en el mes en que "
|
||||||
f"se grabaron, no en el que dice la carpeta.")
|
f"se grabaron, no en el que dice la carpeta.")
|
||||||
for (ay, am, fuente, fconf), archivos in grupos.items():
|
for (ay, am, dia, fuente, fconf), archivos in grupos.items():
|
||||||
sub = fuente if not etiqueta else os.path.join(etiqueta, fuente)
|
sub = fuente if not etiqueta else os.path.join(etiqueta, fuente)
|
||||||
destino = destino_plantilla(self.tipo, "GRABACION", anio=ay, mes=am, sub=sub, nombre=limpiar_nombre(nombre))
|
destino = destino_plantilla(self.tipo, "GRABACION", anio=ay, mes=am, dia=dia, sub=sub,
|
||||||
|
nombre=limpiar_nombre(nombre))
|
||||||
# si todos ya están en esa carpeta, no hay nada que hacer
|
# si todos ya están en esa carpeta, no hay nada que hacer
|
||||||
if all(os.path.normpath(os.path.dirname(a[0])).lower() == self.abs_(destino).lower() for a in archivos):
|
if all(os.path.normpath(os.path.dirname(a[0])).lower() == self.abs_(destino).lower() for a in archivos):
|
||||||
continue
|
continue
|
||||||
mot = motivo + (" · repartido por fuente" if anio and mes else " · mes por la fecha de cada archivo" + (" (fecha de modificación)" if fconf == "baja" else ""))
|
mot = motivo + (" · repartido por fuente" if anio and mes else " · mes por la fecha de cada archivo" + (" (fecha de modificación)" if fconf == "baja" else ""))
|
||||||
|
if dia:
|
||||||
|
mot += " · y por día, que ese mes hubo varios rodajes"
|
||||||
self.agregar(e.path, self.abs_(destino), "GRABACION", "mover archivos", mot + aviso_fecha, fconf,
|
self.agregar(e.path, self.abs_(destino), "GRABACION", "mover archivos", mot + aviso_fecha, fconf,
|
||||||
archivos=archivos)
|
archivos=archivos)
|
||||||
|
|
||||||
@@ -1078,7 +1110,17 @@ class Plan:
|
|||||||
if anio and fila["fecha_origen"] == "mtime":
|
if anio and fila["fecha_origen"] == "mtime":
|
||||||
fa = anio
|
fa = anio
|
||||||
if mes:
|
if mes:
|
||||||
fm = mes
|
# el archivo manda sobre la carpeta cuando su propia fecha es de fiar, igual que dentro de una
|
||||||
|
# carpeta de grabación: si no, un clip de diciembre traspapelado en la carpeta de marzo se
|
||||||
|
# quedaba archivado en marzo, que es justo lo que 2.2.0 dejó de hacer con las grabaciones.
|
||||||
|
if _discrepa_mes(fila["fecha"], fila["fecha_origen"], anio or fa, mes):
|
||||||
|
if fila["fecha_origen"] == "nombre+mtime" and anio:
|
||||||
|
fa = anio if fm <= mes else anio - 1 # el nombre de la cámara no trae año
|
||||||
|
conf = "media"
|
||||||
|
motivo += (f" · la carpeta dice {R.nombre_mes(mes)} y el archivo se grabó en "
|
||||||
|
f"{R.nombre_mes(fm)}: manda el archivo")
|
||||||
|
else:
|
||||||
|
fm = mes
|
||||||
elif fila["fecha_origen"] == "mtime":
|
elif fila["fecha_origen"] == "mtime":
|
||||||
conf = "baja"
|
conf = "baja"
|
||||||
motivo += " · mes por fecha de modificación"
|
motivo += " · mes por fecha de modificación"
|
||||||
@@ -1114,7 +1156,8 @@ class Plan:
|
|||||||
elif cub == "PROYECTOS":
|
elif cub == "PROYECTOS":
|
||||||
destino = destino_plantilla(self.tipo, "PROYECTOS", app=sub)
|
destino = destino_plantilla(self.tipo, "PROYECTOS", app=sub)
|
||||||
else:
|
else:
|
||||||
destino = destino_plantilla(self.tipo, cub, anio=fa, mes=fm, sub=sub, nombre="")
|
destino = destino_plantilla(self.tipo, cub, anio=fa, mes=fm, sub=sub, nombre="",
|
||||||
|
dia=self.dia_de(fila["fecha"], fa, fm) if cub in ("GRABACION", "CELULAR") else "")
|
||||||
self.suelto(fila, cub, destino, motivo, conf)
|
self.suelto(fila, cub, destino, motivo, conf)
|
||||||
|
|
||||||
def plan_basura(self):
|
def plan_basura(self):
|
||||||
@@ -2864,6 +2907,7 @@ def _plural(n, uno, varios):
|
|||||||
EJEMPLO_VAR = {
|
EJEMPLO_VAR = {
|
||||||
"anio": ("2026", "el año del material"),
|
"anio": ("2026", "el año del material"),
|
||||||
"mes": ("09- Septiembre", "el mes del material"),
|
"mes": ("09- Septiembre", "el mes del material"),
|
||||||
|
"dia": ("15- Viernes", "el día en que se grabó, y solo si ese mes hubo más de un rodaje"),
|
||||||
"nombre": ("Nombre de la pieza", "el nombre de la pieza o de la carpeta"),
|
"nombre": ("Nombre de la pieza", "el nombre de la pieza o de la carpeta"),
|
||||||
"fuente": ("URSA_BRAW", "la cámara de la que salió"),
|
"fuente": ("URSA_BRAW", "la cámara de la que salió"),
|
||||||
"app": ("Premiere", "el programa que abre el proyecto"),
|
"app": ("Premiere", "el programa que abre el proyecto"),
|
||||||
@@ -3010,7 +3054,10 @@ def donde_iria(nombre, tipo="contenido"):
|
|||||||
if cub == "PROYECTOS":
|
if cub == "PROYECTOS":
|
||||||
kw["app"] = R.app_proyecto(ext) or ("Diseño" if rol == "diseno" else "Edicion")
|
kw["app"] = R.app_proyecto(ext) or ("Diseño" if rol == "diseno" else "Edicion")
|
||||||
destino = "_Revisar" if cub == "REVISAR" else destino_plantilla(tipo, cub, **kw)
|
destino = "_Revisar" if cub == "REVISAR" else destino_plantilla(tipo, cub, **kw)
|
||||||
return {"nombre": nombre, "rol": rol, "que": R.ROL_QUE.get(rol, rol), "cubeta": cub,
|
# el día solo se sabe con la carpeta del cliente delante (depende de cuántos rodajes tuvo ese mes),
|
||||||
|
# así que aquí se dice cuál sería, no que vaya a estar
|
||||||
|
dia = R.nombre_dia(fecha) if cub in ("GRABACION", "CELULAR") else ""
|
||||||
|
return {"nombre": nombre, "dia": dia, "rol": rol, "que": R.ROL_QUE.get(rol, rol), "cubeta": cub,
|
||||||
"titulo": R.CUBETAS.get(cub, cub), "color": R.COLOR_CUBETA.get(cub, R.COLOR_CUBETA[""]),
|
"titulo": R.CUBETAS.get(cub, cub), "color": R.COLOR_CUBETA.get(cub, R.COLOR_CUBETA[""]),
|
||||||
"destino": destino, "crudo": [x for x in (PLANTILLAS.get(tipo, {}).get(cub, "") or "").split("/") if x],
|
"destino": destino, "crudo": [x for x in (PLANTILLAS.get(tipo, {}).get(cub, "") or "").split("/") if x],
|
||||||
"fecha": fecha, "por_fecha": origen, "camara": firma or "",
|
"fecha": fecha, "por_fecha": origen, "camara": firma or "",
|
||||||
|
|||||||
@@ -55,6 +55,13 @@ class Reglas(unittest.TestCase):
|
|||||||
# la plantilla de proyectos separa por programa
|
# la plantilla de proyectos separa por programa
|
||||||
self.assertIn("{app}", R.PLANTILLAS["contenido"]["PROYECTOS"])
|
self.assertIn("{app}", R.PLANTILLAS["contenido"]["PROYECTOS"])
|
||||||
|
|
||||||
|
def test_dia(self):
|
||||||
|
self.assertEqual(R.nombre_dia("2026-05-15"), "15- Viernes")
|
||||||
|
self.assertEqual(R.nombre_dia("2025-12-17"), "17- Miércoles")
|
||||||
|
self.assertEqual(R.nombre_dia("nada"), "")
|
||||||
|
# la plantilla de grabación lleva el día, que se rellena solo cuando hace falta
|
||||||
|
self.assertIn("{dia}", R.PLANTILLAS["contenido"]["GRABACION"])
|
||||||
|
|
||||||
def test_basura_y_proxies(self):
|
def test_basura_y_proxies(self):
|
||||||
self.assertTrue(R.es_basura_archivo("Thumbs.db"))
|
self.assertTrue(R.es_basura_archivo("Thumbs.db"))
|
||||||
self.assertTrue(R.es_basura_archivo(".DS_Store"))
|
self.assertTrue(R.es_basura_archivo(".DS_Store"))
|
||||||
@@ -242,6 +249,8 @@ class Servidor(unittest.TestCase):
|
|||||||
self.assertIn("12- Diciembre", d["destino"]) # la fecha sale del nombre de la cámara
|
self.assertIn("12- Diciembre", d["destino"]) # la fecha sale del nombre de la cámara
|
||||||
self.assertEqual(donde("Proyecto.aep")["destino"], os.path.join("01_Proyectos", "After Effects"))
|
self.assertEqual(donde("Proyecto.aep")["destino"], os.path.join("01_Proyectos", "After Effects"))
|
||||||
self.assertEqual(donde("cancion.wav")["cubeta"], "MUSICA")
|
self.assertEqual(donde("cancion.wav")["cubeta"], "MUSICA")
|
||||||
|
self.assertEqual(d["dia"], "17- Miércoles") # el 17-dic-2025 fue miércoles
|
||||||
|
self.assertEqual(donde("cancion.wav")["dia"], "") # el día solo aplica a grabaciones
|
||||||
self.assertTrue(donde("")["error"])
|
self.assertTrue(donde("")["error"])
|
||||||
|
|
||||||
# 4b-ter. renombrar una carpeta del mapa cambia TODAS las plantillas que pasan por ella,
|
# 4b-ter. renombrar una carpeta del mapa cambia TODAS las plantillas que pasan por ella,
|
||||||
|
|||||||
30
reglas.py
30
reglas.py
@@ -11,6 +11,7 @@ se guarda en datos/diccionario.json, donde la app va aprendiendo.
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import datetime
|
||||||
import unicodedata
|
import unicodedata
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@@ -70,6 +71,19 @@ def detectar_mes(nombre: str):
|
|||||||
return None, False
|
return None, False
|
||||||
|
|
||||||
|
|
||||||
|
DIAS = ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"]
|
||||||
|
|
||||||
|
|
||||||
|
def nombre_dia(fecha: str) -> str:
|
||||||
|
"""«2026-05-15» -> «15- Viernes». Misma forma que los meses: número delante para que ordene, y el
|
||||||
|
nombre detrás para saber de un vistazo si aquello fue entre semana o un sábado de rodaje."""
|
||||||
|
try:
|
||||||
|
a, m, d = (int(x) for x in fecha.split("-")[:3])
|
||||||
|
return f"{d:02d}- {DIAS[datetime.date(a, m, d).weekday()]}"
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def nombre_mes(mes: int) -> str:
|
def nombre_mes(mes: int) -> str:
|
||||||
"""convención de la casa: «MM- Mes» (la forma más usada en las carpetas actuales)"""
|
"""convención de la casa: «MM- Mes» (la forma más usada en las carpetas actuales)"""
|
||||||
return f"{mes:02d}- {MESES_TITULO[mes - 1]}"
|
return f"{mes:02d}- {MESES_TITULO[mes - 1]}"
|
||||||
@@ -509,15 +523,17 @@ TIPOS_CLIENTE = {
|
|||||||
"interno": "Es material de Labstream · se ordena por tipo de recurso",
|
"interno": "Es material de Labstream · se ordena por tipo de recurso",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Destinos relativos a la raíz del cliente. Variables: {anio} {mes} ("08- Agosto") {nombre} {sub}
|
# Destinos relativos a la raíz del cliente. Variables: {anio} {mes} ("08- Agosto") {dia} ("15- Viernes")
|
||||||
# {fuente} (la cámara) {app} (el programa del proyecto) {rel} (la ruta tal cual venía).
|
# {nombre} {sub} {fuente} (la cámara) {app} (el programa del proyecto) {rel} (la ruta tal cual venía).
|
||||||
|
# {dia} solo se rellena cuando en ese mes se grabó en más de un día: si todo el mes es un solo rodaje,
|
||||||
|
# la carpeta del día sobraría y desaparece sola.
|
||||||
# Una variable vacía se lleva consigo su segmento: «01_Proyectos/{app}/{nombre}» sin programa queda
|
# Una variable vacía se lleva consigo su segmento: «01_Proyectos/{app}/{nombre}» sin programa queda
|
||||||
# en «01_Proyectos/Reel botox».
|
# en «01_Proyectos/Reel botox».
|
||||||
PLANTILLAS = {
|
PLANTILLAS = {
|
||||||
"contenido": {
|
"contenido": {
|
||||||
"MARCA": "00_Marca/{sub}",
|
"MARCA": "00_Marca/{sub}",
|
||||||
"PROYECTOS": "01_Proyectos/{app}/{nombre}",
|
"PROYECTOS": "01_Proyectos/{app}/{nombre}",
|
||||||
"GRABACION": "{anio}/{mes}/00_Grabacion/{sub}",
|
"GRABACION": "{anio}/{mes}/00_Grabacion/{dia}/{sub}",
|
||||||
"PIEZA": "{anio}/{mes}/{nombre}",
|
"PIEZA": "{anio}/{mes}/{nombre}",
|
||||||
"CAMPANA": "Campañas/{anio}_{nombre}",
|
"CAMPANA": "Campañas/{anio}_{nombre}",
|
||||||
"EXPORTADOS": "{anio}/{mes}/_Exportados/{sub}",
|
"EXPORTADOS": "{anio}/{mes}/_Exportados/{sub}",
|
||||||
@@ -531,7 +547,7 @@ PLANTILLAS = {
|
|||||||
"GUION": "{anio}/{mes}/_Guiones/{sub}",
|
"GUION": "{anio}/{mes}/_Guiones/{sub}",
|
||||||
"FOTOS": "{anio}/{mes}/_Fotos/{sub}",
|
"FOTOS": "{anio}/{mes}/_Fotos/{sub}",
|
||||||
"DOCUMENTOS": "_Documentos/{sub}",
|
"DOCUMENTOS": "_Documentos/{sub}",
|
||||||
"CELULAR": "{anio}/{mes}/00_Grabacion/CELULAR/{sub}",
|
"CELULAR": "{anio}/{mes}/00_Grabacion/{dia}/CELULAR/{sub}",
|
||||||
"PROXY": "_Para borrar/proxies/{rel}",
|
"PROXY": "_Para borrar/proxies/{rel}",
|
||||||
"BASURA": "_Para borrar/basura/{rel}",
|
"BASURA": "_Para borrar/basura/{rel}",
|
||||||
"REVISAR": "_Revisar/{nombre}",
|
"REVISAR": "_Revisar/{nombre}",
|
||||||
@@ -539,7 +555,7 @@ PLANTILLAS = {
|
|||||||
"pauta": {
|
"pauta": {
|
||||||
"MARCA": "00_Marca/{sub}",
|
"MARCA": "00_Marca/{sub}",
|
||||||
"PROYECTOS": "01_Proyectos/{app}/{nombre}",
|
"PROYECTOS": "01_Proyectos/{app}/{nombre}",
|
||||||
"GRABACION": "Material/Grabaciones/{anio}/{mes}/{sub}",
|
"GRABACION": "Material/Grabaciones/{anio}/{mes}/{dia}/{sub}",
|
||||||
"PIEZA": "Campañas/{anio}_{nombre}",
|
"PIEZA": "Campañas/{anio}_{nombre}",
|
||||||
"CAMPANA": "Campañas/{anio}_{nombre}",
|
"CAMPANA": "Campañas/{anio}_{nombre}",
|
||||||
"EXPORTADOS": "_Exportados/{anio}/{sub}",
|
"EXPORTADOS": "_Exportados/{anio}/{sub}",
|
||||||
@@ -561,7 +577,7 @@ PLANTILLAS = {
|
|||||||
"proyecto": {
|
"proyecto": {
|
||||||
"MARCA": "00_Marca/{sub}",
|
"MARCA": "00_Marca/{sub}",
|
||||||
"PROYECTOS": "03_Proyecto/{app}/{nombre}",
|
"PROYECTOS": "03_Proyecto/{app}/{nombre}",
|
||||||
"GRABACION": "02_Grabacion/{anio}/{mes}/{sub}",
|
"GRABACION": "02_Grabacion/{anio}/{mes}/{dia}/{sub}",
|
||||||
"PIEZA": "04_Exportados/{nombre}",
|
"PIEZA": "04_Exportados/{nombre}",
|
||||||
"CAMPANA": "{anio}_{nombre}",
|
"CAMPANA": "{anio}_{nombre}",
|
||||||
"EXPORTADOS": "04_Exportados/{sub}",
|
"EXPORTADOS": "04_Exportados/{sub}",
|
||||||
@@ -583,7 +599,7 @@ PLANTILLAS = {
|
|||||||
"interno": {
|
"interno": {
|
||||||
"MARCA": "Marca/{sub}",
|
"MARCA": "Marca/{sub}",
|
||||||
"PROYECTOS": "Proyectos/{app}/{nombre}",
|
"PROYECTOS": "Proyectos/{app}/{nombre}",
|
||||||
"GRABACION": "Grabaciones/{anio}/{mes}/{sub}",
|
"GRABACION": "Grabaciones/{anio}/{mes}/{dia}/{sub}",
|
||||||
"PIEZA": "Piezas/{anio}/{nombre}",
|
"PIEZA": "Piezas/{anio}/{nombre}",
|
||||||
"CAMPANA": "Campañas/{anio}_{nombre}",
|
"CAMPANA": "Campañas/{anio}_{nombre}",
|
||||||
"EXPORTADOS": "Exportados/{anio}/{sub}",
|
"EXPORTADOS": "Exportados/{anio}/{sub}",
|
||||||
|
|||||||
@@ -1471,10 +1471,11 @@ const probarArchivo = seguro(async () => {
|
|||||||
const fecha = d.por_fecha === 'mtime'
|
const fecha = d.por_fecha === 'mtime'
|
||||||
? 'el nombre no trae fecha, así que el mes lo saca de la fecha del archivo'
|
? 'el nombre no trae fecha, así que el mes lo saca de la fecha del archivo'
|
||||||
: `el nombre dice que es del ${d.fecha}`;
|
: `el nombre dice que es del ${d.fecha}`;
|
||||||
|
const dia = d.dia ? ` Y si ese mes hubo más de un rodaje, dentro de «${d.dia}».` : '';
|
||||||
caja.innerHTML = `<div class="resp"><i class="pt" style="background:${d.color}"></i><div>` +
|
caja.innerHTML = `<div class="resp"><i class="pt" style="background:${d.color}"></i><div>` +
|
||||||
`<div><b>${esc(d.nombre)}</b> — ${esc(d.que)}${d.camara ? ` · cámara ${esc(d.camara)}` : ''}${d.app ? ` · proyecto de ${esc(d.app)}` : ''}</div>` +
|
`<div><b>${esc(d.nombre)}</b> — ${esc(d.que)}${d.camara ? ` · cámara ${esc(d.camara)}` : ''}${d.app ? ` · proyecto de ${esc(d.app)}` : ''}</div>` +
|
||||||
`<div class="d" style="margin:4px 0">${esc(raizN + SEP + d.destino)}${SEP}</div>` +
|
`<div class="d" style="margin:4px 0">${esc(raizN + SEP + d.destino)}${SEP}</div>` +
|
||||||
`<div class="mut" style="font-size:12px">${esc(fecha)}. Si el archivo está dentro de una carpeta que la app reconoce, esa carpeta puede mandar sobre esto.</div>` +
|
`<div class="mut" style="font-size:12px">${esc(fecha)}.${esc(dia)} Si el archivo está dentro de una carpeta que la app reconoce, esa carpeta puede mandar sobre esto.</div>` +
|
||||||
'</div></div>';
|
'</div></div>';
|
||||||
MAPAE_DESTINO = d.cubeta;
|
MAPAE_DESTINO = d.cubeta;
|
||||||
pintarMapaEst();
|
pintarMapaEst();
|
||||||
|
|||||||
Referencia en una nueva incidencia
Block a user