EducationSoftwareStrategy.com
StrategyCommunity

Knowledge Base

Product

Community

Knowledge Base

TopicsBrowse ArticlesDeveloper Zone

Product

Download SoftwareProduct DocumentationSecurity Hub

Education

Tutorial VideosSolution GalleryEducation courses

Community

GuidelinesGrandmastersEvents
x_social-icon_white.svglinkedin_social-icon_white.svg
Strategy logoCommunity

© Strategy Inc. All Rights Reserved.

LegalTerms of UsePrivacy Policy
  1. Home
  2. Topics

Using mstrio and Python script to detect subscription recipients without Distribution Services license


Robert Prochowicz

Manager, Sales Engineering • MicroStrategy


Users can subscribe themselves to reports and dashboards that are sent out automatically to email addresses. It is also possible to subscribe other users. It may happen that subscribed users do not have required Distribution Services license. One can use a Python script and mstrio library to find such cases and remove unauthorized recipients from subscriptions automatically.

Starting with the release of Strategy ONE (March 2024), dossiers are also known as dashboards.
The script below has been created in Jupyter Notebook with Strategy 2021u.7 and mstrio 11.3.6.101. 
Main steps in this script:

  • Establish connection to MSTR environment (in Jupyter Notebook or Workstation) without specifying project
  • List all loaded Projects
  • List all subscriptions per each Projects
  • List all recipients per each subscription; group different kinds of recipients into separate lists
  • Define a list of privileges related to Distribution Services license
  • get privileges for each user from recipient list
  • identify recipients without Distribution Services privileges
  • unsubscribe found users
ka0PW0000001JjhYAE_0EM4W000006H2UP.jpeg

Attached to this article are:

  • code in Jupyter Notebook
  • code in Python file

Please note - I've added color formatting in the code below (http://hilite.me/). Code copied from here might not work due to indentation issues. In that case, use attached file with sample code.
 


from mstrio.connection import Connection
import getpass
from mstrio.server import Environment, Project
from mstrio.access_and_security.privilege import Privilege
from mstrio.users_and_groups import (User, UserGroup)
#from mstrio.connection import get_connection

from mstrio.distribution_services import (
    EmailSubscription,
    list_subscriptions,
    Subscription,
    SubscriptionManager
)

mstr_username = "mstr"
mstr_password = getpass.getpass(prompt='Password ')
mstr_base_url = "https://env-XXXXXX.customer.cloud.Strategy.com"
mstr_url_api  = mstr_base_url+"/MicroStrategyLibrary/api"

conn=Connection(mstr_url_api, mstr_username, mstr_password, login_mode=1)
env = Environment(connection=conn)


# ### List all projects
all_project_ids = [project.id for project in env.list_loaded_projects()]
print(f"Project count: {len(all_project_ids)}")


# ### List all subscriptions
subs_all_list=[]
for project_id in all_project_ids:
    print(project_id)
    subs=list_subscriptions(connection=conn, project_id=project_id)
    subs_all_list.extend([[project_id, s.id, s.name, s.owner] for s in subs])
print(f"\nSubscriptions count (+ one example): {len(subs_all_list)}\n{subs_all_list[:]}")


# ### List all recipients
# create a dict of dicts per recipient type
recipient_types=['contact', 'contact_group', 'personal_address', 'unsupported','user','user_group']
recipient_type_dicts={}
for r in recipient_types:
    recipient_type_dicts[r]=dict()


# In[ ]:
# this one contains all recipients subscriptions (many entries for each recipient), 
# will be used for removing recipients without DS provileges at the end
user_subscr_list=[] 

for i,s in enumerate(subs_all_list): # this one will create a distinct list of recipients
    print(i)
    sub = Subscription(connection=conn, subscription_id=s[1], project_id=s[0])
    for r in sub.recipients:
        user_subscr_list.append([r['id'],s[0],s[1]]) #[recipient_id, project_id, subscription_id]
        r_id, r_type = r['id'],r['type']
        recipient=[r['name'],r['isGroup'],r_type]
        if r['isGroup']:
            recipient_type_dicts['user_group'][r_id]=recipient
            print("\n\n\n#############\n",recipient,"\n#############\n\n\n")
        else:
            if not recipient_type_dicts[r_type].get(r_id):
                recipient_type_dicts[r_type][r_id]=recipient
            
print("\nNumber of different recipient types (distinct):")
for r in recipient_types:
    if recipient_type_dicts[r]:
        print("-",r.ljust(20), len(recipient_type_dicts[r]))
        
print(f"\nNumber of recipients-subscription combinations: {len(user_subscr_list)}\n")  


# In[ ]:
recipient_type_dicts


# In[ ]:
# groups should not be recipients, but sometimes they are and then it looks like this:
'''
[{'id': 'CF1185B64B9152EBC5AB1E8CF0340981',
  'name': 'All dossier consumers in Library',
  'isGroup': True,
  'type': 'all_consumers',
  'includeType': 'TO',
  'childSubscriptionId': '72EEC233491BA46E449C6CB141385256'}]
'''
# see that group name is added to 'type' :(


# ### Define Server Distribution privileges list
distr_privil=[]
df_p=Privilege.list_privileges(conn, to_dataframe=True)
for index, row in df_p.iterrows():
    if row['categories'][0]=="Server - Distribution":
        distr_privil.append(row['id'])

print(distr_privil)        


# ### Get User privileges
users_without_ds=[]
for i,recip in enumerate(recipient_type_dicts['user'].keys()):
    print(i)
    user = User(connection=conn, id=recip)
    if not any(i["privilege"]["id"] in distr_privil for i in user.list_privileges()):
        print("##### no DS #####\n")
        users_without_ds.append(recip)
print(f"\nNumber of recipients without DS privilege: {len(users_without_ds)}\n")        


# ### Remove recipients without DS privileges from subscriptions
# identify subscriptions to clean
# row = [recipient_id, project_id, subscription_id]
for row in user_subscr_list:
    if row[0] in users_without_ds:
        print(row)
        sub = Subscription(connection=conn, subscription_id=row[2], project_id=row[1])
        #print(sub.recipients)
        sub.remove_recipient(recipients=row[0])
        


# # Second approach
# With this approach we brute force it. We skip the step 'List all recipients' above
# We check all users and identify those without DS privileges - just as with 1st approach
# Then, we iterate through all subscriptions and just remove users from 'users_without_ds' list

for proj_id in all_project_ids:    
    sub_mngr = SubscriptionManager(connection=conn, project_id=proj_id)
    for s in sub_mngr.list_subscriptions(to_dictionary=False):
        for USER_TO_REMOVE_ID in users_without_ds:
            if USER_TO_REMOVE_ID in [r['id'] for r in s.recipients]:
                print(s)
                print(f"### Project: {proj_id}\n### User:    {USER_TO_REMOVE_ID}")
                #s.remove_recipient(recipients=USER_TO_REMOVE_ID)


Comment

0 comments

Details

Example

Published:

November 18, 2022

Last Updated:

March 21, 2024