Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArcGIS API for Python SharingGroupManager Limitations? #2180

Closed
theisenm12 opened this issue Dec 12, 2024 · 10 comments
Closed

ArcGIS API for Python SharingGroupManager Limitations? #2180

theisenm12 opened this issue Dec 12, 2024 · 10 comments
Assignees
Labels

Comments

@theisenm12
Copy link

I am trying to run a script that loops through my AGOL organizations documents (Microsoft Excel, Word and Powerpoint items) and return how many groups they are shared with in a table export. I have noticed that no matter how many groups an item is shared with, it caps at 5 groups in table. Does anyone know of any limitations on this function? Code below.

existing_item_id = 'xyz'

# Initialize an empty DataFrame to store item and group details
columns = ["Title", "Item Type", "Group Names", "Item Owner", "Number of groups", "Risk level","Item ID"]
items_df = pd.DataFrame(columns=columns)

# Get all unique item types in the organization, you can find a list of this online probably
item_types = ["Microsoft Excel", "Microsoft Powerpoint", "Microsoft Word"]

# Loop through each item type and fetch items
for item_type in item_types:
    print(f"Processing items of type: {item_type}")
    items = gis.content.search(query=f"type:\"{item_type}\"", max_items=5000)
   
    for item in items:
        # Use the SharingGroupManager to list groups the item belongs to
        groups = item.sharing.groups.list()
        group_names = [group['title'] for group in groups]
        
        # Count the number of groups
        num_groups = len(group_names)
       
        # Assign a risk level based on number of groups
        if num_groups >= 10:
            risk_level = "High"
        elif 5 <= num_groups < 10:
            risk_level = "Medium"
        else:
            risk_level = "Low"
    
        # Append item and group data to the DataFrame
        items_df = pd.concat([
            items_df,
            pd.DataFrame([{
                "Title": item.title,
                "Item Type": item_type,
                "Group Names": ", ".join(group_names), 
                "Item Owner": item.owner,
                "Number of groups": num_groups,
                "Risk Level": risk_level,
                "Item ID": item.id
            }])
        ], ignore_index=True)

# Save the DataFrame to a CSV file for further analysis
sharing_table = 'xyz'
items_df.to_csv(sharing_table, index=False)
print("Data export complete. File saved as 'xyz'.")

# Define the schema for the new table
fields = [
    {
        "name": "Title",
        "alias": "Title",
        "type": "esriFieldTypeString",
        "length": 255
    },
    {
        "name": "Item_Type",
        "alias": "Item Type",
        "type": "esriFieldTypeString",
        "length": 255
    },
    {
        "name": "Group_Names",
        "alias": "Group Names",
        "type": "esriFieldTypeString",
        "length": 5000  # Set the character limit to 5000
    },
    {
        "name": "Item_Owner",
        "alias": "Item Owner",
        "type": "esriFieldTypeString",
        "length": 255
    },
    {
        "name": "Number_of_groups",
        "alias": "Number of Groups",
        "type": "esriFieldTypeInteger"
    },
    {
        "name": "Risk_Level",
        "alias": "Risk Level",
        "type": "esriFieldTypeString",
        "length": 255
    },
    {
        "name": "Item_ID",
        "alias": "Item ID",
        "type": "esriFieldTypeString",
        "length": 255
    }
]

# Check if the Hosted table already exists in AGOL
existing_item = gis.content.search(query=f'id:{existing_item_id}')

try:
    if existing_item:
        # Get the existing Feature Layer Collection
        feature_layer_item = existing_item[0]
        feature_layer_collection = FeatureLayerCollection(feature_layer_item.url, gis)
        
        # Overwrite the existing feature layer with the new data
        feature_layer_collection.manager.overwrite(sharing_table)
        print("Updated existing feature layer in AGOL.")
    else:
        # Upload the CSV as a new feature layer
        item_properties = {
            'title': "xyz",
            'tags': "Item Management",
            'description': "xyz."
        }
        feature_layer_item = gis.content.add(item_properties, data=sharing_table, folder="Item_Management")
        
        # Publish the feature layer
        published_layer = feature_layer_item.publish(publish_parameters={"fields":fields})
        print("Uploaded new Hosted Table to AGOL.")

except Exception as e:
    print(f"An error occurred: {e}")

