Replicating Feature Class from EGDB on ArcGIS Online

471
5
01-11-2018 06:47 AM
PatrickFilyer
Occasional Contributor II

I would like to sync a Feature Class stored on an EGDB to a Feature Service on AGOL. The feature class is edited on a daily basis and it would be nice if the feature service is automatically synced with the changed made to the database (instead of having to overwrite the service daily). Ideally, I would like to preserve the feature services offline sync capabilities as well. 

Is this possible?

Tags (1)
0 Kudos
5 Replies
AndrewValenski__IT_
Occasional Contributor III

There're a few options to accomplish this.

If you have a Web GIS deployment, you can try out AGOL-Portal collaboration, in which a features service with the sync capability published to your in-house ArcGIS for Server instance sends a real-time (or scheduled) replica to AGOL through your Portal. This does, however, require a Full Web GIS. 

If not, you can have a custom script , something like this: 

def main():
    # DEFINE PARAMETERS HERE!!!!!
    folder = "INPUT FOLDER HERE
    AGOL_username = "PUT YOUR AGOL USERNAME HERE"
    AGOL_password = "PUT YOUR PASSWORD HERE"

    #import required modules
    import os, arcpy
    import xml.dom.minidom as DOM
    arcpy.env.overwriteOutput = True

    # Set folder containing MXDs from parameters
    mxdFolder = folder

    # Sign in to AGOL using provided credentials
    # The Sign In To Portal tool reads the URL set in Desktop Administrator.
    # Any user entered value is ignored.
    # The default value is URL for the ArcGIS portal currently chosen by the user in Desktop Administrator.
    arcpy.SignInToPortal_server(AGOL_username, AGOL_password)

    # Acquire a list of files in the MXD folder
    filesInMxdFolder = os.listdir(mxdFolder)

    # Add MXD files to a list to avoid other filetypes
    mxdFiles = []
    for file in filesInMxdFolder:
        if file.endswith(".mxd"):
            mxdFiles.append(file[:-4])
    print mxdFiles

    #Begin the workflow of publishing each MXD, one at a time
    for mxd in mxdFiles:
        print "Beginning publish for " + mxd + ".mxd"

        #Prepare necessary parameters for publish
        mapDoc = arcpy.mapping.MapDocument(mxdFolder + mxd + ".mxd")
        service = mxd
        sddraft = mxdFolder + service + '.sddraft'
        newSDdraft = mxdFolder + service + '_updated.sddraft'
        sd = mxdFolder + service + '.sd'

        #Determine MXD summary and tags. If not present, set MXD name to value
        summary = mapDoc.summary
        if summary == "":
            summary = mxd
        tags = mapDoc.tags
        if tags == "":
            tags = mxd

        # Create service definition draft. This is an XML file that holds service properties.
        # This file is deleted once you actually publish the service
        arcpy.mapping.CreateMapSDDraft(mapDoc, sddraft, service, 'MY_HOSTED_SERVICES', summary=summary, tags=tags)
        print "\tThe SD Draft was created for " + mxd + ".mxd"

        # Parse the XML within the SDDraft to modify parameters
        doc = DOM.parse(sddraft)

        # XML 1 - 5
        # If publishing services for the first time, comment out 1-2 and run 3-5
        # If overwriting services, run 1-5

        # XML 1) Get Tags and set to replacement (overwrite)
        tagsType = doc.getElementsByTagName('Type')
        for tagType in tagsType:
            if tagType.parentNode.tagName == 'SVCManifest':
                if tagType.hasChildNodes():
                    tagType.firstChild.data = "esriServiceDefinitionType_Replacement"

        # XML 2) Change to already published
        tagsState = doc.getElementsByTagName('State')
        for tagState in tagsState:
            if tagState.parentNode.tagName == 'SVCManifest':
                if tagState.hasChildNodes():
                    tagState.firstChild.data = "esriSDState_Published"

        # XML 3) Change service type from map service to feature service
        typeNames = doc.getElementsByTagName('TypeName')
        for typeName in typeNames:
            if typeName.firstChild.data == "MapServer":
                typeName.firstChild.data = "FeatureServer"

        # XML 4) Turn off caching
        configProps = doc.getElementsByTagName('ConfigurationProperties')[0]
        propArray = configProps.firstChild
        propSets = propArray.childNodes
        for propSet in propSets:
            keyValues = propSet.childNodes
            for keyValue in keyValues:
                if keyValue.tagName == 'Key':
                    if keyValue.firstChild.data == "isCached":
                        keyValue.nextSibling.firstChild.data = "false"

        # XML 5) Turn on feature access capabilities
        configProps = doc.getElementsByTagName('Info')[0]
        propArray = configProps.firstChild
        propSets = propArray.childNodes
        for propSet in propSets:
            keyValues = propSet.childNodes
            for keyValue in keyValues:
                if keyValue.tagName == 'Key':
                    if keyValue.firstChild.data == "WebCapabilities":
                        keyValue.nextSibling.firstChild.data = "Query"

        # Write the new draft to disk to persist the changes
        f = open(newSDdraft, 'w')
        doc.writexml( f )
        f.close()

        # Analyze the service definition draft to check for errors and report warnings
        analysis = arcpy.mapping.AnalyzeForSD(newSDdraft)
        print "\tThe SD Draft was analyzed for " + mxd + ".mxd"

        # Print errors, warnings, and messages returned from the analysis
        print "\tThe following information was returned during analysis of the MXD:"
        for key in ('messages', 'warnings', 'errors'):
          print '\t----' + key.upper() + '---'
          vars = analysis[key]
          for ((message, code), layerlist) in vars.iteritems():
            print '    ', message, ' (CODE %i)' % code
            print '       applies to:',
            for layer in layerlist:
                print layer.name,
            print
        print "\tError analysis complete"

        # Stage and upload the service if the sddraft analysis did not contain errors
        if analysis['errors'] == {}:
            print "\tBeginning stage service for " + mxd + ".mxd"
            # Execute StageService. This creates the service definition.
            arcpy.StageService_server(newSDdraft, sd)

            print "\tBeginning Upload Service Definition for " + mxd + ".mxd"
            # Execute UploadServiceDefinition. This uploads the service definition and publishes the service.
            arcpy.UploadServiceDefinition_server(in_sd_file=sd, in_server="My Hosted Services")
            print "\tService successfully published for " + mxd + ".mxd"
        else:
            print "\tService could not be published because errors were found during analysis for " + mxd + ".mxd"

        print arcpy.GetMessages()

if __name__ == '__main__':
    main()

Ideally, Esri will extend their Cloud support to basically enable cloud-based databases to become registered, but that's a pipe dream as of today

PatrickFilyer
Occasional Contributor II

Thanks for the detailed response Andrew. I will probably use the second method. Do you know if users will experience sync errors if they have a synced copy of the replicated layer on collector (i.e. they will not be able to sync the data stored offline on their device if the replicated layer is updated). 

0 Kudos
AndrewValenski__IT_
Occasional Contributor III

Hmpf. I'm not sure honestly. I suppose it will depend on if AGOL honors the globalIDs. In other words, if when you overwrite the features the GlobalIDs are preserved by AGOL consistently I'd expect the syncing from offline devices to work just fine. If, however, they get jacked up by AGOL, then I'd expect issues.

I'm also not sure how the hosted data is actually handled by the EC2 instances behind AGOL. When you sync an offline replica, I'd imagine that the syn only shoots over items from the A & D tables and doesn't mess with the other data. But, if the sync process detects an unexpected conflict (i.e. more things exist in the AGOL feature layer than expected), I'm not sure how the system responds.

In short, this would have to be tested. Was that useful or am I crazy guy rambling again?

PatrickFilyer
Occasional Contributor II

Haha no you aren't crazy. It makes sense. I am going to test this eventually and I will let you know what happens. 

Thanks again ! 

0 Kudos
Juan_ManuelAngel
New Contributor III

Try to insert this code to preserve the Global Id's

import arcpy

arcpy.env.preserveGlobalIds = True