jueves, 3 de junio de 2021

Limpiar la denonimación de los intervalos con nombre importados de otra hoja de cálculo

Al hilo de los intervalos con nombre , cuando se copia una pestaña en otra hoja
de cálculo, resulta que los intervalos con nombre de dicha pestaña también se 
copian pero el nombre de dicho intervalo cambia, Google le pone un prenombre 
con la denominación de la pestaña, tal que así: mi_rango pasa a llamarse 
mipestaña!mi_rango.
A parte de quedar feo, da problemas en el código, ya que, por ejemplo, no puedes
ir a la pestaña donde está dicho rango.
 
Por lo tanto, he creado una función que se ocupa de limpiar los nombres de los
intervalos, quitándoles dicho prenombre y volviendo al nombre original.
 
function limpiar_intervalos_con_nombre (){
  // Conseguimos todos los intervalos con nombre de la hoja de cálculo
var herramienta = SpreadsheetApp.getActiveSpreadsheet()
var rangos = herramienta.getNamedRanges()

// Preparamos las variables necesarias: punto desde donde borrar el nombre, 
nuevo nombre con la segunda parte, rango a aplicar que es el mismo que tenía
var partir_por
var nuevo_nombre
var cual_es_el_rango

// Hacemos un bucle por intervalos con nombre
for (cada in rangos){
  // Comprobamos que tenga en el nombre un ! para pillar los intervalos importados
if (rangos[cada].getName().indexOf('!') !=-1){
  // Conseguimos el punto de corte, que es uno más siempre
 partir_por = rangos[cada].getName().indexOf('!')+parseInt(1)
 // Partimos el nombre viejo desde ese punto en adelante
  nuevo_nombre = rangos[cada].getName().substring(partir_por)
 // Guardamos el rango que tenía el intervalo con nombre
  cual_es_el_rango = rangos[cada].getRange()
//Borramos el intervalo con nombre
rangos[cada].remove()
// Lo volvemos a crear con nuevo nombre y el mismo rango
herramienta.setNamedRange(nuevo_nombre,cual_es_el_rango)
}
}

}

Rangos con nombre (intervalos con nombre) "GetRangeByName"

 Como ya explique en un artículo antiguo (Referencias a pestañas y/o celdas en código) es muy interesante utilizar los intervalos con nombre creándolos en la hoja de cálculo y referenciándolos en el código con .getRangeByName()

Como comenté en dicho artículo, la ventaja más evidente es que, aunque el usuario cambie el nombre de la pestaña o el lugar de una celda, el código sigue apuntando a donde le hemos dicho que apunte.

Para dejar más claro el problema: 

Si utilizo en el código 

var pestana = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('configuración')

corro el peligro que deje de funcionar si alguien cambia el nombre de pestaña, un peligro que se escapa de nuestras manos

Peor aún, si utilizo en el código esta referencia:

var resultado = pestana.getRange('B2').getValue()

Dejará de funcionar en cuanto añadamos alguna columna o fila antes de la B o de la fila 2

Para evitar todo esto, la solución es crear intervalos con nombre en la hoja de cálculo  y referenciarlos tal que así:

var pestana= SpreadsheetApp.getActiveSpreadsheet().getRangeByName('configuración')

var resultado = pestana.getRangeByName('el_de_la_celda_B2')

 

Pero... Nos hemos encontrado con una buena noticia y una mala en relación a estos rangos con nombre:

  • La buena: Cuando copiamos una pestaña en otra hoja de cálculo ("Copiar en" de la pestaña), los intervalos con nombre también se copian.. pero con un nombre mezcla de la pestaña y el nombre antiguo.
  • La mala: El código de referencia a estos rangos con nombre a veces no funciona, no encuentra dicho intervalo con nombre. La cuestión es que a veces funciona y a veces no. Y la única solución que he encontrado es borrar dicho intervalo y volverlo a crear.

jueves, 27 de mayo de 2021

Saltar errores y continuar con el código

Estoy haciendo código para conseguir quitar todos los permisos a un correo concreto en todos los archivos y subcarpetas de una carpeta dada. 

Para conseguir quitar permisos el código necesita una dirección de correo  tal que así: 

archivo.removeEditor(email)

Metiendo esta orden en un bucle que recorra todos los archivos nos permite conseguir nuestro objetivo.

Pero, a veces, resulta que no encuentra dicho correo y el código da error y NO SIGUE ADELANTE, se para.

Un truco para saltar por encima es utilizar este tratamiento de errores de Javascript:

try{archivo.removeEditor(email)} 

catch(e){}


Gracias a envolver nuestro código en try{} y añadir después catch(e){} logramos que el error no nos pare.

Entre las llaves de la segunda parte catch(e){}, podemos poner código que solamente corra cuando se da el error.



jueves, 4 de marzo de 2021

Recorrer carpetas y documentos en DRIVE

Este artículo tiene como objetivo mostrar el código básico para trabajar con Drive, es decir, para recorrer todos los archivos y subcarpetas de drive.

 

Ultimamente he estado trabajando con los permisos que damos a carpetas y documentos drive. 

Nuestra recomendaciones son:

  • No utilizar permisos en carpetas (la herencia es difícil de controlar)
  • No utilizar permisos globales, salvo que haya una razón importante
  • Utilizar google groups para gestionar los permisos

 

Debido a estas recomendaciones, ha habido centros que se han encontrado con la tarea de cambiar un montón de permisos anteriores, como por ejemplo: infinidad de carpetas, subcarpetas y documentos que tienen permiso de "cualquiera de educación con el enlace". Otro ejemplo es tener infinidad de permisos en documentos obsoletos a los que queremos hacer desaparecer, sin tener que meter horas yendo documento por documento y persona por persona quitando estos permisos.

Gracias al código, todas estas necesidades se pueden hacer de forma automática, simplemente dando la dirección de una carpeta y el código se ocupa de limpiar de permisos todo lo que contenga dicha carpeta.

He hecho diferentes funciones, según el objetivo a conseguir, pero en todas se ha repetido el proceso básico de ir recorriendo todos los documentos y subcarpetas incluidas en una carpeta dada. Por eso, me parece interesante hacer una especie de código básico que controle este proceso y, que con diferentes variantes, nos permita hacer infinidad de cosas con drive.

 

Lo vamos a hacer en tres funciones entrelazadas: función inicial, función para archivos y función para subcarpetas. La unión es la siguiente:

Empezamos con la función inicial y de ahí llamamos a la función de los archivos. Desde archivos llamamos a la función de subcarpetas. En esta última, en la función subcarpetas volvemos a llamar a la función archivos (que vuelve a llamar a subcarpetas, que vuelve a llamar a archivos..... hasta que no hay más)

Este recorrido es fundamental para poder recorrer todos los archivos y subcarpetas que haya dentro de una carpeta.

 

Dentro de cada función estas son las líneas básicas:

 

Inicio:

 // Conseguimos la carpeta principal

 var carpeta_global =DriveApp.getFolderById(id_general)

// Llamamos a la función archivos

archivos(carpeta_global)

Archivos:

 // Conseguimos todos los archivos de la carpeta

  var archivos_global = carpeta_global.getFiles() 

// Los recorremos todos con un bucle

  while (archivos_global.hasNext()){

// Apuntamos a cada archivo (dentro del bucle)

var archivo= archivos_global.next();

// Con cada archivo podemos hacer lo que necesitemos, por ejemplo, 

archivo. setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.NONE) //quitamos todos los permisos globales

}

// Una vez terminado con todos los archivos de esta carpeta llamamos a la función  de subcarpetas, fuera del bucle

   subcarpeta(carpeta_global)

Subcarpetas: 

// Conseguimos todas las subcarpetas que haya dentro

  var sub_carpetas = carpeta_global.getFolders()
