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

Merged
merged 4 commits into from
May 27, 2025
Merged
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
61 changes: 61 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,61 @@
import json
import subprocess
import sys

def get_org_linked_group_info(org_id, group_name):
"""
Return an org-linked group's description as JSON.
"""
wb_command = ["wb","group","describe",f"--org={org_id}",f"--name={group_name}","--format=JSON"]
result = subprocess.run(wb_command,capture_output=True,text=True)
group_info = json.loads(result.stdout)
return group_info


def expiration_duration(org_id, group_name):
"""
Return the number of days until group role expiration.
"""
info = get_org_linked_group_info(org_id, group_name)
return info['expirationDays'] if 'expirationDays' in info else 0


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>"
for role in roles_dict:
html += "<tr>"
if roles_dict[role] != set():
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

if __name__ == "__main__":
print(get_org_linked_group_info("verily", "emmarogge-friends"))
80 changes: 69 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,23 @@ 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>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,9 +67,26 @@ 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>"
for row in json_data:
if row['orgId'] != 'null':
html += "<tr>"
html += f"<td>{row['orgId']}</td>"
html += f"<td>{row['name']}</td>"
html += "</table>"
return html


def list_data_collections(json_string):
"""
Expand Down Expand Up @@ -149,6 +164,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):
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 +203,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 +244,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(
True,
description = description,
layout = {'width' : 'max-content'},
disabled = False,
indent = False
)

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