Source code for arcrest.agol.services

"""

.. module:: services.py
   :platform: Windows, Linux
   :synopsis: Represents functions/classes used to control Feature Services,
              Feature Service Layer, Map Service, etc...

.. moduleauthor:: Esri

"""
from __future__ import absolute_import
from __future__ import print_function
from __future__ import division
import os
import uuid
import json
import types
from re import search
from ..packages.six.moves import urllib_parse as urlparse
#from six.moves import urllib_parse as urlparse
from ._uploads import Uploads
from ..security import security
from .._abstract import abstract
from ..common.filters import LayerDefinitionFilter, GeometryFilter, TimeFilter
from ..common.general import FeatureSet
from ..common import filters
from ..common.geometry import SpatialReference
from ..common.general import _date_handler, Feature
from ..common.spatial import scratchFolder, scratchGDB, json_to_featureclass
from ..common.spatial import get_OID_field, get_records_with_attachments
from ..common.spatial import create_feature_layer, merge_feature_class
from ..common.spatial import featureclass_to_json, create_feature_class
from ..common.spatial import get_attachment_data
from ..common import geometry
from ..hostedservice import AdminFeatureService, AdminFeatureServiceLayer
from .._abstract.abstract import BaseSecurityHandler, BaseAGOLClass

########################################################################
[docs]class FeatureService(abstract.BaseAGOLClass): """ contains information about a feature service """ _url = None _currentVersion = None _serviceDescription = None _hasVersionedData = None _supportsDisconnectedEditing = None _hasStaticData = None _maxRecordCount = None _supportedQueryFormats = None _capabilities = None _description = None _copyrightText = None _spatialReference = None _initialExtent = None _fullExtent = None _allowGeometryUpdates = None _units = None _syncEnabled = None _syncCapabilities = None _editorTrackingInfo = None _documentInfo = None _layers = None _tables = None _enableZDefaults = None _zDefault = None _size = None _xssPreventionInfo = None _editingInfo = None _proxy_url = None _proxy_port = None _securityHandler = None _serverURL = None _json = None _json_dict = None _supportsApplyEditsWithGlobalIds = None _serviceItemId = None #---------------------------------------------------------------------- def __init__(self, url, securityHandler=None, initialize=False, proxy_url=None, proxy_port=None): """Constructor""" self._url = url self._proxy_port = proxy_port self._proxy_url = proxy_url if isinstance(securityHandler, BaseSecurityHandler): if hasattr(securityHandler, 'is_portal'): if securityHandler.is_portal: if hasattr(securityHandler, 'portalServerHandler'): self._securityHandler = securityHandler.portalServerHandler(serverUrl=url) else: self._securityHandler = securityHandler else: self._securityHandler = securityHandler else: self._securityHandler = securityHandler if initialize: self.__init() #---------------------------------------------------------------------- def __init(self): """ loads the data into the class """ params = {"f": "json"} json_dict = self._get(self._url, params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) self._json_dict = json_dict self._json = json.dumps(self._json_dict, default=_date_handler) attributes = [attr for attr in dir(self) if not attr.startswith('__') and \ not attr.startswith('_')] for k,v in json_dict.items(): if k == 'layers': self._getLayers() elif k == 'tables': self._getTables() elif k in attributes: setattr(self, "_"+ k, json_dict[k]) else: print("%s - attribute not implemented in Feature Service." % k) #---------------------------------------------------------------------- def __str__(self): """ returns object as string """ if self._json is None: self.__init() return self._json #---------------------------------------------------------------------- def __iter__(self): """ iterator generator for public values/properties It only returns the properties that are public. """ attributes = [attr for attr in dir(self) if not attr.startswith('__') and \ not attr.startswith('_') and \ not isinstance(getattr(self, attr), (types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType)) ] for att in attributes: yield (att, getattr(self, att)) #---------------------------------------------------------------------- @property def securityHandler(self): """ returns the security handler """ return self._securityHandler #---------------------------------------------------------------------- @securityHandler.setter def securityHandler(self, value): """ sets the security handler """ if isinstance(value, abstract.BaseSecurityHandler): if isinstance(value, security.AGOLTokenSecurityHandler): self._securityHandler = value self._username = value.username self._password = value._password self._token_url = value.token_url elif isinstance(value, security.OAuthSecurityHandler): self._securityHandler = value else: pass #---------------------------------------------------------------------- @property def editingInfo(self): """ returns the editing information """ if self._editingInfo is None: self.__init() return self._editingInfo #---------------------------------------------------------------------- @property def xssPreventionInfo(self): """returns the xssPreventionInfo information """ if self._xssPreventionInfo is None: self.__init() return self._xssPreventionInfo #---------------------------------------------------------------------- @property def size(self): """returns the size parameter""" if self._size is None: self.__init() return self._size #----------------------------------------------------------------------
[docs] def refresh_service(self): """ repopulates the properties of the service """ self._tables = None self._layers = None self.__init()
#---------------------------------------------------------------------- @property def maxRecordCount(self): """returns the max record count""" if self._maxRecordCount is None: self.__init() return self._maxRecordCount #---------------------------------------------------------------------- @property def supportedQueryFormats(self): """ returns the supported query formats """ if self._supportedQueryFormats is None: self.__init() return self._supportedQueryFormats #---------------------------------------------------------------------- @property def capabilities(self): """ returns a list of capabilities """ if self._capabilities is None: self.__init() return self._capabilities #---------------------------------------------------------------------- @property def description(self): """ returns the service description """ if self._description is None: self.__init() return self._description #---------------------------------------------------------------------- @property def supportsApplyEditsWithGlobalIds(self): """returns the supportsApplyEditsWithGlobalIds property""" if self._supportsApplyEditsWithGlobalIds is None: self.__init() return self._supportsApplyEditsWithGlobalIds #---------------------------------------------------------------------- @property def serviceItemId(self): """ returns the serviceItemId""" if self._serviceItemId is None: self.__init() return self._serviceItemId #---------------------------------------------------------------------- @property def copyrightText(self): """ returns the copyright text """ if self._copyrightText is None: self.__init() return self._copyrightText #---------------------------------------------------------------------- @property def spatialReference(self): """ returns the spatial reference """ if self._spatialReference is None: self.__init() return self._spatialReference #---------------------------------------------------------------------- @property def initialExtent(self): """ returns the initial extent of the feature service """ if self._initialExtent is None: self.__init() return self._initialExtent #---------------------------------------------------------------------- @property def fullExtent(self): """ returns the full extent of the feature service """ if self._fullExtent is None: self.__init() return self._fullExtent #---------------------------------------------------------------------- @property def allowGeometryUpdates(self): """ informs the user if the data allows geometry updates """ if self._allowGeometryUpdates is None: self.__init() return self._allowGeometryUpdates #---------------------------------------------------------------------- @property def units(self): """ returns the measurement unit """ if self._units is None: self.__init() return self._units #---------------------------------------------------------------------- @property def syncEnabled(self): """ informs the user if sync of data can be performed """ if self._syncEnabled is None: self.__init() return self._syncEnabled #---------------------------------------------------------------------- @property def uploads(self): """returns the class to perform the upload function. it will only return the uploads class if syncEnabled is True. """ if self.syncEnabled == True: return Uploads(url=self._url + "/uploads", securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) return None #---------------------------------------------------------------------- @property def syncCapabilities(self): """ type of sync that can be performed """ if self._syncCapabilities is None: self.__init() return self._syncCapabilities #---------------------------------------------------------------------- @property def editorTrackingInfo(self): """ returns the editor tracking information """ if self._editorTrackingInfo is None: self.__init() return self._editorTrackingInfo #---------------------------------------------------------------------- @property def documentInfo(self): """ returns the document information """ if self._documentInfo is None: self.__init() return self._documentInfo #---------------------------------------------------------------------- def _getLayers(self): """ gets layers for the featuer service """ if self._layers is None: self._layers = [] params = {"f": "json"} json_dict = self._get(self._url, params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) if isinstance(json_dict, dict) and \ json_dict.has_key("layers"): for l in json_dict['layers']: self._layers.append(FeatureLayer(url=self._url + "/%s" % l['id'], securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url)) del l return self._layers #---------------------------------------------------------------------- def _getTables(self): """ gets layers for the featuer service """ if self._tables is None: self._tables = [] params = {"f": "json"} json_dict = self._get(self._url, params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) if isinstance(json_dict, dict) and \ json_dict.has_key("tables"): for l in json_dict['tables']: self._tables.append(FeatureLayer(url=self._url + "/%s" % l['id'], securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url)) del l return self._tables @property def url(self): """ returns the url for the feature service""" return self._url #---------------------------------------------------------------------- @property def layers(self): """ returns a list of layer objects """ if self._layers is None: self._getLayers() return self._layers #---------------------------------------------------------------------- @property def tables(self): """ returns the tables """ if self._tables is None: self._getTables() return self._tables #---------------------------------------------------------------------- @property def enableZDefaults(self): """ returns the enable Z defaults value """ if self._enableZDefaults is None: self.__init() return self._enableZDefaults #---------------------------------------------------------------------- @property def zDefault(self): """ returns the Z default value """ if self._zDefault is None: self.__init() return self._zDefault #---------------------------------------------------------------------- @property def hasStaticData(self): """ returns boolean for has statistic data """ if self._hasStaticData is None: self.__init() return self._hasStaticData #---------------------------------------------------------------------- @property def currentVersion(self): """ returns the map service current version """ if self._currentVersion is None: self.__init() return self._currentVersion #---------------------------------------------------------------------- @property def administration(self): """returns the hostservice object to manage the back-end functions""" url = self._url res = search("/rest/", url).span() addText = "admin/" part1 = url[:res[1]] part2 = url[res[1]:] adminURL = "%s%s%s" % (part1, addText, part2) res = AdminFeatureService(url=url, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port, initialize=False) return res #---------------------------------------------------------------------- @property def serviceDescription(self): """ returns the serviceDescription of the map service """ if self._serviceDescription is None: self.__init() return self._serviceDescription #---------------------------------------------------------------------- @property def hasVersionedData(self): """ returns boolean for versioned data """ if self._hasVersionedData is None: self.__init() return self._hasVersionedData #---------------------------------------------------------------------- @property def supportsDisconnectedEditing(self): """ returns boolean is disconnecting editted supported """ if self._supportsDisconnectedEditing is None: self.__init() return self._supportsDisconnectedEditing #----------------------------------------------------------------------
[docs] def query(self, layerDefsFilter=None, geometryFilter=None, timeFilter=None, returnGeometry=True, returnIdsOnly=False, returnCountOnly=False, returnZ=False, returnM=False, outSR=None ): """ The Query operation is performed on a feature service resource """ qurl = self._url + "/query" params = {"f": "json", "returnGeometry": returnGeometry, "returnIdsOnly": returnIdsOnly, "returnCountOnly": returnCountOnly, "returnZ": returnZ, "returnM" : returnM} if not layerDefsFilter is None and \ isinstance(layerDefsFilter, LayerDefinitionFilter): params['layerDefs'] = layerDefsFilter.filter if not geometryFilter is None and \ isinstance(geometryFilter, GeometryFilter): gf = geometryFilter.filter params['geometryType'] = gf['geometryType'] params['spatialRel'] = gf['spatialRel'] params['geometry'] = gf['geometry'] params['inSR'] = gf['inSR'] if not outSR is None and \ isinstance(outSR, geometry.SpatialReference): params['outSR'] = outSR.asDictionary if not timeFilter is None and \ isinstance(timeFilter, TimeFilter): params['time'] = timeFilter.filter res = self._get(url=qurl, param_dict=params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) if returnIdsOnly == False and returnCountOnly == False: if isinstance(res, str): jd = json.loads(res) return [FeatureSet.fromJSON(json.dumps(lyr)) for lyr in jd['layers']] elif isinstance(res, dict): return [FeatureSet.fromJSON(json.dumps(lyr)) for lyr in res['layers']] else: return res return res
#---------------------------------------------------------------------- #---------------------------------------------------------------------- @property def replicas(self): """ returns all the replicas for a feature service """ params = { "f" : "json", } url = self._url + "/replicas" return self._get(url, params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) #----------------------------------------------------------------------
[docs] def unRegisterReplica(self, replica_id): """ removes a replica from a feature service Inputs: replica_id - The replicaID returned by the feature service when the replica was created. """ params = { "f" : "json", "replicaID" : replica_id } url = self._url + "/unRegisterReplica" return self._post(url, params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port)
#----------------------------------------------------------------------
[docs] def replicaInfo(self, replica_id): """ The replica info resources lists replica metadata for a specific replica. Inputs: replica_id - The replicaID returned by the feature service when the replica was created. """ params = { "f" : "json" } url = self._url + "/replicas/" + replica_id return self._get(url, param_dict=params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port)
#----------------------------------------------------------------------
[docs] def createReplica(self, replicaName, layers, layerQueries=None, geometryFilter=None, replicaSR=None, transportType="esriTransportTypeUrl", returnAttachments=False, returnAttachmentsDatabyURL=False, async=False, attachmentsSyncDirection="none", syncModel="none", dataFormat="json", replicaOptions=None, wait=False, out_path=None): """ The createReplica operation is performed on a feature service resource. This operation creates the replica between the feature service and a client based on a client-supplied replica definition. It requires the Sync capability. See Sync overview for more information on sync. The response for createReplica includes replicaID, server generation number, and data similar to the response from the feature service query operation. The createReplica operation returns a response of type esriReplicaResponseTypeData, as the response has data for the layers in the replica. If the operation is called to register existing data by using replicaOptions, the response type will be esriReplicaResponseTypeInfo, and the response will not contain data for the layers in the replica. Inputs: replicaName - name of the replica layers - layers to export layerQueries - In addition to the layers and geometry parameters, the layerQueries parameter can be used to further define what is replicated. This parameter allows you to set properties on a per layer or per table basis. Only the properties for the layers and tables that you want changed from the default are required. Example: layerQueries = {"0":{"queryOption": "useFilter", "useGeometry": true, "where": "requires_inspection = Yes"}} geometryFilter - Geospatial filter applied to the replica to parse down data output. returnAttachments - If true, attachments are added to the replica and returned in the response. Otherwise, attachments are not included. returnAttachmentDatabyURL - If true, a reference to a URL will be provided for each attachment returned from createReplica. Otherwise, attachments are embedded in the response. replicaSR - the spatial reference of the replica geometry. transportType - The transportType represents the response format. If the transportType is esriTransportTypeUrl, the JSON response is contained in a file, and the URL link to the file is returned. Otherwise, the JSON object is returned directly. The default is esriTransportTypeUrl. If async is true, the results will always be returned as if transportType is esriTransportTypeUrl. If dataFormat is sqlite, the transportFormat will always be esriTransportTypeUrl regardless of how the parameter is set. Values: esriTransportTypeUrl | esriTransportTypeEmbedded returnAttachments - If true, attachments are added to the replica and returned in the response. Otherwise, attachments are not included. The default is false. This parameter is only applicable if the feature service has attachments. returnAttachmentsDatabyURL - If true, a reference to a URL will be provided for each attachment returned from createReplica. Otherwise, attachments are embedded in the response. The default is true. This parameter is only applicable if the feature service has attachments and if returnAttachments is true. attachmentsSyncDirection - Client can specify the attachmentsSyncDirection when creating a replica. AttachmentsSyncDirection is currently a createReplica property and cannot be overridden during sync. Values: none, upload, bidirectional async - If true, the request is processed as an asynchronous job, and a URL is returned that a client can visit to check the status of the job. See the topic on asynchronous usage for more information. The default is false. syncModel - Client can specify the attachmentsSyncDirection when creating a replica. AttachmentsSyncDirection is currently a createReplica property and cannot be overridden during sync. dataFormat - The format of the replica geodatabase returned in the response. The default is json. Values: filegdb, json, sqlite, shapefile replicaOptions - This parameter instructs the createReplica operation to create a new replica based on an existing replica definition (refReplicaId). It can be used to specify parameters for registration of existing data for sync. The operation will create a replica but will not return data. The responseType returned in the createReplica response will be esriReplicaResponseTypeInfo. wait - if async, wait to pause the process until the async operation is completed. out_path - folder path to save the file """ if self.syncEnabled == False and "Extract" not in self.capabilities: return None url = self._url + "/createReplica" dataformat = ["filegdb", "json", "sqlite", "shapefile"] params = {"f" : "json", "replicaName": replicaName, "returnAttachments": returnAttachments, "returnAttachmentsDatabyURL": returnAttachmentsDatabyURL, "attachmentsSyncDirection" : attachmentsSyncDirection, "async" : async, "syncModel" : syncModel, "layers" : layers } if dataFormat.lower() in dataformat: params['dataFormat'] = dataFormat.lower() else: raise Exception("Invalid dataFormat") if layerQueries is not None: params['layerQueries'] = layerQueries if geometryFilter is not None and \ isinstance(geometryFilter, GeometryFilter): params.update(geometryFilter.filter) if replicaSR is not None: params['replicaSR'] = replicaSR if replicaOptions is not None: params['replicaOptions'] = replicaOptions if transportType is not None: params['transportType'] = transportType if async: if wait: exportJob = self._post(url=url, param_dict=params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) status = self.replicaStatus(url=exportJob['statusUrl']) while status['status'].lower() != "completed": status = self.replicaStatus(url=exportJob['statusUrl']) if status['status'].lower() == "failed": return status res = status else: res = self._post(url=url, param_dict=params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) else: res = self._post(url=url, param_dict=params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) if out_path is not None and \ os.path.isdir(out_path): dlURL = None if 'resultUrl' in res: dlURL = res["resultUrl"] elif 'responseUrl' in res: dlURL = res["responseUrl"] if dlURL is not None: return self._get(url=dlURL, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port, out_folder=out_path) else: return res elif res is not None: return res return None
#----------------------------------------------------------------------
[docs] def synchronizeReplica(self, replicaID, transportType="esriTransportTypeUrl", replicaServerGen=None, returnIdsForAdds=False, edits=None, returnAttachmentDatabyURL=False, async=False, syncDirection="snapshot", syncLayers="perReplica", editsUploadID=None, editsUploadFormat=None, dataFormat="json", rollbackOnFailure=True): """ TODO: implement synchronize replica http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r3000000vv000000 """ params = { "f" : "json", "replicaID" : replicaID, "transportType" : transportType, "dataFormat" : dataFormat, "rollbackOnFailure" : rollbackOnFailure, "async" : async, "returnIdsForAdds": returnIdsForAdds, "syncDirection" : syncDirection, "returnAttachmentDatabyURL" : returnAttachmentDatabyURL } return
#----------------------------------------------------------------------
[docs] def replicaStatus(self, url): """gets the replica status when exported async set to True""" params = {"f" : "json"} url = url + "/status" return self._get(url=url, param_dict=params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url)
########################################################################
[docs]class FeatureLayer(abstract.BaseAGOLClass): """ This contains information about a feature service's layer. """ _objectIdField = None _allowGeometryUpdates = None _globalIdField = None _token_url = None _currentVersion = None _id = None _name = None _type = None _description = None _definitionExpression = None _geometryType = None _hasZ = None _hasM = None _copyrightText = None _parentLayer = None _subLayers = None _minScale = None _maxScale = None _effectiveMinScale = None _effectiveMaxScale = None _defaultVisibility = None _extent = None _timeInfo = None _drawingInfo = None _hasAttachments = None _htmlPopupType = None _displayField = None _typeIdField = None _fields = None _types = None # sub-types _relationships = None _maxRecordCount = None _canModifyLayer = None _supportsValidateSql = None _supportsCoordinatesQuantization = None _supportsStatistics = None _supportsAdvancedQueries = None _hasLabels = None _canScaleSymbols = None _capabilities = None _supportedQueryFormats = None _isDataVersioned = None _ownershipBasedAccessControlForFeatures = None _useStandardizedQueries = None _templates = None _indexes = None _hasStaticData = None _supportsRollbackOnFailureParameter = None _advancedQueryCapabilities = None _editingInfo = None _proxy_url = None _proxy_port = None _securityHandler = None _supportsCalculate = None _supportsAttachmentsByUploadId = None _editFieldsInfo = None _serverURL = None _supportsValidateSql = None _supportsCoordinatesQuantization = None _supportsApplyEditsWithGlobalIds = None _serviceItemId = None _json = None _json_dict = None _standardMaxRecordCount = None _tileMaxRecordCount = None _maxRecordCountFactor = None #---------------------------------------------------------------------- def __init__(self, url, securityHandler=None, initialize=False, proxy_url=None, proxy_port=None): """Constructor""" self._url = url self._proxy_port = proxy_port self._proxy_url = proxy_url if isinstance(securityHandler, BaseSecurityHandler): if hasattr(securityHandler, 'is_portal'): if securityHandler.is_portal: if hasattr(securityHandler, 'portalServerHandler'): self._securityHandler = securityHandler.portalServerHandler(serverUrl=url) else: self._securityHandler = securityHandler else: self._securityHandler = securityHandler else: self._securityHandler = securityHandler if initialize: self.__init() #---------------------------------------------------------------------- def __init(self): """ initializes the service """ params = { "f" : "json", } json_dict = self._get(self._url, params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url) self._json_dict = json_dict self._json = json.dumps(json_dict, default=self._date_handler) attributes = [attr for attr in dir(self) if not attr.startswith('__') and \ not attr.startswith('_')] for k,v in json_dict.items(): if k in attributes: setattr(self, "_"+ k, json_dict[k]) else: print("%s - attribute not implemented in Feature Layer." % k) if not self._parentLayer is None: self._parentLayer = FeatureService( url=os.path.dirname(self._url), securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url, initialize=False) #----------------------------------------------------------------------
[docs] def refresh(self): """refreshes all the properties of the service""" self.__init()
#---------------------------------------------------------------------- def __str__(self): """ returns object as string """ return self._json #---------------------------------------------------------------------- def __iter__(self): """ iterator generator for public values/properties It only returns the properties that are public. """ attributes = [attr for attr in dir(self) if not attr.startswith('__') and \ not attr.startswith('_') and \ not isinstance(getattr(self, attr), (types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType)) ] for att in attributes: yield (att, getattr(self, att)) #---------------------------------------------------------------------- @property def standardMaxRecordCount(self): """ returns the standardMaxRecordCount for the feature layer""" if self._standardMaxRecordCount is None: self.__init() return self._standardMaxRecordCount #---------------------------------------------------------------------- @property def tileMaxRecordCount(self): """ returns the tileMaxRecordCount for the feature layer""" if self._tileMaxRecordCount is None: self.__init() return self._tileMaxRecordCount #---------------------------------------------------------------------- @property def maxRecordCountFactor(self): """ returns the maxRecordCountFactor for the feature layer""" if self._maxRecordCountFactor is None: self.__init() return self._maxRecordCountFactor #---------------------------------------------------------------------- @property def url(self): """ returns the url for the feature layer""" return self._url #---------------------------------------------------------------------- @property def supportsApplyEditsWithGlobalIds(self): """ returns the url for the feature layer""" if self._supportsApplyEditsWithGlobalIds is None: self.__init() return self._supportsApplyEditsWithGlobalIds #---------------------------------------------------------------------- @property def serviceItemId(self): """returns the service item id""" if self._serviceItemId is None: self.__init() return self._serviceItemId #---------------------------------------------------------------------- @property def administration(self): """returns the hostservice object to manage the back-end functions""" url = self._url res = search("/rest/", url).span() addText = "admin/" part1 = url[:res[1]] part2 = url[res[1]:] adminURL = "%s%s%s" % (part1, addText, part2) res = AdminFeatureServiceLayer(url=adminURL, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port, initialize=True) return res #---------------------------------------------------------------------- @property def supportsValidateSql(self): """ returns the supports calculate values """ if self._supportsValidateSql is None: self.__init() return self._supportsValidateSql #---------------------------------------------------------------------- @property def supportsCoordinatesQuantization(self): """ returns the supports calculate values """ if self._supportsCoordinatesQuantization is None: self.__init() return self._supportsCoordinatesQuantization #---------------------------------------------------------------------- @property def supportsCalculate(self): """ returns the supports calculate values """ if self._supportsCalculate is None: self.__init() return self._supportsCalculate #---------------------------------------------------------------------- @property def editFieldsInfo(self): """ returns edit field info """ if self._editFieldsInfo is None: self.__init() return self._editFieldsInfo #---------------------------------------------------------------------- @property def supportsAttachmentsByUploadId(self): """ returns is supports attachments by uploads id """ if self._supportsAttachmentsByUploadId is None: self.__init() return self._supportsAttachmentsByUploadId #---------------------------------------------------------------------- @property def editingInfo(self): """ returns the edit information """ if self._editingInfo is None: self.__init() return self._editingInfo #---------------------------------------------------------------------- @property def advancedQueryCapabilities(self): """ returns the advanced query capabilities """ if self._advancedQueryCapabilities is None: self.__init() return self._advancedQueryCapabilities #---------------------------------------------------------------------- @property def supportsRollbackOnFailureParameter(self): """ returns if rollback on failure supported """ if self._supportsRollbackOnFailureParameter is None: self.__init() return self._supportsRollbackOnFailureParameter #---------------------------------------------------------------------- @property def hasStaticData(self): """boolean T/F if static data is present """ if self._hasStaticData is None: self.__init() return self._hasStaticData #---------------------------------------------------------------------- @property def indexes(self): """gets the indexes""" if self._indexes is None: self.__init() return self._indexes #---------------------------------------------------------------------- @property def templates(self): """ gets the template """ if self._templates is None: self.__init() return self._templates #---------------------------------------------------------------------- @property def allowGeometryUpdates(self): """ returns boolean if geometry updates are allowed """ if self._allowGeometryUpdates is None: self.__init() return self._allowGeometryUpdates #---------------------------------------------------------------------- @property def globalIdField(self): """ returns the global id field """ if self._globalIdField is None: self.__init() return self._globalIdField #---------------------------------------------------------------------- @property def objectIdField(self): if self._objectIdField is None: self.__init() return self._objectIdField #---------------------------------------------------------------------- @property def currentVersion(self): """ returns the current version """ if self._currentVersion is None: self.__init() return self._currentVersion #---------------------------------------------------------------------- @property def id(self): """ returns the id """ if self._id is None: self.__init() return self._id #---------------------------------------------------------------------- @property def name(self): """ returns the name """ if self._name is None: self.__init() return self._name #---------------------------------------------------------------------- @property def type(self): """ returns the type """ if self._type is None: self.__init() return self._type #---------------------------------------------------------------------- @property def description(self): """ returns the layer's description """ if self._description is None: self.__init() return self._description #---------------------------------------------------------------------- @property def definitionExpression(self): """returns the definitionExpression""" if self._definitionExpression is None: self.__init() return self._definitionExpression #---------------------------------------------------------------------- @property def geometryType(self): """returns the geometry type""" if self._geometryType is None: self.__init() return self._geometryType #---------------------------------------------------------------------- @property def hasZ(self): """ returns if it has a Z value or not """ if self._hasZ is None: self.__init() return self._hasZ #---------------------------------------------------------------------- @property def hasM(self): """ returns if it has a m value or not """ if self._hasM is None: self.__init() return self._hasM #---------------------------------------------------------------------- @property def copyrightText(self): """ returns the copyright text """ if self._copyrightText is None: self.__init() return self._copyrightText #---------------------------------------------------------------------- @property def parentLayer(self): """ returns information about the parent """ if self._parentLayer is None: self.__init() return self._parentLayer #---------------------------------------------------------------------- @property def subLayers(self): """ returns sublayers for layer """ if self._subLayers is None: self.__init() return self._subLayers #---------------------------------------------------------------------- @property def minScale(self): """ minimum scale layer will show """ if self._minScale is None: self.__init() return self._minScale @property def maxScale(self): """ sets the max scale """ if self._maxScale is None: self.__init() return self._maxScale @property def effectiveMinScale(self): """ returns the effective minimum scale value """ if self._effectiveMinScale is None: self.__init() return self._effectiveMinScale @property def effectiveMaxScale(self): """ returns the effective maximum scale value """ if self._effectiveMaxScale is None: self.__init() return self._effectiveMaxScale @property def defaultVisibility(self): """ returns the default visibility of the layer """ if self._defaultVisibility is None: self.__init() return self._defaultVisibility @property def extent(self): """ returns the extent """ if self._extent is None: self.__init() return self._extent @property def timeInfo(self): """ returns the time information about the layer """ if self._timeInfo is None: self.__init() return self._timeInfo @property def drawingInfo(self): """ returns the symbol information about the layer """ if self._drawingInfo is None: self.__init() return self._drawingInfo @property def hasAttachments(self): """ boolean that tells if attachments are associated with layer """ if self._hasAttachments is None: self.__init() return self._hasAttachments @property def htmlPopupType(self): """ returns the popup type """ if self._htmlPopupType is None: self.__init() return self._htmlPopupType @property def displayField(self): """ returns the primary display field """ if self._displayField is None: self.__init() return self._displayField @property def typeIdField(self): """ returns the type Id field """ if self._typeIdField is None: self.__init() return self._typeIdField @property def fields(self): """ returns the layer's fields """ if self._fields is None: self.__init() return self._fields @property def types(self): """ returns the types """ if self._types is None: self.__init() return self._types @property def relationships(self): """ returns the relationships for the layer """ if self._relationships is None: self.__init() return self._relationships @property def maxRecordCount(self): """ returns the maximum returned records """ if self._maxRecordCount is None: self.__init() if self._maxRecordCount is None: self._maxRecordCount = 1000 return self._maxRecordCount @property def canModifyLayer(self): """ returns boolean to say if layer can be modified """ if self._canModifyLayer is None: self.__init() return self._canModifyLayer @property def supportsStatistics(self): """ boolean to if supports statistics """ if self._supportsStatistics is None: self.__init() return self._supportsStatistics @property def supportsAdvancedQueries(self): """ boolean value if advanced queries is supported """ if self._supportsAdvancedQueries is None: self.__init() return self._supportsAdvancedQueries @property def hasLabels(self): """ returns if layer has labels on or not """ if self._hasLabels is None: self.__init() return self._hasLabels @property def canScaleSymbols(self): """ states if symbols can scale """ if self._canScaleSymbols is None: self.__init() return self._canScaleSymbols @property def capabilities(self): """ operations that can be performed on layer """ if self._capabilities is None: self.__init() return self._capabilities @property def supportedQueryFormats(self): """ returns supported query formats """ if self._supportedQueryFormats is None: self.__init() return self._supportedQueryFormats @property def isDataVersioned(self): """ returns boolean if data is in version control """ if self._isDataVersioned is None: self.__init() return self._isDataVersioned @property def ownershipBasedAccessControlForFeatures(self): """ returns value for owernship based access control """ if self._ownershipBasedAccessControlForFeatures is None: self.__init() return self._ownershipBasedAccessControlForFeatures @property def useStandardizedQueries(self): """ returns value if standardized queries can be used """ if self._useStandardizedQueries is None: self.__init() return self._useStandardizedQueries #---------------------------------------------------------------------- @property def securityHandler(self): """ gets the security handler """ return self._securityHandler #---------------------------------------------------------------------- @securityHandler.setter def securityHandler(self, value): """ sets the security handler """ if isinstance(value, abstract.BaseSecurityHandler): if isinstance(value, security.AGOLTokenSecurityHandler): self._securityHandler = value self._token = value.token self._username = value.username self._password = value._password self._token_url = value.token_url elif isinstance(value, security.OAuthSecurityHandler): self._token = value.token self._securityHandler = value else: pass #----------------------------------------------------------------------
[docs] def addAttachment(self, oid, file_path): """ Adds an attachment to a feature service Input: oid - string - OBJECTID value to add attachment to file_path - string - path to file Output: JSON Repsonse """ if self.hasAttachments == True: attachURL = self._url + "/%s/addAttachment" % oid params = {'f':'json'} parsed = urlparse.urlparse(attachURL) files = {'attachment': file_path} res = self._post(url=attachURL, param_dict=params, files=files, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url) return self._unicode_convert(res) else: return "Attachments are not supported for this feature service."
#----------------------------------------------------------------------
[docs] def deleteAttachment(self, oid, attachment_id): """ removes an attachment from a feature service feature Input: oid - integer or string - id of feature attachment_id - integer - id of attachment to erase Output: JSON response """ url = self._url + "/%s/deleteAttachments" % oid params = { "f":"json", "attachmentIds" : "%s" % attachment_id } return self._post(url, params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url)
#----------------------------------------------------------------------
[docs] def updateAttachment(self, oid, attachment_id, file_path): """ updates an existing attachment with a new file Inputs: oid - string/integer - Unique record ID attachment_id - integer - Unique attachment identifier file_path - string - path to new attachment Output: JSON response """ url = self._url + "/%s/updateAttachment" % oid params = { "f":"json", "attachmentId" : "%s" % attachment_id } files = {'attachment': file_path } res = self._post(url=url, param_dict=params, files=files, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url) return self._unicode_convert(res)
#----------------------------------------------------------------------
[docs] def listAttachments(self, oid): """ list attachements for a given OBJECT ID """ url = self._url + "/%s/attachments" % oid params = { "f":"json" } return self._get(url, params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url)
#----------------------------------------------------------------------
[docs] def create_fc_template(self, out_path, out_name): """creates a featureclass template on local disk""" fields = self.fields objectIdField = self.objectIdField geomType = self.geometryType wkid = self.parentLayer.spatialReference['wkid'] return create_feature_class(out_path, out_name, geomType, wkid, fields, objectIdField)
[docs] def create_feature_template(self): """creates a feature template""" fields = self.fields feat_schema = {} att = {} for fld in fields: self._globalIdField if not fld['name'] == self._objectIdField and not fld['name'] == self._globalIdField: att[fld['name']] = '' feat_schema['attributes'] = att feat_schema['geometry'] = '' return Feature(feat_schema)
#----------------------------------------------------------------------
[docs] def query(self, where="1=1", out_fields="*", timeFilter=None, geometryFilter=None, returnGeometry=True, returnIDsOnly=False, returnCountOnly=False, returnFeatureClass=False, returnDistinctValues=False, returnExtentOnly=False, groupByFieldsForStatistics=None, statisticFilter=None, resultOffset="", resultRecordCount="", out_fc=None, objectIds="", **kwargs): """ queries a feature service based on a sql statement Inputs: where - the selection sql statement out_fields - the attribute fields to return timeFilter - a TimeFilter object where either the start time or start and end time are defined to limit the search results for a given time. The values in the timeFilter should be as UTC timestampes in milliseconds. No checking occurs to see if they are in the right format. geometryFilter - a GeometryFilter object to parse down a given query by another spatial dataset. returnGeometry - true means a geometry will be returned, else just the attributes returnIDsOnly - false is default. True means only OBJECTIDs will be returned returnCountOnly - if True, then an integer is returned only based on the sql statement returnFeatureClass - Default False. If true, query will be returned as feature class groupByFieldsForStatistics - One or more field names on which the values need to be grouped for calculating the statistics. resultOffset - Default is 0. If set, this option can be used for fetching query results by skipping the specified number of records and starting from the next record (that is, resultOffset + 1th). resultRecordCount - This option can be used for fetching query results up to the resultRecordCount specified. When resultOffset is specified but this parameter is not, the map service defaults it to maxRecordCount. The maximum value for this parameter is the value of the layer's maxRecordCount property. statisticFilter - object that performs statistic queries out_fc - only valid if returnFeatureClass is set to True. Output location of query. kwargs - optional parameters that can be passed to the Query function. This will allow users to pass additional parameters not explicitly implemented on the function. A complete list of functions available is documented on the Query REST API. Output: A list of Feature Objects (default) or a path to the output featureclass if returnFeatureClass is set to True. """ params = {"f": "json", "where": where, "outFields": out_fields, "returnGeometry" : returnGeometry, "returnIdsOnly" : returnIDsOnly, "returnCountOnly" : returnCountOnly, "returnDistinctValues" : returnDistinctValues, "returnExtentOnly" : returnExtentOnly } for key, value in kwargs.items(): params[key] = value if not timeFilter is None and \ isinstance(timeFilter, filters.TimeFilter): params['time'] = timeFilter.filter if not geometryFilter is None and \ isinstance(geometryFilter, filters.GeometryFilter): gf = geometryFilter.filter params['geometry'] = gf['geometry'] params['geometryType'] = gf['geometryType'] params['spatialRelationship'] = gf['spatialRel'] params['inSR'] = gf['inSR'] if objectIds is not None and objectIds != "": params['objectIds'] = objectIds if resultOffset is not None and resultOffset != "": params['resultOffset'] = resultOffset if resultRecordCount is not None and resultRecordCount != "": params['resultRecordCount'] = resultRecordCount if not groupByFieldsForStatistics is None: params['groupByFieldsForStatistics'] = groupByFieldsForStatistics if not statisticFilter is None and \ isinstance(statisticFilter, filters.StatisticFilter): params['outStatistics'] = statisticFilter.filter fURL = self._url + "/query" results = self._post(fURL, params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url) if 'error' in results: raise ValueError (results) if not returnCountOnly and not returnIDsOnly: if returnFeatureClass == True: json_text = json.dumps(results) temp = scratchFolder() + os.sep + uuid.uuid4().get_hex() + ".json" with open(temp, 'wb') as writer: writer.write(json_text) writer.flush() del writer fc = json_to_featureclass(json_file=temp, out_fc=out_fc) os.remove(temp) return fc else: return FeatureSet.fromJSON(json.dumps(results)) else: return results return
#---------------------------------------------------------------------- #----------------------------------------------------------------------
[docs] def getHTMLPopup(self, oid): """ The htmlPopup resource provides details about the HTML pop-up authored by the user using ArcGIS for Desktop. Input: oid - object id of the feature where the HTML pop-up Output: """ if self.htmlPopupType != "esriServerHTMLPopupTypeNone": popURL = self._url + "/%s/htmlPopup" % oid params = { 'f' : "json" } return self._get(url=popURL, param_dict=params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url) return ""
#---------------------------------------------------------------------- def _chunks(self, l, n): """ Yield n successive chunks from a list l. """ l.sort() newn = int(1.0 * len(l) / n + 0.5) for i in xrange(0, n-1): yield l[i*newn:i*newn+newn] yield l[n*newn-newn:] #----------------------------------------------------------------------
[docs] def get_local_copy(self, out_path, includeAttachments=False): """ exports the whole feature service to a feature class Input: out_path - path to where the data will be placed includeAttachments - default False. If sync is not supported then the paramter is ignored. Output: path to exported feature class or fgdb (as list) """ if self.hasAttachments and \ self.parentLayer.syncEnabled: return self.parentLayer.createReplica(replicaName="fgdb_dump", layers="%s" % self.id, attachmentsSyncDirection="upload", async=True, wait=True, returnAttachments=includeAttachments, out_path=out_path)[0] elif self.hasAttachments == False and \ self.parentLayer.syncEnabled: return self.parentLayer.createReplica(replicaName="fgdb_dump", layers="%s" % self.id, attachmentsSyncDirection="upload", async=True, wait=True, returnAttachments=includeAttachments, out_path=out_path)[0] else: result_features = [] res = self.query(returnIDsOnly=True) OIDS = res['objectIds'] OIDS.sort() OIDField = res['objectIdFieldName'] count = len(OIDS) if count <= self.maxRecordCount: bins = 1 else: bins = count / self.maxRecordCount v = count % self.maxRecordCount if v > 0: bins += 1 chunks = self._chunks(OIDS, bins) for chunk in chunks: chunk.sort() sql = "%s >= %s and %s <= %s" % (OIDField, chunk[0], OIDField, chunk[len(chunk) -1]) temp_base = "a" + uuid.uuid4().get_hex()[:6] + "a" temp_fc = r"%s\%s" % (scratchGDB(), temp_base) temp_fc = self.query(where=sql, returnFeatureClass=True, out_fc=temp_fc) result_features.append(temp_fc) return merge_feature_class(merges=result_features, out_fc=out_path)
#----------------------------------------------------------------------
[docs] def updateFeature(self, features, gdbVersion=None, rollbackOnFailure=True): """ updates an existing feature in a feature service layer Input: feature - feature object(s) to get updated. A single feature, a list of feature objects can be passed, or a FeatureSet object. Output: dictionary of result messages """ params = { "f" : "json", "rollbackOnFailure" : rollbackOnFailure } if gdbVersion is not None: params['gdbVersion'] = gdbVersion if isinstance(features, Feature): params['features'] = json.dumps([features.asDictionary], default=_date_handler ) elif isinstance(features, list): vals = [] for feature in features: if isinstance(feature, Feature): vals.append(feature.asDictionary) params['features'] = json.dumps(vals, default=_date_handler ) elif isinstance(features, FeatureSet): params['features'] = json.dumps( [feature.asDictionary for feature in features.features], default=_date_handler ) else: return {'message' : "invalid inputs"} updateURL = self._url + "/updateFeatures" res = self._post(url=updateURL, securityHandler=self._securityHandler, param_dict=params, proxy_port=self._proxy_port, proxy_url=self._proxy_url) return res
#----------------------------------------------------------------------
[docs] def deleteFeatures(self, objectIds="", where="", geometryFilter=None, gdbVersion=None, rollbackOnFailure=True ): """ removes 1:n features based on a sql statement Input: objectIds - The object IDs of this layer/table to be deleted where - A where clause for the query filter. Any legal SQL where clause operating on the fields in the layer is allowed. Features conforming to the specified where clause will be deleted. geometryFilter - a filters.GeometryFilter object to limit deletion by a geometry. gdbVersion - Geodatabase version to apply the edits. This parameter applies only if the isDataVersioned property of the layer is true rollbackOnFailure - parameter to specify if the edits should be applied only if all submitted edits succeed. If false, the server will apply the edits that succeed even if some of the submitted edits fail. If true, the server will apply the edits only if all edits succeed. The default value is true. Output: JSON response as dictionary """ dURL = self._url + "/deleteFeatures" params = { "f": "json", } if geometryFilter is not None and \ isinstance(geometryFilter, filters.GeometryFilter): gfilter = geometryFilter.filter params['geometry'] = gfilter['geometry'] params['geometryType'] = gfilter['geometryType'] params['inSR'] = gfilter['inSR'] params['spatialRel'] = gfilter['spatialRel'] if where is not None and \ where != "": params['where'] = where if objectIds is not None and \ objectIds != "": params['objectIds'] = objectIds result = self._post(url=dURL, param_dict=params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url) return result
#----------------------------------------------------------------------
[docs] def applyEdits(self, addFeatures=[], updateFeatures=[], deleteFeatures=None, gdbVersion=None, useGlobalIds=False, rollbackOnFailure=True): """ This operation adds, updates, and deletes features to the associated feature layer or table in a single call. Inputs: addFeatures - The array of features to be added. These features should be common.Feature objects updateFeatures - The array of features to be updateded. These features should be common.Feature objects deleteFeatures - string of OIDs to remove from service gdbVersion - Geodatabase version to apply the edits. useGlobalIds - instead of referencing the default Object ID field, the service will look at a GUID field to track changes. This means the GUIDs will be passed instead of OIDs for delete, update or add features. rollbackOnFailure - Optional parameter to specify if the edits should be applied only if all submitted edits succeed. If false, the server will apply the edits that succeed even if some of the submitted edits fail. If true, the server will apply the edits only if all edits succeed. The default value is true. Output: dictionary of messages """ editURL = self._url + "/applyEdits" params = {"f": "json", "useGlobalIds" : userGlobalIds, "rollbackOnFailure" : rollbackOnFailure } if gdbVersion is not None: params['gdbVersion'] = gdbVersion if len(addFeatures) > 0 and \ isinstance(addFeatures[0], Feature): params['adds'] = json.dumps([f.asDictionary for f in addFeatures], default=_date_handler) if len(updateFeatures) > 0 and \ isinstance(updateFeatures[0], Feature): params['updates'] = json.dumps([f.asDictionary for f in updateFeatures], default=_date_handler) if deleteFeatures is not None and \ isinstance(deleteFeatures, str): params['deletes'] = deleteFeatures return self._post(url=editURL, param_dict=params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url)
#----------------------------------------------------------------------
[docs] def addFeature(self, features, gdbVersion=None, rollbackOnFailure=True): """ Adds a single feature to the service Inputs: feature - list of common.Feature object or a single common.Feature Object or a FeatureSet object gdbVersion - Geodatabase version to apply the edits rollbackOnFailure - Optional parameter to specify if the edits should be applied only if all submitted edits succeed. If false, the server will apply the edits that succeed even if some of the submitted edits fail. If true, the server will apply the edits only if all edits succeed. The default value is true. Output: JSON message as dictionary """ url = self._url + "/addFeatures" params = { "f" : "json" } if gdbVersion is not None: params['gdbVersion'] = gdbVersion if isinstance(rollbackOnFailure, bool): params['rollbackOnFailure'] = rollbackOnFailure if isinstance(features, list): params['features'] = json.dumps([feature.asDictionary for feature in features], default=_date_handler) elif isinstance(features, Feature): params['features'] = json.dumps([features.asDictionary], default=_date_handler) elif isinstance(features, FeatureSet): params['features'] = json.dumps([feature.asDictionary for feature in features.features], default=_date_handler) else: return None return self._post(url=url, param_dict=params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url)
#----------------------------------------------------------------------
[docs] def addFeatures(self, fc, attachmentTable=None, nameField="ATT_NAME", blobField="DATA", contentTypeField="CONTENT_TYPE", rel_object_field="REL_OBJECTID", lowerCaseFieldNames=False): """ adds a feature to the feature service Inputs: fc - string - path to feature class data to add. attachmentTable - string - (optional) path to attachment table nameField - string - (optional) name of file field in attachment table blobField - string - (optional) name field containing blob data contentTypeField - string - (optional) name of field containing content type rel_object_field - string - (optional) name of field with OID of feature class Output: boolean, add results message as list of dictionaries """ messages = {'addResults':[]} if attachmentTable is None: count = 0 bins = 1 uURL = self._url + "/addFeatures" max_chunk = 250 js = json.loads(self._unicode_convert( featureclass_to_json(fc))) js = js['features'] if lowerCaseFieldNames == True: for feat in js: feat['attributes'] = dict((k.lower(), v) for k,v in feat['attributes'].items()) if len(js) == 0: return {'addResults':None} if len(js) <= max_chunk: bins = 1 else: bins = int(len(js)/max_chunk) if len(js) % max_chunk > 0: bins += 1 chunks = self._chunks(l=js, n=bins) for chunk in chunks: params = { "f" : 'json', "features" : json.dumps(chunk, default=_date_handler) } result = self._post(url=uURL, param_dict=params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url) if messages is None: messages = result else: if 'addResults' in result: if 'addResults' in messages: messages['addResults'] = messages['addResults'] + result['addResults'] else: messages['addResults'] = result['addResults'] else: messages['errors'] = result del params del result return messages else: oid_field = get_OID_field(fc) OIDs = get_records_with_attachments(attachment_table=attachmentTable) fl = create_feature_layer(fc, "%s not in ( %s )" % (oid_field, ",".join(OIDs))) result = self.addFeatures(fl) if result is not None: messages.update(result) del fl for oid in OIDs: fl = create_feature_layer(fc, "%s = %s" % (oid_field, oid), name="layer%s" % oid) msgs = self.addFeatures(fl) for result in msgs['addResults']: oid_fs = result['objectId'] sends = get_attachment_data(attachmentTable, sql="%s = %s" % (rel_object_field, oid)) result['addAttachmentResults'] = [] for s in sends: attRes = self.addAttachment(oid_fs, s['blob']) if 'addAttachmentResult' in attRes: attRes['addAttachmentResult']['AttachmentName'] = s['name'] result['addAttachmentResults'].append(attRes['addAttachmentResult']) else: attRes['AttachmentName'] = s['name'] result['addAttachmentResults'].append(attRes) del s del sends del result messages.update( msgs) del fl del oid del OIDs return messages
#----------------------------------------------------------------------
[docs] def calculate(self, where, calcExpression, sqlFormat="standard"): """ The calculate operation is performed on a feature service layer resource. It updates the values of one or more fields in an existing feature service layer based on SQL expressions or scalar values. The calculate operation can only be used if the supportsCalculate property of the layer is true. Neither the Shape field nor system fields can be updated using calculate. System fields include ObjectId and GlobalId. See Calculate a field for more information on supported expressions Inputs: where - A where clause can be used to limit the updated records. Any legal SQL where clause operating on the fields in the layer is allowed. calcExpression - The array of field/value info objects that contain the field or fields to update and their scalar values or SQL expression. Allowed types are dictionary and list. List must be a list of dictionary objects. Calculation Format is as follows: {"field" : "<field name>", "value" : "<value>"} sqlFormat - The SQL format for the calcExpression. It can be either standard SQL92 (standard) or native SQL (native). The default is standard. Values: standard, native Output: JSON as string Usage: >>>sh = arcrest.AGOLTokenSecurityHandler("user", "pw") >>>fl = arcrest.agol.FeatureLayer(url="someurl", securityHandler=sh, initialize=True) >>>print fl.calculate(where="OBJECTID < 2", calcExpression={"field": "ZONE", "value" : "R1"}) {'updatedFeatureCount': 1, 'success': True} """ url = self._url + "/calculate" params = { "f" : "json", "where" : where, } if isinstance(calcExpression, dict): params["calcExpression"] = json.dumps([calcExpression], default=_date_handler) elif isinstance(calcExpression, list): params["calcExpression"] = json.dumps(calcExpression, default=_date_handler) if sqlFormat.lower() in ['native', 'standard']: params['sqlFormat'] = sqlFormat.lower() else: params['sqlFormat'] = "standard" return self._post(url=url, param_dict=params, securityHandler=self._securityHandler, proxy_port=self._proxy_port, proxy_url=self._proxy_url)
########################################################################
[docs]class TableLayer(FeatureLayer): """Table object is exactly like FeatureLayer object""" pass
########################################################################
[docs]class TiledService(BaseAGOLClass): """ AGOL Tiled Map Service """ _mapName = None _documentInfo = None _copyrightText = None _id = None _layers = None _tables = None _supportedImageFormatTypes = None _storageFormat = None _capabilities = None _access = None _currentVersion = None _units = None _type = None _serviceDescription = None _status = None _tileInfo = None _description = None _fullExtent = None _singleFusedMapCache = None _name = None _created = None _maxScale = None _modified = None _spatialReference = None _minScale = None _server = None _tileServers = None _securityHandler = None _exportTilesAllowed = None _maxExportTilesCount = None _initialExtent = None #---------------------------------------------------------------------- def __init__(self, url, securityHandler, initialize=False, proxy_url=None, proxy_port=None): """Constructor""" self._url = url if isinstance(securityHandler, BaseSecurityHandler): self._securityHandler = securityHandler if not securityHandler is None: self._referer_url = securityHandler.referer_url self._proxy_url = proxy_url self._proxy_port = proxy_port if initialize: self.__init() #---------------------------------------------------------------------- def __init(self): """ loads the data into the class """ params = {"f": "json"} json_dict = self._get(self._url, params, securityHandler=self._securityHandler, proxy_url=self._proxy_url, proxy_port=self._proxy_port) attributes = [attr for attr in dir(self) if not attr.startswith('__') and \ not attr.startswith('_')] for k,v in json_dict.items(): if k in attributes: setattr(self, "_"+ k, json_dict[k]) else: print("%s - attribute not implemented in tiled service." % k) #---------------------------------------------------------------------- @property def maxExportTilesCount(self): """ returns the max export tiles count""" if self._maxExportTilesCount is None: self.__init() return self._maxExportTilesCount #---------------------------------------------------------------------- @property def exportTilesAllowed(self): """ export tiles allowed """ if self._exportTilesAllowed is None: self.__init() return self._exportTilesAllowed #---------------------------------------------------------------------- @property def securityHandler(self): """ gets the security handler """ return self._securityHandler #---------------------------------------------------------------------- @securityHandler.setter def securityHandler(self, value): """ sets the security handler """ if isinstance(value, BaseSecurityHandler): if isinstance(value, security.AGOLTokenSecurityHandler): self._securityHandler = value elif isinstance(value, security.OAuthSecurityHandler): self._securityHandler = value else: pass #---------------------------------------------------------------------- def __str__(self): """ returns object as string """ return json.dumps(dict(self), default=_date_handler) #---------------------------------------------------------------------- def __iter__(self): """ iterator generator for public values/properties It only returns the properties that are public. """ attributes = [attr for attr in dir(self) if not attr.startswith('__') and \ not attr.startswith('_') and \ not isinstance(getattr(self, attr), (types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType)) ] for att in attributes: yield (att, getattr(self, att)) #---------------------------------------------------------------------- @property def initialExtent(self): """ initial extent of tile service """ if self._initialExtent is None: self.__init() return self._initialExtent #---------------------------------------------------------------------- @property def mapName(self): """ returns the map name """ if self._mapName is None: self.__init() return self._mapName #---------------------------------------------------------------------- @property def documentInfo(self): """ returns the document information """ if self._documentInfo is None: self.__init() return self._documentInfo #---------------------------------------------------------------------- @property def copyrightText(self): """ returns the copyright information """ if self._copyrightText is None: self.__init() return self._copyrightText #---------------------------------------------------------------------- @property def id(self): """ returns the ID """ if self._id is None: self.__init() return self._id #---------------------------------------------------------------------- @property def layers(self): """ returns the layers """ if self._layers is None: self.__init() return self._layers #---------------------------------------------------------------------- @property def tables(self): """ returns the tables in the map service """ if self._tables is None: self.__init() return self._tables #---------------------------------------------------------------------- @property def supportedImageFormatTypes(self): """ returns the supported image format types """ if self._supportedImageFormatTypes is None: self.__init() return self._supportedImageFormatTypes #---------------------------------------------------------------------- @property def storageFormat(self): """ returns the storage format """ if self._storageFormat is None: self.__init() return self._storageFormat #---------------------------------------------------------------------- @property def capabilities(self): """ returns the capabilities """ if self._capabilities is None: self.__init() return self._capabilities #---------------------------------------------------------------------- @property def access(self): """ returns the access value """ if self._access is None: self.__init() return self._access #---------------------------------------------------------------------- @property def currentVersion(self): """ returns the current version """ if self._currentVersion is None: self.__init() return self._currentVersion #---------------------------------------------------------------------- @property def units(self): """ returns the units """ if self._units is None: self.__init() return self._units #---------------------------------------------------------------------- @property def type(self): """ returns the type """ if self._type is None: self.__init() return self._type #---------------------------------------------------------------------- @property def serviceDescription(self): """ returns the service description """ if self._serviceDescription is None: self.__init() return self._serviceDescription #---------------------------------------------------------------------- @property def status(self): """ returns the status """ if self._status is None: self.__init() return self._status #---------------------------------------------------------------------- @property def tileInfo(self): """ returns the tile information """ if self._tileInfo is None: self.__init() return self._tileInfo #---------------------------------------------------------------------- @property def description(self): """ returns the description """ if self._description is None: self.__init() return self._description #---------------------------------------------------------------------- @property def fullExtent(self): """ returns the full extent """ if self._fullExtent is None: self.__init() return self._fullExtent #---------------------------------------------------------------------- @property def singleFusedMapCache(self): """ information about the single fused map cache """ if self._singleFusedMapCache is None: self.__init() return self._singleFusedMapCache #---------------------------------------------------------------------- @property def name(self): """ returns the service name """ if self._name is None: self.__init() return self._name #---------------------------------------------------------------------- @property def created(self): """ returns the created value """ if self._created is None: self.__init() return self._created #---------------------------------------------------------------------- @property def maxScale(self): """ returns the maximum scale """ if self._maxScale is None: self.__init() return self._maxScale #---------------------------------------------------------------------- @property def modified(self): """ returns the modified value """ if self._modified is None: self.__init() return self._modified #---------------------------------------------------------------------- @property def spatialReference(self): """ returns the spatial reference value """ if self._spatialReference is None: self.__init() return self._spatialReference #---------------------------------------------------------------------- @property def minScale(self): """ returns the minimum scale """ if self._minScale is None: self.__init() return self._minScale #---------------------------------------------------------------------- @property def server(self): """ returns the server information """ if self._server is None: self.__init() return self._server #---------------------------------------------------------------------- @property def tileServers(self): """ returns the tile services value """ if self._tileServers is None: self.__init() return self._tileServers