// Hacemos el bucle para recorrerlas
     while (sub_carpetas.hasNext()) {

// Apuntamos a cada una de ellas
       var subcarpeta = sub_carpetas.next()

// Con cada subcarpeta podemos hacer muchas cosas, por ejemplo,

subcarpeta.removeEditor(correo) // Eliminamos un editor concreto

 // y dentro del bucle llamamos a la función archivos, PERO, pasando cada subcarpeta, en vez de la carpeta_global

archivos(subcarpeta)

lunes, 4 de enero de 2021

Conseguir fecha legible

En otros artículos anteriores ya he hablado de las fechas y como tratarlas, pero siempre buscando trucos para conseguirlo.

Ahora os traigo, lo que espero que sea, la solución definitiva. Además con alternativas para diferentes idiomas:

Euskaraz egitean, deklinazio arazo txiki bat dugu. Hortaz, bide luzetxoa egin behar dugu:

  var marca_temporal_mes = marca_temporal.toLocaleString('eu-ES',{month:'long'})
  var marca_temporal_ano= marca_temporal.toLocaleString('eu-ES',{year: 'numeric'})
  var marca_temporal_dia= marca_temporal.toLocaleString('eu-ES',{day:'2-digit'})
  Logger.log(marca_temporal_ano+'(e)ko '+marca_temporal_mes+'ren '+marca_temporal_dia)

En  castellano, nos basta con definir cada parte de la fecha en una única orden:

  var mi_marca =marca_temporal.toLocaleString('es-ES',{year: 'numeric',month:'long',day:'2-digit'})
  Logger.log(mi_marca)

En inglés americano, igual que en castellano, lo hacemos en una sola orden:

var my_time = marca_temporal.toLocaleString('en-US',{year: 'numeric',month:'long',day:'2-digit'})

Logger.log(my_time)

En inglés británico, cambiamos simplemente de 'en-US' a 'en-GB'

var my_time = marca_temporal.toLocaleString('en-GB',{year: 'numeric',month:'long',day:'2-digit'})

Logger.log(my_time)

 Referencia:

 toLocaleString

 Artículos antiguos:

Trabajar con fechas

Problemas con la marca temporal

jueves, 17 de diciembre de 2020

Insertar logo en pestaña (código y proceso extraño)

Estoy haciendo una aplicación para crear actas y necesito que en la pestaña de cada acta aparezca como encabezado el logo del centro.

Quiero, además, que la imagen del logo esté en drive.

Como cada pestaña con cada acta se crea por código, no me vale el menú insertar-imagen-en celda, ya que, que yo sepa, no hay forma de hacerlo desde código.

Pero si puedo utilizar la función "image(url;[]) 

Pero la url que nos da drive de una imagen, utilizando "conseguir enlace" no funciona con esta función. 

Me funciona, sin embargo, la combinación de https://drive.google.com/uc?export=view&id= con el id de cada imagen.

Para hacerle la vida más fácil al usuario, le pido la url de "conseguir enlace" y mediante este trozo de código la convierto al tipo de enlace que he descrito en el anterior párrafo:

  

  var herramienta = SpreadsheetApp.getActiveSpreadsheet()

  var imagen_url = herramienta.getRangeByName('imagen').getValue()
  var imagen_primera_parte = "https://drive.google.com/uc?export=view&id="       
  var imagen_id imagen_url.match(/[-\w]{25,}/)
  var imagen = imagen_primera_parte+imagen_id

pestana.getRange('A1').setValue('=image("'+imagen+'";1)')

 

El código funciona perfectamente: desde la url extrae el id de la imagen y después la añade al enlace especial. Con la última fila del código, insertamos la imagen en la celda A1 con la función image.


Pero, me he encontrado con la SORPRESA que hay veces que la imagen aparece y otras que no.

Después de investigar y probar diferentes situaciones, me he encontrado con que aparece siempre la imagen si utilizamos un proceso muy particular.

 El proceso funciona así:

  1. Debemos subir la imagen desde el ordenador
  2. Debemos darle permiso público a la imagen(cualquiera con el enlace)

Sorprendentemente, no me ha funcionado con imágenes que ya estaban de antemano en mi Drive y, por supuesto, ninguna imagen que no tenga dado el permiso público de antemano.

lunes, 30 de noviembre de 2020

Regexmatch (Comportamiento extraño de la fórmula?!)

¡Cuidado con el orden de los datos en la fórmula!

El resultado no es el mismo

La formula regexmatch la traducimos así:  Resultado de(texto donde se busca; texto a buscar)

 Hay que tener claro  que en la primera parte se define "donde" "dentro de" 

Y en la segunda parte el "que"

Os planteo un caso real en la herramienta de reservas del aula de informática en un centro con horario tanto de mañana como de tarde. Esto es importante ya que las horas a reservar van desde las 8:30 a las 20:30

Vamos a ver si ( 18:30; todas las horas) nos da dos resultados: 8:30 y 18:30

En este busca cada hora del día si cabe en "18:30" y encuentra que tanto 8:30 como 18:30 caben

Pero, al revés, ( todas las horas; 18:30) nos da solamente 18:30

En este caso busca la hora 18:30 si cabe en alguna hora del día y solamente encuentra 18:30

En resumen: 

El orden de los parámetros de la fórmula es al revés de como lo formulamos, primero en donde buscamos y, segundo el qué.


Crear PDF horizontal

El código que vamos a mostrar aquí no ha sido creado por nosotros, sino que lo hemos encontrado en Internet. Objetivo: Publicar una pesta...