Acceso a los Adjuntos de un servicio

880
0
02-27-2023 05:29 PM
OscarSolis1
Esri Contributor
2 0 880

A veces es necesario poder administrar los adjuntos de alguna forma, en este artículo abarcaremos ciertos conceptos y más adelante en mi github se tendrán varios ejemplos que pueden modificar a su gusto.

Para empezar, para poder acceder a los servicios de ArcGIS Enterprise (Portal) o ArcGIS Online se requiere la librería de arcgis, así que si no la tienen instalada deben hacerlo (viene incluida cuando se instala ArcGIS Pro o dentro del Python de ArcGIS Enterprise).

Esta primera sección les permitirá acceder a su Portal o ArcGIS Online

#Conectarse al Portal
PORTAL = GIS("url", "usuario","contraseña")

Dependiendo lo que estén haciendo puede ser necesario obtener el token, esto se usa básicamente cuando quieres ver la url de los adjuntos y el servicio no se encuentra público.

token = PORTAL._con.token

Luego de esto ya podemos acceder al servicio, el api de arcgis permite acceder a los objetos almacenados en el Portal o ArcGIS Online, para ello desde nuestro sitio accederemos a la dirección del servicio (es el que tiene el ícono amarillo con rojo). (Es un texto por lo que debe ir entre comillas)

servicio = PORTAL.content.get('item id')

Luego cuando accedemos a esta parte podemos ver las diferentes capas y tablas que posee el servicio que estamos consultando, por ende se requiere colocar el número que indica el índice de capa o tabla, y acá debo aclarar, que si tenemos por ejemplo: una entidad geográfica y una tabla ambas tendrán el índice 0, esto porque el sistema detecta como una lista de capas y una lista de tablas dentro del servicio, por ende, en el caso de existir una capa y una tabla ambas tienen el índice 0. Ahora bien si se tienen dos capas y una tabla, habrá una capa con un índice 0, la otra con 1 y la tabla será 0. Espero no causar confusión con esto jeje.

El asunto es que para parametrizar hice lo siguiente:

num_capa=int(indice)

Nota: es un dato numérico por lo que simplemente escriben el valor

Como indicaba anteriormente pueden existir capas o tablas, de esta forma, también puede ser que elos adjuntos se encuentren en una capa o una tabla, por lo que me vi en la necesidad de agregar algo así a la herramienta:

tipo="aca se escribe capa o tabla"
if tipo=="capa":
capa=(servicio.layers)[num_capa]
elif tipo=="tabla":
capa =(servicio.tables)[num_capa]

Para consutruir la URL, la misma API de Esri nos indica como obtenerla, sólo que no lo hace como un texto sino como un objeto, así que hice lo siguiente usando la librería de python re:

cap_url=str(re.findall('htt.*'+str(num_capa), str(capa)))
cap_url=str(cap_url)[2:-2]

De esta forma transforma el objeto en un string (texto)

Ahora, debemos identificar la carpeta en la vamos a guardar los adjuntos
carpeta=r"dirección de la carpeta deseada"

Otro parámetro que uso es un campo de texto vacío donde almacenaré la dirección en donde se almacenan los adjuntos, de esa forma se puede parametrizar con el dataframe de pandas y justo lo que haremos acá:

sdf = capa.query(where="nombre del campo"+" IS NULL").sdf

De esa forma la consulta irá únicamente hacia los que tienen ese campo vacío

Luego de esto se genera una lista vacía que capturará los registros que vayamos a incluir dentro de una edición, esto va a ser muy importante más adelante ya que permite ingresar los datos dentro de la lista vacía y enviarlos al método de edición, de acuerdo con lo que estemos haciendo.

elementos_a_actualizar = []

Ahora sí debemos iterar en la capa o tabla que tiene los adjuntos

#Iterar a través del dataframe
for index, row in sdf.iterrows():
       objID=str("campo que contiene el objectid")
       Attachments = capa.attachments.get_list(oid=objID)

Dentro de la misma iteración coloqué un if, como pueden observar en las líneas anteriores se crea una lista en el Attachments, por lo tanto, una característica de las listas es que podemos contar la cantidad de elementos y precisamente eso hace el if, ya que le consultaremos únicamente donde esta lista es mayor que 0, así estaremos doblemente seguros de que vamos a trabajar únicamente con datos que tienen adjuntos.

if len(Attachments)>0:

Dentro de esta condición vamos a iterar los adjuntos correspondientes a cada ObjectId de la capa principal:

for k in range(len(Attachments)):
             attachmentId = Attachments[k]['id']
             attachmentName = Attachments[k]['name']
             img_url = cap_url+"/{0}/attachments/{1}".format(objID,attachmentId)+"?token="+token
             fileName = os.path.join(carpeta, attachmentName)

Como pueden observar, se obtiene el Id del adjunto y el nombre, con esto concateno la url obtenida anteriormente con el objectid de la capa o tabla y el Id del adjunto. Así se obitene una URL única para cada adjunto.

También con esto es posible concatenar la dirección de la carpeta que tenía anteriormente y el nombre del archivo que tiene el adjunto.

Con esto se puede jugar un poco, en este caso voy a mostrarles algo para que el sistema sepa si ya se descargó un adjunto con el mismo nombre en la carpeta que le están indicando, por ende, no descargaría ese adjunto. Pero también es posible decirle que cree una carpeta por cada elemento y que en esa descargue todos los adjuntos correspondientes a ese registro. En este caso lo haré para que creé el archivo, en otra ocasión les muestro como crear la carpeta, que dicho sea de paso se encuentra en el script de descarga adjuntos del github.

Bueno como decía, acá la parte para revisar si el adjunto existe:

existe_archiv=os.path.exists(fileName)
if existe_archiv is True:
      arcpy.AddMessage("No descargar")
else:
      arcpy.AddMessage("Descargar ShapeFile "+attachmentName)

Como pueden observar uso la librería os y arcpy en esta parte, la de os es para revisar la existencia o no del archivo en la carpeta que le indicamos y el arcpy en este caso es para devolver un mensaje dentro del ArcGIS, que dicho sea de paso si no lo usan pueden reemplazar acá el arcpy.AddMessage por print.

Ahora ya con la URL podemos descargar los adjuntos


try:
    arcpy.AddMessage("Obteniendo la URL del ShapeFile")
    request=urllib.request.urlretrieve(img_url)
    req = requests.get(img_url)
    file = open(fileName, 'wb')
    for chunk in req.iter_content(100000):
    file.write(chunk)
    file.close()
  arcpy.AddMessage("ShapeFile "+attachmentName+" descargado en la carpeta "+carpeta+ " de forma correcta")

Acá borra el adjunto de la capa
    capa.attachments.delete(objID,attachmentId)

Acá vamos a capturar el error para mostrarlo en el sistema (en mi caso estaba descargando shapefiles, por eso el mensaje dice eso.


except urllib.error.HTTPError as err:
   arcpy.AddMessage("Error "+str(err.code)+" no existe un ShapeFile para descargar o existe un problema de conexion")

En este momento, y dentro de la identación del if (donde estábamos pidiendo que nos devolviera donde la lista tenía más de 0 elementos) usaremos el método que capturará los datos y los anexará a nuestra lista vacía.

#Actualizar el campo de la direccion de la carpeta
seleccion=capa.query(where="campo del objectid"+"="+objID)
elem_actualizar=(seleccion.features[0])
elem_actualizar.attributes["nombre del campo"]=str(fileName)

Ahora anexaremos los registros que vamos capturando que tienen adjuntos y el campo que contendrá la dirección vacía
elementos_a_actualizar.append(elem_actualizar)

Luego de ir anexando estos datos en la lista vacía, con este método, procederemos a usar la lista para editar los elementos. Esto se hace afuera de la iteración del dataframe, o sea está por fuera de todo.

capa.edit_features(updates=elementos_a_actualizar)

Como pueden observar, updates indica que es igual a la lista vacía que se había indicado anteriormente.

Y eso es todo, tengo varios ejemplos en mi GitHub, en estos descargo adjuntos y los elimino del servicio, o simplemente los descargo, o en otro caso, descargo adjuntos que son shapefiles y los adjunto a otro servicio como nuevas entidades,con gusto les puedo explicar cómo funcionan más adelante.

Otra cosa muy importante que debo aclarar es que el toolbox que se encuentra adjunto es para versión 3 en adelante, si se tiene una versión anterior a 2.9, pues hay que configurar el toolbox, es por eso que también adjunto los scripts y puedan personalizarlo. Dejo acá un enlace que es una pequeña guía para configurar herramientas con versiones inferiores a 2.9

Espero que les ayude