Automatiser les opérations de réconciliation et de réinjection pour les données de branche versionnée pour lesquelles la synchronisation est activée

Les administrateurs de portail et les administrateurs de version peuvent utiliser les scripts Python pour automatiser une réconciliation planifiée des versions de réplica après la synchronisation des mises à jour avec les données faisant partie d’une branche versionnée par les équipes sur le terrain.

Ce processus est applicable pour la gestion des versions de réplica créées à l’aide d’un service d’entités pour lequel la synchronisation est activée avec des données de branche versionnée.

En savoir plus sur l’utilisation de cartes hors connexion avec des données de branche versionnée

Scénarios applicables

L’exemple Python de la section qui suit utilise le service d’entités ArcGIS REST API et des outils de géotraitement ArcPy pour réconcilier et réinjecter des versions de réplica avec des règles attributaires en place. La section suivante décrit quel exemple de code utiliser et l’opération effectuée par l’exemple de script.

Remarque :

Ce script est un exemple de processus avec une configuration des données spécifique. Vos processus peuvent ne pas correspondre exactement à ce modèle de données, mais vous pouvez malgré tout utiliser les concepts et la logique du script pour personnaliser un script automatique pour votre organisation. Par exemple, vous ne pourrez peut-être pas utiliser les règles de validation, mais pouvez modifier le script pour supprimer l’évaluation des règles de validation. Ainsi, lors de l’utilisation des règles de validation, il est possible, dans certains cas, que vous souhaitiez réconcilier, exécuter une validation et réinjecter chaque version individuellement avant l’opération d’évaluation pour vous assurer que la validation inclut les mises à jour effectuées par d’autres éditeurs.

Conditions préalables :

Dans cet exemple, la configuration des données est la suivante :

  • La synchronisation du service d’entités avec des données de branche versionnée doit être activée avec l’option Create a version for each downloaded map (Créer une version pour chaque carte téléchargée) sélectionnée, ce qui nécessite ArcGIS Enterprise 10.8.1 ou version ultérieure.
  • Des réplicas ont été créés sur le service lors de la mise hors connexion d’une carte.
  • La fonction de serveur de gestion des versions a été activée sur le service.
  • Les identifiants de connexion fournis pour l’utilisateur du portail dans le script doivent être adaptés à un membre du rôle d’administrateur de portail par défaut ou d’un rôle personnalisé doté du privilège de gestion des versions.
  • Dans cet exemple, des règles attributaires de validation sont également appliquées aux données et la fonction du serveur de validation est activée. Cela n’est pas nécessaire pour utiliser des versions de réplica, mais permet de garantir l’intégrité des données.

  1. Pour obtenir une liste des versions de réplica du service d’entités, utilisez la ressource replicas dans ArcGIS REST API.
  2. Utilisez la ressource versionsInfos dans ArcGIS REST API pour obtenir les propriétés de la version. Grâce à ces propriétés, créez un filtre pour obtenir uniquement les versions dans lesquelles la date d’évaluation est nulle ou la date de modification est supérieure à la date de la dernière évaluation (ce qui signifie que des mises à jour ont été apportées depuis la dernière évaluation). Ajoutez les noms des versions qui correspondent au filtre à la liste listOfVersionsToValidate qui sera évaluée.
  3. Pour chaque version de la liste d’évaluation, utilisez l’opération evaluate dans ArcGIS REST API pour évaluer les règles attributaires de validation sur le service. Si l’évaluation aboutit sans erreur, la version est prête pour la réconciliation et la réinjection. Si l’évaluation aboutit mais que des erreurs sont renvoyées, ne réinjectez pas les mises à jour ; générez un message indiquant que les erreurs peuvent être inspectées et résolues manuellement.
  4. Dans le cas de versions dont l’évaluation a abouti sans erreur, exécutez l’outil Réconcilier des versions avec l’option de réinjection des mises à jour.
  5. Exécutez l’outil Réconcilier des versions avec l’option de réconciliation uniquement (pas de réinjection) sur toutes les versions de réplica.

Exemple de code

