Git Product home page Git Product logo

hub-py's People

Contributors

ajturner avatar jgravois avatar manushim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hub-py's Issues

Starter code

## 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)

Add 'hub' as a property of 'GIS'

  • Get latest version of arcgis
  • Add hub as a lazy property which tests if GIS is AGOL instance and only then calls class Hub()
  • Test rest of the functionality
  • Refine examples
  • PR working version to geosaurus

Redesigned Indicators

#38

Feature support:

  • User can add, update, delete, fetch a particular and search for all indicators for an initiative.
  • Example demonstrating ^

Redesigned Events

#34

Feature Support:

  • User can search for events and visualize all events for a Hub in an embedded webmap
  • Example demonstrating ^

Option to provide Community Admin credentials to get user emails

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)

Create initiative doesn't create

Blank initiatives need two things:

  1. Data
  2. Groups

Test-cases:

  1. I can edit created initiative
  2. Reflects under filter Mine in initiative manager

Analytics for initiative

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()

Developer can search for Events

  • Given a Hub, get a list of all events
  • Given an adopted initiative, events for that initiative
  • Given an Event, list of attendees

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()

Write access for anonymous user

Add checks to test if a user has logged in with credentials on add/update/delete functions and allow operation accordingly.

  • Follow arcgis error handling conventions for performing analysis without signing in.

Developer can Search for Hubs

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]

Hub API

Developer can search for Indicators

# 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]

User can clone a site

Usage:

  1. User fetches site using mySite = myHub.sites.get(siteId)
  2. User can clone to their or another org mySite.clone(org=None)

Note: Test process using item.clone() and document limitations/features here.

Initiative groups aren't delete protected

  • change script in add() to allow for delete protection
  • change script in delete that first disables it before deleting

To Test:
Check setting of groups in AGOL once created. Also try deleting them and verify that they don't.

Creating a Hub should work with any associated URL

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

Scenarios

Nouns

hub
initiative
indicator
site
initiative team
initiative template
hub organization
community organization
solution template
initiative template
user
event
page
catalog
analysis
comment
location

Different use cases to cover

data science

  • extract data from initiatives
    event (hackathon)
  • organizer
  • attendee
    searching
  • across hubs
    • by location ("All Hubs located in the UK")
    • by organization type ("All Hubs at the state level")
    • by number of initiatives, events ("All Hubs that have hosted at least 10 events")
    • by initiatives adopted ("All Hubs that have adopted Vision Zero")
  • across initiatives (this is probably just Portal API calls)
    • by location ("All Vision Zero initiatives in the US")
    • by age ("All initiatives created within six months")
    • by last updated ("All initiatives updated within the past month")
    • by tag ("All livable initiatives")
  • across applications
    • by initiative ("All applications in the Vision Zero initiative")
    • by type ("All dashboards")
      initiative updating
  • add application to an initiative
    community organization
  • view members (present in the Python API)
  • invite members (present in the Python API)

Deleting an initiative

  • unshare content from groups
  • delete groups
  • delete initiative

To test:

  • initiative shouldn't be found on hub
  • groups shouldn't be found on AGOL
  • initiative site does not get deleted

Search initiative based on scope of GIS

  1. scope will be an optional parameter to the initiatives.search() function.
  2. Valid values for the parameter are:
  • official - all Public initiatives in Enterprise org
  • community - all Public initiatives in Community org
  • all - 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')

v1 functionality for Site class

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)

Define site item dictionary

  1. For initiative site
  2. For other sites

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.

Support for Pages of a Site

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()

Change `indicator_layers` to `indicators`

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?

Support for export all Hub community members

This is a bit complicated due to restrictions of the AGO api for protecting user credentials. Here is a rough sketch.

  • Given an Initiative, get the full list of followers
    • include username, joined date, email (only if Community Admin)

This call should work for

  • Anonymous - only get for public initiatives and public users (per API)
  • Core Member - ability to get all members (will not include email)
  • Core Member with Community Admin credentials - (includes Email)

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]`

Add Support for Referencing both Orgs

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)

@ManushiM

Create initiative from template

myhub.initiatives.add_from_template(templateid, title, description=None)

will adopt existing initiative template and create new initiative

Pseudo-code for Crime Analysis

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)

Granicus workflow

#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

Developer can search for Initiatives

  • by initiatives adopted ("All Hubs that have adopted Vision Zero")
  • across initiatives (this is probably just Portal API calls)
  • by location ("All Vision Zero initiatives in the US")
  • by age ("All initiatives created within six months")
  • by last updated ("All initiatives updated within the past month")
  • by tag ("All livable initiatives")

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')

Attribute mapping for indicators

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.

Change 'search' for events.search()

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 regular FeatureLayer.query() operation. Not a priority, but if scalability becomes an issue, we may revisit this enhancement.

Initiative site is created along with initiative

Steps to test:

  • Site is created and is visible as an item in the initiative editor
  • Clicking view/edit redirects correctly
  • Site is added to Collaboration group on AGOL

@ajturner

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?

Deep cloning an initiative

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)

Hot spot analysis example for hub.py redesign

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)

Adding/Updating an indicator to initiative

Current version has two issues:

  1. Doesn't compare with solution template to see if the id for indicator that you add exists.
  2. Allows for adding indicators with duplicate id/name.

To test fix:

  1. Adding indicator that doesn't exist in corresponding solution (derived from initiative) template must return an error message
  2. Adding indicator with existing id should return an indicator already exists message
  3. Updating non existent indicator should return an error message

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.