foresterdave

One way to update metadata using Python

Discussion created by foresterdave on Feb 24, 2012
Latest reply on Jan 18, 2013 by tracylemon
For those interested in using Python to update metadata I have figured out one way to do it.
The big trick is that this can't be done using the ESRI Python tools such as arcpy as those tools block access to the metadata fields.
My environment is SDE on Oracle.  Adjust this script as appropriate for your environment.



# SDMetadataChangeBasic.py
# 2/24/2012  David Anderson  Bighorn National Forest
# A python script to manipulate metadata values stored in SDE
# I run this using Python 2.6 that comes withArcGIS 10.0.  Running against a SDE version 9.3 installed onOracle 11.

# Import in the various modules that weill be used
import cx_Oracle
from xml.etree import ElementTree
import StringIO

# Establish a connection to the database.  For this script Oracle is the database storing the SDE data
ora_conn = cx_Oracle.connect('[s_r02_bnf]/@idb')  # privileged admin account that can edit metadata
ora_curs=ora_conn.cursor()

# SQL query to get metadata records to manipulate.  Note the for update clause at the end.  This is important to include
sql = "select xml from g_r02_bnf.gdb_usermetadata where name='Cultural_Districts' for update"
# Execute the query and pull the data into a Python variable
xml_cur=ora_curs.execute(sql)
xml_val=xml_cur.fetchone()

# Straightforward until here.
# Now comes a little bit of manipulation
# The goal is to get the XML from the SDe table into a ElementTree so it can be easily manipulated.
# ElementTrees are parsed from files, so need to make the table value look like a file.
# Luckily Python as a module for that.  StringIO
xml_string_file=StringIO.StringIO(xml_val[0])

# Create an XML tree variable
xml_tree = ElementTree.ElementTree()

# And put some data into the variable
xml_tree.parse(xml_string_file)

# WooHoo.  At this point we can now manipulate the metadata xml using the python xml module tools

# Lets go to some element to change
# Knowing the XML tags and the order to get to them requires knowledge about the metadata xml format.
# On option is to print the xml value (print xml_val[0]), copy and paste into a text file, save the file, then open that file with a browser such a firefox
# I use the XML browser tool in PLSQL developer from Oracle.
purpose_element = xml_tree.find("idinfo/descript/purpose")

# Change the value to whatever you want
purpose_element.text='A new purpose, a new hope.'

# Now time to reverse the process to put the updated XML back into the metadata table
# Need a place to put the updated xml.
# Create a new in memory file using the handy dandy StringIO module
xml_output_file=StringIO.StringIO()  #

# Use the ElementTree functionality to write out the updated xml
# The write command take a file as input, which is why use stringio to create something that looks like a file.
# Could actually do this with a real file.  Keeping all in memory seems cleaner and faster.
xml_tree.write(xml_output_file)

# Head back to the top of the file
xml_output_file.seek(0)

# Now read the data into a simple string variable
my_new_xml = xml_output_file.read()

# The above proces seems rather convulted process of converting.  But it works.

# One thing the write function does not seem to do is put in the correct header information so that the XML can be interpreted as XML.
# This is copied straight from the ESRI Metadata XML header
add_string='<?xml version="1.0"?><!--<!DOCTYPE metadata SYSTEM "http://www.esri.com/metadata/esriprof80.dtd">-->'
my_newer_xml = add_string + my_new_xml

# Now use the cx_Oracle functionality to write the data back into Oracle.
# To make this happen is why the for update clause was added to the select.  The update clause lets Oracle put a lock on the record.
# One quick check.  If the new xml is smaller than the original the Oracle LOB has to shrunk
if len(my_newer_xml) < xml_val[0].size():
   xml_val[0].trim(len(my_newer_xml))

xml_val[0].write(my_newer_xml)

# Oracle is transactional so the change has to be commited.
ora_conn.commit()

# Done
# It seems so easy and logical afterwards.

Outcomes