L’exemple de code Python complet ci-dessous effectue les opérations indiquées plus haut. Ce script inclut une option permettant de générer un fichier journal qui capture la sortie de chaque opération terminée et qui peut être affiché une fois le script exécuté.

# Import modules
import arcpy, traceback, urllib, json, urllib.request, urllib.parse, os, urllib.error, datetime

# Overwrite the reconcile log output each time the script is run
arcpy.env.overwriteOutput = True

# Script parameters
serviceName = "MyServiceName"
baseURL = "https://MyServer.MyDomain.com"
portalAdmin = "MyAdminUser"
portalAdminPwd = "MyAdmin.Password"
logFileScript = "C:/Logs/validateRecPostScriptLog.txt"
logfileOutputRecPost = 'C:/Logs/reconcile_log.txt' 

# Choose to output a log file for the script
outputScriptReport = True

# Define functions
def openURL(url, params=None):
    """This function used to open a URL and returns the json response"""
    try:
        request_params = {'f':'pjson'}
        if params:
            request_params.update(params)
        encodedParams = urllib.parse.urlencode(request_params)
        request = urllib.request.urlopen(url, encodedParams.encode('UTF-8'))
        response = request.read()
        json_response = json.loads(response)
        return json_response
    except:
        print (traceback.format_exc())

def versionInfo(versionOwner=""):
    """This function queries the versions owned by the versionOwner.
    It returns a list of dictionaries."""
    vmsUrlinfo = "{}/server/rest/services/{}/VersionManagementServer/versionInfos?&ownerFilter={}&includeHidden=&f=json&token={}".format(baseURL, serviceName, versionOwner, token)
    response = openURL(vmsUrlinfo)
    if response['success'] == True:
        versionsDict = response['versions']
        return versionsDict
    else:
        return("Unable to get version info")

def evaluateUrl(validationUrl, validate_params):
    """This function runs evaluate on the validation server
    It returns the json response."""
    evalJsonResp = openURL(validationUrl, validate_params)
    if evalJsonResp['success'] == False:
        return [False, evalJsonResp]
    else:
        return [True, evalJsonResp]

def generateMessage(msg, print_statement=True):
    """This function generates messages as the script runs. If print_statement
    is set to True, print to the screen. If outputScriptReport is set to true,
    write the message to the logfile"""
    if outputScriptReport == True:
        with open(logFileScript, 'a') as log_file:
            log_file.write(msg + "\n")
    if print_statement == True:
        print(msg)

def recPostVersions(versionList, post):
    """This function runs the Reconcile Versions GP tool to reconcile
    and optionally post to the feature service"""
    if post == True:
        postVersion = "POST"
    elif post == False:
        postVersion = "NO_POST"
    # Reconcile and post the replica versions 
    # This tool is set to abort if there are conflicts and detects conflicts by object
    arcpy.ReconcileVersions_management(featureService,
                                        'ALL_VERSIONS',
                                        'sde.DEFAULT',
                                         versionList,
                                        'NO_LOCK_ACQUIRED',
                                        'ABORT_CONFLICTS',
                                        'BY_OBJECT',
                                        'FAVOR_EDIT_VERSION',
                                        postVersion,
                                        'KEEP_VERSION',
                                        logfileOutputRecPost)
    generateMessage(arcpy.GetMessages()+"\n")
    
# Start execution
generateMessage('Starting Validation/Reconcile/Post Automation Script... {:%Y-%b-%d %H:%M:%S}\n'.format(datetime.datetime.now()))

# Sign in to ArcGIS Enterprise    
signIntoPortal = arcpy.SignInToPortal(baseURL+"/portal", portalAdmin, portalAdminPwd)
generateMessage("Signed into ArcGIS Enterprise {} as user {}".format(baseURL+"/portal", portalAdmin))

# Get the token returned by the SignIntoPortal arcpy function to use for making REST requests
token = signIntoPortal['token']

# Build the feature service URL
featureService = "{}/server/rest/services/{}/FeatureServer".format(baseURL, serviceName)