notification_users = ["xyz"]
subject = "xyz"
msgTxt = (f"xyz.")
                    
for user in notification_users:
    gis.users.send_notification([user], subject, msgTxt, type='email')    
    
print("Process completed.")

error:
The code runs successfully, but does not grab all the groups. I thought it might be a character limit issue but when running some debugging scripts, The for loop that cycles through each item and reads the shared groups was only picking up 5 groups for items that were shared with many more than that. I am wondering if I don't need to paginate somewhere in here, but I am not sure how I would go about that.


**Expected behavior**
The code works great for all items and apps that have less than 5 groups shared. I am a novice programmer at best though and relish and tips or tricks. 

**Platform (please complete the following information):**
 - I am working in AGOL Notebook Python 3 Standard - 10.0 (the code also did not work properly in 11.0.)

Anywhere I have put xyz is to replace private information. 
@theisenm12 theisenm12 added the bug label Dec 12, 2024
@nanaeaubry nanaeaubry self-assigned this Dec 16, 2024
@nanaeaubry
Copy link
Contributor

nanaeaubry commented Dec 16, 2024

@theisenm12 Thanks for finding this! It is a bug. For now you can used the shared_with property instead in the Item class.

@theisenm12
Copy link
Author

@nanaeaubry I found that this was also happening with other Item types as well. I will try to replace it with the shared_with function.

Where would I replace "shared_with" in the script to theoretically make it work as a start? I thought that method was deprecated.

@nanaeaubry
Copy link
Contributor

@theisenm12
You can use it on each item: item.shared_with and then get the groups from that. It returns a dictionary.

https://developers.arcgis.com/python/latest/api-reference/arcgis.gis.toc.html#arcgis.gis.Item.shared_with

Deprecated means it will be removed in a later version, not that it doesn't work anymore. You can use it as a workaround for now and we will put a fix in for the other method

@theisenm12
Copy link
Author

@nanaeaubry I replaced "groups = item.sharing.groups.list()" with "groups = item.shared_with()" and am getting the following error. Any thoughts? Should I use an older version than v10.0?

/tmp/ipykernel_36/2597715065.py:22: DeprecatedWarning: shared_with is deprecated as of 2.3.0.1 and has be removed in 3.0.0. Use Item.sharing instead.
groups = item.shared_with()

TypeError Traceback (most recent call last)
/tmp/ipykernel_36/2597715065.py in <cell line: 0>()
20 for item in items:
21 # Use the SharingGroupManager to list groups the item belongs to
---> 22 groups = item.shared_with()
23 group_names = [group['title'] for group in groups]
24

TypeError: 'dict' object is not callable

@nanaeaubry
Copy link
Contributor

@theisenm12 That's because it's a property: item.shared_with

@theisenm12
Copy link
Author

@nanaeaubry Ope, good call. I changed it to item.sharing.shared_with["groups"]. The script ran well and populated my table, however, it looks like the number of groups it called is still capped at 5. Is this potentially the same issue as the .sharing property? Or were you able to get .shared_with to produce more than 5?

@nanaeaubry
Copy link
Contributor

@theisenm12 Hmm no with shared_with we are able to get more than 5 in our code. However, if you are not an admin it will only return groups you have access to.

@theisenm12
Copy link
Author

@nanaeaubry Weird. I have a custom role but it essentially gives me the same permissions as admin. We also just tested the script with my coworker who is an admin and got the same results. Even using the following code snippet, I am still only getting 5 groups returned for specific item ID's.

access the content item as an item object

item = gis.content.get("xyz")

item object -> SharingManager (.sharing)

SharingManager -> SharingGroupManager (.groups)

SharingGroupManager list() method

for group_obj in item.sharing.shared_with["groups"]:
print(group_obj.title)

@theisenm12
Copy link
Author

@nanaeaubry Could you share the script using shared_with that you got to return more than 5 groups? Or have you been able to find something in my script that is limiting us to only return 5 groups?

Thanks and happy new years!

@nanaeaubry
Copy link
Contributor

@theisenm12 We put a fix in for this issue and it will be in 2.4.1

For the shared_with the code is:

gis = GIS(profile="your_online_profile")
item = gis.content.get("item_id")
res = item.shared_with
res["groups"]

Closing since the issue has been fixed for next release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants