Skip to content

Update FHOW notebooks due to org-linked groups #11

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion first_hour_on_vwb/batch_template.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
WORKBENCH_USER_EMAIL,POLICY
WORKBENCH_USER_EMAIL,ROLE
[email protected],MEMBER
[email protected],MEMBER
[email protected],MEMBER
41 changes: 41 additions & 0 deletions first_hour_on_vwb/vwb_org_group_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import json
import subprocess
import sys

def get_org_linked_group_roles(org_id, group_name):
"""
Return a flattened mapping of users to roles for a named org-linked group and org ID.
"""
roles_dict = {role: set() for role in ["ADMIN", "MEMBER", "READER", "SUPPORT"]}

wb_command = ["wb","group","role","list",f"--org={org_id}",f"--name={group_name}","--format=JSON"]
result = subprocess.run(wb_command,capture_output=True,text=True)
nested_roles = json.loads(result.stdout)

for item in nested_roles:
if item['principal']['principalType'] == "GROUP":
roles_dict[role].update(get_org_linked_group_roles(item['principal']['groupOrg'], item['principal']['groupName'])["MEMBER"])
else:
for role in item['roles']:
if item['principal']['userEmail'] != None:
roles_dict[role].add(item['principal']['userEmail'])

return roles_dict

def get_flat_roles_html(roles_dict):
html = f"""<table style='margin: 0 auto,text-align: left'>"""
html += "<th>ROLE</th>"
html += "<th>USER_EMAILS</th>"
html += "<tr>"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whenever I have to do something like this (construct html from strings) I wonder if there's a better way...

not asking for a change -- I think this is perfectly fine in small doses. just musing that it might be worth some thought someday.

for role in roles_dict:
html += "<tr>"
if roles_dict[role] != set():
print(f"role: {role}, users: {roles_dict[role]}")
users = ", ".join(str(e) for e in sorted(roles_dict[role]))
html += f"<td>{role}</td>"
html += f"<td>{users}</td>"
else:
html += f"<td>{role}</td>"
html += f"<td>NONE</td>"
html += "</table>"
return html
83 changes: 72 additions & 11 deletions first_hour_on_vwb/widget_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,24 @@ def list_bq_tables(json_string):
for table in tables:
all_tables.append(f"{row['projectId']}.{row['datasetId']}.{table.table_id}")
return all_tables


def list_groups(json_string):
def list_legacy_groups(json_string):
""" List VWB groups in which user is a member in HTML table form.
"""
html = f"""<table style='margin: 0 auto,text-align: left'>"""
json_data = json.loads(json_string)
# html += "<th>ORG_ID</th>"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this communicating intent of some kind, or a stray comment? there are a few similar commented-out html strings below

html += "<th>NAME</th>"
html += "<th>EMAIL</th>"
html += "<th>POLICIES</th>"
html += "<th>CURRENT_USER_ROLES</th>"
for row in json_data:
html += "<tr>"
html += f"<td>{row['name']}</td>"
html += f"<td>{row['email']}</td>"
html += f"<td>{row['currentUserPolicies'][0]}</td>"
if not row['orgId']:
html += "<tr>"
html += f"<td>{row['name']}</td>"
html += f"<td>{sorted(row['currentUserRoles'])}</td>"
html += "</table>"
return html

def list_group_members(json_string):
def list_legacy_group_members(json_string):
"""
List members of a VWB group in HTML table form.
"""
Expand All @@ -69,10 +68,29 @@ def list_group_members(json_string):
for row in json_data:
html += "<tr>"
html += f"<td>{row['email']}</td>"
html += f"<td>{row['policies'][0]}</td>"
html += f"<td>{sorted(row['policies'])}</td>"
html += "</table>"
return html


def list_org_groups(json_string):
""" List VWB groups in which user is a member in HTML table form.
"""
html = f"""<table style='margin: 0 auto,text-align: left'>"""
json_data = json.loads(json_string)
html += "<th>ORG_ID</th>"
html += "<th>NAME</th>"
# html += "<th>CURRENT_USER_ROLES</th>"
for row in json_data:
if row['orgId'] != 'null':
html += "<tr>"
html += f"<td>{row['orgId']}</td>"
html += f"<td>{row['name']}</td>"
# html += f"<td>{sorted(row['currentUserRoles'])}</td>"
html += "</table>"
return html


def list_data_collections(json_string):
"""
Given the output of the command `wb workspace list`,
Expand Down Expand Up @@ -149,6 +167,32 @@ def get(self):
return self.dropdown_widget


@dataclass
class BoundIntInputWidget:
"""A styled input accepting bounded integers widget with layout.
"""
default_value: int = 0
min_value: int = 0
max_value: int = 0
description: str = ""
bound_int_widget: widgets.BoundedIntText = None

def __post_init__(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neato. I didn't know about this function

self.bound_int_widget = widgets.BoundedIntText(
value=self.default_value,
min=self.min_value,
max=self.max_value,
step=1,
description=self.description,
disabled=False,
layout = {'width' : 'max-content'},
style=input_style
)

def get(self):
'''Return widget.'''
return self.bound_int_widget

@dataclass
class StyledButton():
""" A styled button widget with layout.
Expand All @@ -162,7 +206,7 @@ def __post_init__(self):
self.button = widgets.Button(
description=self.description,
disabled=False,
layout=widgets.Layout(width='75%'),
layout=widgets.Layout(width='100%'),
display='flex',
align_items='stretch',
button_style='',
Expand Down Expand Up @@ -203,3 +247,20 @@ def __init__(self):
def get(self):
'''Return widget.'''
return self.checkbox


class LongLabelCheckbox:
""" Creates a styled checkbox.
"""
def __init__(self, description):
self.checkbox = widgets.Checkbox(
False,
description = description,
layout = {'width' : 'max-content'},
disabled = False,
indent = False
)

def get(self):
'''Return widget.'''
return self.checkbox
Loading