esri / hub-py Goto Github PK
View Code? Open in Web Editor NEWPython interface to ArcGIS Hub
Home Page: https://github.com/esri/hub-py/wiki
License: Apache License 2.0
Python interface to ArcGIS Hub
Home Page: https://github.com/esri/hub-py/wiki
License: Apache License 2.0
I'm not sure why add_derived_indicator
is a unique function when it really is just add_indicator
. I believe that function works for even scripting adding any indicator, and there isn't any additional attribute, so we don't need to be so specific.
## Obtain crash data from DC Vision Zero initiative
# returns all hub objects that match 'Washington, DC', assume the one we want is first in the list
hubs = Hub.search("Washington, DC")
# create object for DC's Hubs
dchub = hubs[0]
# pass list of DC's initiative objects into dc_i
dc_i = dchub.initiatives
# pass the first initiative matching Vision Zero into dc_vz_i
dc_vz_i = dc_i.search("Vision Zero")[0]
# pass list of app objects into vz_apps
dc_vz_apps = dc_vz_i.apps
# pass the first app of type 'dashboard' that matches query 'crash' into dc_vz_crashDash
dc_vz_crashDash = dc_vz_apps.search("crash", item_type="dashboard")[0]
# pass the first indicator (dataset) into dc_vz_crashData
dc_vz_crashData = dc_vz_crashDash.indicator[0]
# add the crash data indicator to a map
map.add_layer(vz_crashData)
## Events
# pass the first event that matches query 'Vision Zero' into vz_h
hackathon = dchub.events.search(q="Vision Zero")[0]
# pass the first event returned within the vision zero initiative into vz_h
hackathon = dc_vz_i.events[0]
# pass list of all hackathon attendees/organizers into hackathon_a/o (include contact info, user location, number of submitted apps, etc)
hackathon_a = hackathon.attendees()
hackathon_o = hackathon.organizers()
# create new event
meeting = event.create("name": "6th Street Bike Lane Public Forum", "date":"03/11/2018 8:00 PM - 03/11/2018 9:00 PM", "location":"1700 7th St Nw, Washington DC, 20001", "description": "Voice your opinion on the bike lanes.", "Registration": "Unlimited", "initiative":"Vision Zero", "site":"Vision Zero Initiative Site")
# cancel event
event.cancel(meeting)
## Various searching
# returns all hub objects within the extent of the UK
hubs = Hub.search("extent":{"type":"extent","xmin":-8865305.05467878,"ymin":5402526.23156312,"xmax":-8808254.51250724,"ymax":5443099.21866579,"spatialReference":{"wkid":102100}})
# returns all hub objects with organization type 'Local government' (organization type needs to be added to ArcGIS Online organization object)
hubs = Hub.search("organization_type": "Local government")
# returns all Hubs
hubs = Hub.search("*")
## Initiative misc
# activate an initiative
dc_vz = Hub.initiative.activate("Vision Zero")
# add application to an initiative
# get crash lens application from your AGOL organization
crash_lens = gis.Item("4ef")
# add crash lens to DC VZ initiative
dc_vz_i.add.application(crash_lens)
They should serve as both a guide to a new user, as well as unit tests for functionality.
Should include rich narrative, images (if and where applicable).
Refer https://developers.arcgis.com/python/guide/ for pattern.
Should generate API reference for Hub
arcgis
hub
as a lazy property which tests if GIS is AGOL instance and only then calls class Hub()
Feature support:
Current process for updating an initiative is
init1_data = init1.definition
init1_data['title'] = 'New Title'
init1.update(initiative_properties=init1_data)
Needs to work like https://developers.arcgis.com/python/guide/managing-your-content/#Updating-item-properties
Feature Support:
Getting Community User email addresses (and other private information) is only available to community org admins. So add option to add the Community admin credentials. This would then add a full communityGIS
object to the Hub
that would be used instead for subsequent community related API calls (e.g. community_user
search)
Blank initiatives need two things:
Test-cases:
Mine
in initiative managerRe-run initiatives notebook
Events may have either initiativeId
or siteId
. Currently, it's not very easy to get initiativeId
from the siteId
.
The idea is to promote analysis by marrying the tools and libraries supported in ArcGIS notebooks to the solution templates of Initiatives to create analysis solutions in a notebook and configure them to the initiative from the same interface.
myInitiative = myhub.initiatives.get(id)
myInitiative.analytics.search()
>> returns all the implemented analytics for a particular initiative
myInitiative.analytics.get('hotSpotAnalysis')
>> returns item associated to that analysis Id for the initiative
# perform analysis using `arcgis`
myInitiative.analytics.add(item generated from analysis)
hotSpot = myInitiative.analytics.get(`hotSpotAnalysis')
# update existing layer using `arcgis` and assign to `hotSpot`
hotSpot.update(hotSpot)
# delete analytic
hotspot.delete()
Currently returns list with [X,Y]
Change it to return location of type - arcgis.geometry.Point
e.g.
# pass the first event that matches query 'Vision Zero' into vz_h
hackathon = dchub.events.search(q="Vision Zero")[0]
# pass the first event returned within the vision zero initiative into vz_h
hackathon = dc_vz_i.events[0]
# pass list of all hackathon attendees/organizers into hackathon_a/o (include contact info, user location, number of submitted apps, etc)
hackathon_a = hackathon.attendees()
hackathon_o = hackathon.organizers()
streetIndicator = vz_init.indicators.get('street')
streetIndicator.item
Add checks to test if a user has logged in with credentials on add/update/delete functions and allow operation accordingly.
arcgis
error handling conventions for performing analysis without signing in.for example:
# returns all hub objects that match 'Washington, DC', assume the one we want is first in the list
hubs = Hub.search("Washington, DC")
# create object for DC's Hubs
dchub = hubs[0]
# returns all hub objects that match 'Washington, DC', assume the one we want is first in the list
hubs = Hub.search("Washington, DC")
# create object for DC's Hubs
dchub = hubs[0]
# pass list of DC's initiative objects into dc_i
dc_i = dchub.initiatives
# pass the first initiative matching Vision Zero into dc_vz_i
dc_vz_i = dc_i.search("Vision Zero")[0]
# Get list of indicators - which can be values, strings or layer items
dc_vz_indicators = dc_vz_i.indicators
# pass list of app objects into vz_apps
dc_vz_apps = dc_vz_i.apps
# pass the first app of type 'dashboard' that matches query 'crash' into dc_vz_crashDash
dc_vz_crashDash = dc_vz_apps.search("crash", item_type="dashboard")[0]
# pass the first indicator (dataset) into dc_vz_crashData
dc_vz_crashData = dc_vz_crashDash.indicator[0]
Usage:
mySite = myHub.sites.get(siteId)
mySite.clone(org=None)
Note: Test process using item.clone()
and document limitations/features here.
add()
to allow for delete protectiondelete
that first disables it before deletingTo Test:
Check setting of groups in AGOL once created. Also try deleting them and verify that they don't.
A Hub is a comprehensive object for managing all of the content related to that Hub. Technically, there are two AGO organizations - but this should be transparent to the developer .
myHub1 = hub.Hub("https://dc.maps.arcgis.com", username, password)
myHub2 = hub.Hub("https://community.maps.arcgis.com", username, password)
myHub3 = hub.Hub("https://www.arcgis.com", username, password)
myHub4 = hub.Hub("https://opendata.dc.gov", username, password)
myHub1.orgs.enterprise #=> https://dc.maps.arcgis.com
myHub1.orgs.community #=> https://community.maps.arcgis.com
myHub2.orgs.enterprise.url #=> https://dc.maps.arcgis.com
myHub2.orgs.community.url #=> https://community.maps.arcgis.com
myHub3.orgs.enterprise.url #=> https://dc.maps.arcgis.com
myHub3.orgs.community.url #=> https://community.maps.arcgis.com
myHub4.orgs.enterprise.url #=> https://dc.maps.arcgis.com
myHub4.orgs.community.url #=> https://community.maps.arcgis.com
len(myHub1.events.search()) #=> 4
len(myHub2.events.search()) #=> 4
len(myHub3.events.search()) #=> 4
len(myHub4.events.search()) #=> 4
hub
initiative
indicator
site
initiative team
initiative template
hub organization
community organization
solution template
initiative template
user
event
page
catalog
analysis
comment
location
data science
To test:
scope
will be an optional parameter to the initiatives.search()
function.official
- all Public initiatives in Enterprise orgcommunity
- all Public initiatives in Community orgall
- All initiatives in current GIS
(anonymous or signed in) with outside_org=True
None
(for no user-provided value) - All initiatives in current GIS
(anonymous or signed in)Usage:
myHub.initiatives.search(scope='official', title='Vision Zero')
Functionality supported:
>>> hub.sites.add(title=title, group_id, domain=None, initiative=None)
>>> site.update()
>>> site.delete()
>>> hub.sites.get(siteId)
>>> hub.sites.search(initiative=None, creator/owner=None)
Specify Minimum viable dictionary needed to create a site using Python. Item dictionary will be passed to the gis.content.add()
function to create the site item correctly.
The Python API needs to prevent Sites from having a subdomain longer than 63 characters. Itβs a limit of DNS and was added to our UI. But our Python also needs to prevent bad URL
myInitiative.delete(site=True, apps=True)
apps
can only be deleted by a user that has permissions to delete those items. In other cases, give appropriate error message.
The implementation architecture will be identical to that of an Initiative
has Indicators
.
mySite = myHub.sites.get(id)
#pages
mySite.pages.add(name)
mySite.pages.get(id)
mySite.pages.search(name=None, owner=None, tags=None)
mySite.pages.clone(page, title=None) #clones page within the same site
myPage.update()
myPage.delete()
Indicators can be datasets or other types of information. Adding _layers
seems to be unnecessary jargon (GIS-y) and also overly specific when that's not always the case.
if the indicators are specifically data, should we call it that?
This is a bit complicated due to restrictions of the AGO api for protecting user credentials. Here is a rough sketch.
This call should work for
For this to work for both non-Community Admin and with Community Admin the Hub will need to support the option to also add Credentials for the community org. This may look something
hub = GIS.hub("https://opendata.dc.gov")
dcInitiatives = hub.initiatives.search()
initiativeFollowers = dcInitiatives[0].followers
firstUser = initiativeFollowers[0]
firstUser.username #=> 'sally'
firstUser.email #=> Null
# Hub has reference to 2 Orgs: Enterprise + Community. First, they are both 'anonymous'
hub._enterpriseGIS
hub._communityGIS
# Sign into the Enterprise Org. This will replace/store hub._enterpriseGIS with a signed in GIS
hub.enterpriseSignin(enterpriseUsername, enterprisePassword) #=> True (success)
# Sign into the Community Org. This will replace/store hub._communityGIS with a signed in GIS
hub.communitySignin(communityUsername, communityPassword) #=> True
# Now the requests return additional information
dcInitiatives = hub.initiatives.search()
initiativeFollowers = dcInitiatives[0].followers
firstUser = initiativeFollowers[0]
# getting profile information may require 'lazy loading' during each user (group.users doesn't return this info)
firstUser.username #=> 'sally'
firstUser.email #=> `[email protected]`
As we've discussed, I think that we need to add caching of both organizations (enterprise + community) so that the right org can be used explicitly in other methods.
in Hub.init - add something like the following (but with much better error checking)
def __init__(self, gis, username=None, password=None):
self.gis = gis
self._username = username
self._password = password
self.gis = GIS(self.url, self._username, self._password)
try:
self._gis_id = self.gis.properties.id
# add test if actually a Hub
# orgType is either 'enterprise' or 'community'
self._hubRole = self.gis.properties.portalProperties.hub.settings.orgType
if self._hubRole == 'enterprise':
self._enterpriseGIS = self.gis
self._communityGIS = GIS(self.community_org_url)
elif self._hubRole == 'community':
self._enterpriseGIS = GIS(self.enterprise_org_url)
self._communityGIS = self.gis
except AttributeError:
self._gis_id = None
def enterpriseSignin(username=None, password=None)
self._enterpriseGIS = GIS(self.enterprise_org_url, username, password)
def communitySignin(username=None, password=None)
self._communityGIS = GIS(self.community_org_url, username, password)
myhub.initiatives.add_from_template(templateid, title, description=None)
will adopt existing initiative template and create new initiative
from arcgis.hub import *
#Search for DC's Hub --> returns object with 2 orgs (enterprise, community)
myhub = arcgis.hub.Hub.search("DC")
#Connect to enterprise org
dc_eOrg = myHub.orgs[0]
gis = GIS(dc_eOrg.url, username, password)
#Fetch crime indicator
dc_crime_url = dc_eOrg.indicators["crime"]
#Perform analysis, and save maps in your org
hotspot_crime = arcgis.analysis.hotspot(with parameters)
result_map = webmap.add(hotspt_crime)
#Add 'result_map' Item to 'City Safety' initiative
safety_eOrg = dc_eOrg.Initiatives.search("City Safety")
mymap = safety_eOrg.add(result_map, name='Crime Cluster Map', type='webmap', tags=['dc crime', 'safety'])
#Make it public to add to community org
mymap.share(everyone=True)
mymap_id = mymap.item
#Add it to community org
dc_cOrg = myHub.orgs[1]
gis = GIS(dc_cOrg.url, username, password)
#Search for mymap and add to 'City Safety' initiative
mymap_item = gis.content.search(mymap_id)
safety_cOrg = dc_cOrg.Initiatives.search("City Safety")
c_mymap = safety_cOrg.add(mymap_item, name='Crime Cluster Map', type='webmap', tags=['dc crime', 'safety'])
c_mymap.share(organization=True)
#Requirements
e_gis = GIS(...)
c_gis = GIS(...)
myHub = e_gis.hub
#Fetch initiatives
all_initiatives = myHub.initiatives.search()
for initiative in all_initiatives:
hub_followers = initiative.followers(c_gis)
g_subscribers = #GET using govdelivery api
#EITHER overwrite all followers to subscribers list
g_subscribers = hub_followers
#POST g_subscribers using govdelivery API
#OR
new_add = hub_followers not in g_subscribers
new_subtract = g_subscribers not in hub_followers
g_subscribers = g_subscribers + new_add - new_subtract
#Updating followers
followers_group = e_gis.groups.get(groupid)
for user['username'] in new subtract:
followers_group.remove_users(user['username'])
#POST new_add using govdelivery API
#Update g_subscribers to remove new_subtract using govdelivery API
example pseudo-code
vz_initiative = Initiatives.search("Vision Zero", status='template')
vz_hubs = vz_initiatives.hubs
vz_hubs_2 = Initiatives.search("Vision Zero", status='adopted')
TBD
The current UI for adding indicators to an initiative restricts the flexibility of updating attributes on the fly.
E.g. Indicator streetCrashes
accepts only two attributes currently Injuries
and Fatalities
. Future versions of hub may (?) allow a user to tweak attributes in initiatives instead of solution templates of initiative templates.
Script this only in case of valid use-case or if ^ doesn't hold, whichever is first.
arcgis.hub.EventManager.search()
currently does a client-side search by downloading all events and looping through each event entity. Events by design are records in a FeatureLayer. Thus this search op can be delegated to be run server-side using a regularFeatureLayer.query()
operation. Not a priority, but if scalability becomes an issue, we may revisit this enhancement.
Steps to test:
While this can be done using the Python API like:
site = gis.content.add(site item dictionary)
site.update(data)
does this warrant for creating class Site()
now since it is a crucial concept anyway?
myInitiative = myhub.initiatives.get(id)
newInitiative = myhub.initiatives.clone(myInitiative, title=None, include_apps = True)
# and
newInitiative = anotherHub.initiatives.clone(myInitiative, originHub = myhub, include_apps = False, title=None)
from arcgis.hub import *
#User-defined inputs
url = ''
username = ''
password = ''
initiative_id = ''
#Connect to Hub
gis = GIS(url, username, password)
myHub = gis.hub
#Fetch VZ initiative
initiative = myHub.initiatives.get(initiative_id)
#Fetch crash indicator
crash = initiative.indicators.get('crash')
crash_url = crash.url
#Perform analysis
hotspot_crash = arcgis.analysis.hotspot(with parameters)
#Publish to AGOL if doesn't exist, else update
if does not exist:
published = gis.content.import_data(hotspot_crash)
else:
#fetch item, update
#Add 'hotspot_crash' indicator to VZ initiative
indicator_properties = {...}
initiative.indicators.add(indicator_properties)
Current version has two issues:
id
for indicator that you add exists.To test fix:
indicator already exists
messageA declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.