Skip to content

Commit

Permalink
Dashboard duplication support (#154)
Browse files Browse the repository at this point in the history
Add get_dashboards function and duplicate_dashboard function with table change
  • Loading branch information
jp-harvey authored and randyzwitch committed May 1, 2019
1 parent 882a74f commit 7f4c066
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 0 deletions.
94 changes: 94 additions & 0 deletions pymapd/_transforms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import json
from base64 import b64decode, b64encode


def change_dashboard_sources(dashboard, remap):
"""
Remap a dashboard to use a new table
Parameters
----------
dashboard : A dictionary containing the old dashboard state
remap: A dictionary containing the new dashboard state to be mapped
Returns
-------
dashboard : A base64 encoded json object containing the new dashboard state
"""
dm = json.loads(dashboard.dashboard_metadata)
tlst = map(str.strip, dm.get('table', '').split(','))
tlst = [remap[t]['name'] if remap.get(t, {}).get('name',
{}) else t for t in tlst]
dm['table'] = ', '.join(tlst)

# Load our dashboard state into a python dictionary
ds = json.loads(b64decode(dashboard.dashboard_state).decode())

# Remap items in our old dashboard state to the new table name
for old_table, defs in remap.items():
new_table = defs.get('name', {}) or old_table
if ds['dashboard']['table'] == old_table:
ds['dashboard']['table'] = new_table

# Change the name of the dashboard or use the old one
ds['dashboard']['title'] = defs.get('title',
{}) or ds['dashboard']['title']

# Remap our datasources keys
for key, val in ds['dashboard']['dataSources'].items():
for col in val['columnMetadata']:
if col['table'] == old_table:
col['table'] = new_table

# Remap our charts to the new table
for c, val in ds['charts'].items():
if val.get('dataSource', None):
if ds['charts'][c]['dataSource'] == old_table:
ds['charts'][c]['dataSource'] = new_table

# Remap Our Dimensions to the new table
i = 0
for dim in val.get('dimensions', []):
if dim.get('table', {}) == old_table:
ds['charts'][c]['dimensions'][i]['table'] = new_table
if dim.get('selector', {}).get('table') == old_table:
(ds['charts'][c]['dimensions'][i]
['selector']['table']) = new_table
i += 1

# Remap Our Measures to the new table
i = 0
for m in val.get('measures', []):
if m.get('table', None) == old_table:
ds['charts'][c]['measures'][i]['table'] = new_table
i += 1

# Remap Our Layers to the new table
il = 0
for layer in val.get('layers', []):
im = 0
if layer.get('dataSource', {}) == old_table:
ds['charts'][c]['layers'][il]['dataSource'] = new_table
for measure in layer.get('measures', []):
if measure.get('table', None) == old_table:
(ds
['charts']
[c]
['layers']
[il]
['measures']
[im]
['table']
) = new_table
im += 1
il += 1
(ds
['dashboard']
['dataSources']
[new_table]
) = ds['dashboard']['dataSources'].pop(old_table)

# Convert our new dashboard state to a python object
dashboard.dashboard_state = b64encode(json.dumps(ds).encode()).decode()
dashboard.dashboard_metadata = json.dumps(dm)
return dashboard
61 changes: 61 additions & 0 deletions pymapd/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
)

from ._loaders import _build_input_rows
from ._transforms import change_dashboard_sources
from .ipc import load_buffer, shmdt
from ._pandas_loaders import build_row_desc, _serialize_arrow_payload
from . import _pandas_loaders
Expand Down Expand Up @@ -647,6 +648,66 @@ def render_vega(self, vega, compression_level=1):
rendered_vega = RenderedVega(result)
return rendered_vega

def get_dashboards(self):
"""List all the dashboards in the database
Example
--------
>>> con.get_dashboards()
"""
dashboards = self._client.get_dashboards(
session=self._session
)
return dashboards

def duplicate_dashboard(self, dashboard_id, new_name=None,
source_remap=None):
"""
Duplicate an existing dashboard, returning the new dashboard id.
Parameters
----------
dashboard_id : int
The id of the dashboard to duplicate
new_name : str
The name for the new dashboard
source_remap: dict
EXPERIMENTAL
A dictionary remapping table names. The old table name(s)
should be keys of the dict, with each value being another
dict with a 'name' key holding the new table value. This
structure can be used later to support changing column
names.
Example of source_remap format:
{
'oldtablename1': {
'name': 'newtablename1'
},
'oldtablename2': {
'name': 'newtablename2'
}
}
"""
source_remap = source_remap or {}
d = self._client.get_dashboard(
session=self._session,
dashboard_id=dashboard_id
)

newdashname = new_name or '{0} (Copy)'.format(d.dashboard_name)
d = change_dashboard_sources(d, source_remap) if source_remap else d

new_dashboard_id = self._client.create_dashboard(
session=self._session,
dashboard_name=newdashname,
dashboard_state=d.dashboard_state,
image_hash='',
dashboard_metadata=d.dashboard_metadata,
)

return new_dashboard_id


class RenderedVega:
def __init__(self, render_result):
Expand Down

0 comments on commit 7f4c066

Please sign in to comment.