# Get a list of the replica versions from the REST endpoint
listOfRepVersions = []
replicaVersionsURL = featureService + "/replicas?returnVersion=true&f=pjson"
repVersionsJson = openURL(replicaVersionsURL, signIntoPortal)
for repVersion in repVersionsJson:
    versionName = repVersion['replicaVersion']
    listOfRepVersions.append(versionName)
    
# Create an empty list to append version names to validate
listOfVersionsToValidate = []

# Iterate through each version returned by the versionInfo() function to find 
# the versions that need to be validated that are also in the listOfRepVersions list
for version in versionInfo():
    print("")    
    # Parse the version info response, which is a python dictionary/json
    # If the version name is sde.DEFAULT, pass since we do not want to evaluate the default version
    if version['versionName'] == "sde.DEFAULT":
        pass
    # If the modifiedDate property is null, pass
    elif version['modifiedDate'] == "None":
        pass
    # If the evaluation date is null, append the version name to the list to listOfVersions to be evaluated
    elif version['evaluationDate'] == None:
        if version['versionName'] in listOfRepVersions:
            listOfVersionsToValidate.append(version['versionName'])
    # If the evaluation date is not null, but it has been modifed since the last evaluation, add it to the list to be validated
    elif version['evaluationDate'] != None and version['modifiedDate'] > version['evaluationDate']:
        if version['versionName'] in listOfRepVersions:
            listOfVersionsToValidate.append(version['versionName'])
    # If none of these conditions are met
    else:
        generateMessage("Version {} will not be validated.".format(version['versionName']))
            
# Validate versions
generateMessage('The following versions will be validated: {}\n'.format(listOfVersionsToValidate))

# Create lists to contain versions where the validation passed or failed
failEval = []
passEval = []

# For each version in the list of versions, build the json request needed to validate
for i in listOfVersionsToValidate:
    validate_params = { "gdbVersion": i,
             "sessionId": "",
             "evaluationArea": "",
             "changesInVersion": "true",
             "selection": "",
             "evaluationType": '["validationRules"]',
             "returnEdits": "true",
             "async": "false",
             "f": "pjson",
             "token": token
    }
    # Build the REST URL used to validate the service
    validationUrl = baseURL + "/server/rest/services/"+ serviceName +"/ValidationServer/evaluate"
    
    # Call the evalVersion() function to validate the version
    evalVersion = evaluateUrl(validationUrl, validate_params)
    
    # If the evaluate failed, append to the failEval list
    if evalVersion[0] == False:
        generateMessage("Evalution of version {} failed".format(i))
        generateMessage(str(evalVersion[1]))
        failEval.append(i)
        
    # If the evaluate passed, check to see if errors were returned    
    elif evalVersion[0] == True:
        # If errors are returned, only reconcile this version
        if evalVersion[1]['errorsIdentified'] != 0:
            generateMessage("{} Errors were identified in version {}.\nThe version will be reconciled but will not be posted.\n".format((str(evalVersion[1]['errorsIdentified'])),i))
            generateMessage(str(evalVersion[1]), False)
        # If errors were not found this version can be posted
        else:
            generateMessage("Evaluation of version {} passed with no errors identified.\nThis version will be reconciled and posted.\n".format(i))
            generateMessage(str(evalVersion[1]))
            passEval.append(i)

# The versions that passed validation should be reconciled/posted
generateMessage('\nThe following versions passed validation and will be reconciled and posted: {}\n'.format(passEval))

# Run recPostVersions on the list of versions that passed evaluation with the option to post
recPostVersions(passEval, True)

# Open the reconcile log file and append the results to our report
with open(logfileOutputRecPost, 'r') as openRecLog:
    generateMessage(openRecLog.read(), False)
    
# Run recPostVersions with the option to reconcile all replica versions, no post
recPostVersions(listOfRepVersions, False)

# Open the reconcile log file and append the results to our report
with open(logfileOutputRecPost, 'r') as openRecLog:
    generateMessage(openRecLog.read(), False)

# Script execution complete
generateMessage('Validate, Reconcile, & Post Script Complete.')