Fixing broken links on multiple mxds

3057
2
Jump to solution
03-22-2013 07:09 AM
KenLucas
Occasional Contributor
I looked thru the esri sample code and determined that the "replaceDatasource" function may fix broken links. However, I need to modify the script to fix broken links in many mxds. I hoping someone can spot the problem in this script:

import arcpy, os, glob mxds_path = r'S:\\Users Shared\\LOpperman\\Town Scale MXDs\\MXDs\\targetlayers\\' layer_name = r'dams' data_frame = r'Primary'  for infile in glob.glob(os.path.join( mxds_path, "*.mxd" )):          mxd = arcpy.mapping.MapDocument(infile)           for df in arcpy.mapping.ListDataFrames(mxd, data_frame):              for lyr in arcpy.mapping.ListLayers(mxd, "", df):                lyr.replaceDataSource("dams")       mxd.save()
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
T__WayneWhitley
Frequent Contributor
See the code below; this is an oversimplified example - certainly not what I ended up using to replace SDE data sources (and other misc sources) in one fell swoop, but this is meant to make a 'combined' illustration of the use of os.walk and glob techniques to do something like replace the same file gdb data source (replacing in a data frame 'Layers', a layer 'parcels' data source with a file gdb fc 'parcels_improved').
The case I'll use here is just to keep the code 'clean' and not overshadow the concept of applying the same replacement in 'walking' the directory:
import arcpy, os, glob mxds_path = r'\\someServer\someRoot' workspace = r'\\someServer\data\fileGeodata\test.gdb'  # initializing empty list var to hold set of mxd pathnames mxds = []   for dirpath, dirnames, filenames in os.walk(mxds_path):     mxds = glob.glob(os.path.join(dirpath, '*.mxd'))  #loads list at dirpath level     for each_mxd in mxds:             mxd = arcpy.mapping.MapDocument(each_mxd)             for df in arcpy.mapping.ListDataFrames(mxd, "Layers"):                 for lyr in arcpy.mapping.ListLayers(mxd, "parcels", df):                     lyr.replaceDataSource(workspace, "FILEGDB_WORKSPACE", "parcels_improved")             mxd.save()     mxds = [] # reinitializes list for next dirpath level


Essentially, Ken, this combines the orig technique you were using in your 1st post with 'glob' and the technique Lucas demonstrated with os.walk (I used the vars, "dirpath, dirnames, filenames", equivalent to Lucas's use of "root, dirs, files").

The important distinction to note here is that os.walk 'drills down' to sub-directories, not just the map docs that would load the list at the 'root' level directory (defined by the pathname var, mxds_path).  This is why the mxds list var is 'reloaded' in the loop - there may be other ways to use glob techniques more direct than I have illustrated, but this at least points out the difference between the orig code and the walk code, and gives you at least 1 alternative.  Otherwise, if you prefer, use Lucas's "for, for, if" structure.  The 1st 'for' is pretty much identical; the second 'for' is where things are changed a bit...

If using Lucas's code, just have to remember to 'join' the filename to the dir rootname to get the mxd pathname in order to set the mxd object, as in:
for root, dirs, files in os.walk(inDir):      for name in files:           if name.endswith(".mxd"):                mxd = arcpy.mapping.MapDocument(os.path.join(root, name))                #continue code here


Hope that helps.  ...and actually, the code using the 'glob' technique can be shortened and python can take care of init/reinit of the list - the list var does not have to be explicitly set, so here is the equivalent of the os.walk code portion above 'aided' with glob:
for root, dirs, files in os.walk(inDir):      for each_mxd in glob.glob(os.path.join(root, '*.mxd')):           mxd = arcpy.mapping.MapDocument(each_mxd)           #continue code here


Enjoy,
Wayne

View solution in original post

0 Kudos
2 Replies
LucasDanzinger
Esri Frequent Contributor
Ken,

You might try using os.walk to walk through a directory and find all of your MXDs. Then use your code to check if it has an old data source and if it does, switch it out.

import os
inDir = r"C:\Shared"
for root, dirs, files in os.walk(inDir):
 for name in files:
  if name.endswith(".mxd"):
0 Kudos
T__WayneWhitley
Frequent Contributor
See the code below; this is an oversimplified example - certainly not what I ended up using to replace SDE data sources (and other misc sources) in one fell swoop, but this is meant to make a 'combined' illustration of the use of os.walk and glob techniques to do something like replace the same file gdb data source (replacing in a data frame 'Layers', a layer 'parcels' data source with a file gdb fc 'parcels_improved').
The case I'll use here is just to keep the code 'clean' and not overshadow the concept of applying the same replacement in 'walking' the directory:
import arcpy, os, glob mxds_path = r'\\someServer\someRoot' workspace = r'\\someServer\data\fileGeodata\test.gdb'  # initializing empty list var to hold set of mxd pathnames mxds = []   for dirpath, dirnames, filenames in os.walk(mxds_path):     mxds = glob.glob(os.path.join(dirpath, '*.mxd'))  #loads list at dirpath level     for each_mxd in mxds:             mxd = arcpy.mapping.MapDocument(each_mxd)             for df in arcpy.mapping.ListDataFrames(mxd, "Layers"):                 for lyr in arcpy.mapping.ListLayers(mxd, "parcels", df):                     lyr.replaceDataSource(workspace, "FILEGDB_WORKSPACE", "parcels_improved")             mxd.save()     mxds = [] # reinitializes list for next dirpath level


Essentially, Ken, this combines the orig technique you were using in your 1st post with 'glob' and the technique Lucas demonstrated with os.walk (I used the vars, "dirpath, dirnames, filenames", equivalent to Lucas's use of "root, dirs, files").

The important distinction to note here is that os.walk 'drills down' to sub-directories, not just the map docs that would load the list at the 'root' level directory (defined by the pathname var, mxds_path).  This is why the mxds list var is 'reloaded' in the loop - there may be other ways to use glob techniques more direct than I have illustrated, but this at least points out the difference between the orig code and the walk code, and gives you at least 1 alternative.  Otherwise, if you prefer, use Lucas's "for, for, if" structure.  The 1st 'for' is pretty much identical; the second 'for' is where things are changed a bit...

If using Lucas's code, just have to remember to 'join' the filename to the dir rootname to get the mxd pathname in order to set the mxd object, as in:
for root, dirs, files in os.walk(inDir):      for name in files:           if name.endswith(".mxd"):                mxd = arcpy.mapping.MapDocument(os.path.join(root, name))                #continue code here


Hope that helps.  ...and actually, the code using the 'glob' technique can be shortened and python can take care of init/reinit of the list - the list var does not have to be explicitly set, so here is the equivalent of the os.walk code portion above 'aided' with glob:
for root, dirs, files in os.walk(inDir):      for each_mxd in glob.glob(os.path.join(root, '*.mxd')):           mxd = arcpy.mapping.MapDocument(each_mxd)           #continue code here


Enjoy,
Wayne
0 Kudos