From e680104adb1e3c219f13818b218dd3087dd01009 Mon Sep 17 00:00:00 2001 From: THIVEND Date: Mon, 24 Oct 2022 11:08:14 +0200 Subject: [PATCH 01/10] First commit From d44371bd023f0b0ad2b67ac24267d88dfe579e93 Mon Sep 17 00:00:00 2001 From: THIVEND Date: Mon, 24 Oct 2022 11:08:28 +0200 Subject: [PATCH 02/10] Add minimal setup for dashboard creation --- metabase_api/metabase_api.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/metabase_api/metabase_api.py b/metabase_api/metabase_api.py index 6d3f07f..3e45b40 100644 --- a/metabase_api/metabase_api.py +++ b/metabase_api/metabase_api.py @@ -642,6 +642,26 @@ def create_segment(self, segment_name, column_name, column_values, segment_descr + def create_dashboard(self, name, description=None, parameters=None, cache_ttl=None, collection_id=None, collection_position=None): + assert name + + json = {} + if description: + json['description'] = description + if collection_id: + assert isinstance(collection_ie, int) + assert collection_id != 0 + + json['collection_id'] = collection_id + + if collection_position: + assert isinstance(collection_position, int) + assert collection_position != 0 + + json['collection_position'] = collection_position + + res = self.post("/api/dashboard", params=parameters) + def copy_card(self, source_card_name=None, source_card_id=None, source_collection_name=None, source_collection_id=None, destination_card_name=None, From 8436b45b16216156eeb970cf812582c79df4323f Mon Sep 17 00:00:00 2001 From: THIVEND Date: Thu, 27 Oct 2022 13:42:50 +0200 Subject: [PATCH 03/10] Add helper create_dashboard --- README.md | 1 + metabase_api/metabase_api.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0783b95..7a83f15 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ You usually don't need to deal with these functions directly (e.g. [get_item_inf - [create_segment](https://github.com/vvaezian/metabase_api_python/blob/150c8143bf3ec964568d54bddd80bf9c1b2ca214/metabase_api/metabase_api.py#L486) - [copy_card](https://github.com/vvaezian/metabase_api_python/blob/150c8143bf3ec964568d54bddd80bf9c1b2ca214/metabase_api/metabase_api.py#L530) - [copy_pulse](https://github.com/vvaezian/metabase_api_python/blob/150c8143bf3ec964568d54bddd80bf9c1b2ca214/metabase_api/metabase_api.py#L591) +- [create_dashboard] - [copy_dashboard](https://github.com/vvaezian/metabase_api_python/blob/150c8143bf3ec964568d54bddd80bf9c1b2ca214/metabase_api/metabase_api.py#L643) - [copy_collection](https://github.com/vvaezian/metabase_api_python/blob/150c8143bf3ec964568d54bddd80bf9c1b2ca214/metabase_api/metabase_api.py#L736) - [clone_card](https://github.com/vvaezian/metabase_api_python/blob/77ef837972bc169f96a3ca520da769e0b933e8a8/metabase_api/metabase_api.py#L1003) diff --git a/metabase_api/metabase_api.py b/metabase_api/metabase_api.py index 3e45b40..d3274ab 100644 --- a/metabase_api/metabase_api.py +++ b/metabase_api/metabase_api.py @@ -62,6 +62,7 @@ def get(self, endpoint, *args, **kwargs): def post(self, endpoint, *args, **kwargs): + import pdb; pdb.set_trace() self.validate_session() res = requests.post(self.domain + endpoint, headers=self.header, **kwargs, auth=self.auth) if 'raw' in args: @@ -642,14 +643,18 @@ def create_segment(self, segment_name, column_name, column_values, segment_descr - def create_dashboard(self, name, description=None, parameters=None, cache_ttl=None, collection_id=None, collection_position=None): + def create_dashboard(self, name, description=None, parameters=None, cache_ttl=None, collection_id=None, collection_position=None, collection_name=None): assert name - json = {} + json = {'name': name} if description: json['description'] = description + + if collection_name: + collection_id = self.get_item_id('collection', collection_name) + if collection_id: - assert isinstance(collection_ie, int) + assert isinstance(collection_id, int) assert collection_id != 0 json['collection_id'] = collection_id @@ -659,8 +664,11 @@ def create_dashboard(self, name, description=None, parameters=None, cache_ttl=No assert collection_position != 0 json['collection_position'] = collection_position + + + - res = self.post("/api/dashboard", params=parameters) + res = self.post("/api/dashboard", json=json) def copy_card(self, source_card_name=None, source_card_id=None, source_collection_name=None, source_collection_id=None, From b81f9d4c9ae8bdbbe0231618449aba58bb945817 Mon Sep 17 00:00:00 2001 From: THIVEND Date: Thu, 27 Oct 2022 13:46:43 +0200 Subject: [PATCH 04/10] Fix empty lines convention --- metabase_api/metabase_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/metabase_api/metabase_api.py b/metabase_api/metabase_api.py index d3274ab..e83e016 100644 --- a/metabase_api/metabase_api.py +++ b/metabase_api/metabase_api.py @@ -669,7 +669,9 @@ def create_dashboard(self, name, description=None, parameters=None, cache_ttl=No res = self.post("/api/dashboard", json=json) - + + + def copy_card(self, source_card_name=None, source_card_id=None, source_collection_name=None, source_collection_id=None, destination_card_name=None, From fe666db1d74a0f2c8edd1ffae876870132733a93 Mon Sep 17 00:00:00 2001 From: THIVEND Date: Thu, 27 Oct 2022 14:45:11 +0200 Subject: [PATCH 05/10] Remove pdb --- metabase_api/metabase_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/metabase_api/metabase_api.py b/metabase_api/metabase_api.py index e83e016..45bf270 100644 --- a/metabase_api/metabase_api.py +++ b/metabase_api/metabase_api.py @@ -62,7 +62,6 @@ def get(self, endpoint, *args, **kwargs): def post(self, endpoint, *args, **kwargs): - import pdb; pdb.set_trace() self.validate_session() res = requests.post(self.domain + endpoint, headers=self.header, **kwargs, auth=self.auth) if 'raw' in args: From 331575998610badfddb5d0b516254fa453a1f1d3 Mon Sep 17 00:00:00 2001 From: THIVEND Date: Thu, 27 Oct 2022 21:46:34 +0200 Subject: [PATCH 06/10] Correct parameters setup for create_dashboard --- metabase_api/metabase_api.py | 51 ++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/metabase_api/metabase_api.py b/metabase_api/metabase_api.py index 45bf270..6885ca8 100644 --- a/metabase_api/metabase_api.py +++ b/metabase_api/metabase_api.py @@ -642,32 +642,43 @@ def create_segment(self, segment_name, column_name, column_values, segment_descr - def create_dashboard(self, name, description=None, parameters=None, cache_ttl=None, collection_id=None, collection_position=None, collection_name=None): - assert name + def create_dashboard( + self, name, description=None, parameters=[], cache_ttl=None, + collection_id=None, collection_position=None, collection_name=None): + custom_json={} - json = {'name': name} + if not(name and isinstance(name, str)): + raise ValueError("Dashboard name incorrect. Please provide a string for dashboard name") + custom_json['name'] = name + if description: - json['description'] = description - - if collection_name: - collection_id = self.get_item_id('collection', collection_name) + custom_json['description'] = description - if collection_id: - assert isinstance(collection_id, int) - assert collection_id != 0 + if parameters: + if not isinstance(parameters, list): + raise ValueError("Parameter 'parameters' incorrect. Please provide an array") + for element in parameters: + if set(element.keys()) != {'id', 'type'}: + raise ValueError("Parameter 'parameters' incorrect. Please provide an array of maps of keys 'id' and 'type'") + custom_json['parameters'] = parameters - json['collection_id'] = collection_id + if cache_ttl: + if not(isinstance(cache_ttl, int) and cache_ttl > 0): + raise ValueError("Parameter `cache_ttl` must be a scrictly positive integer. Please provide a correct value") + custom_json['cache_ttl']: cache_ttl - if collection_position: - assert isinstance(collection_position, int) - assert collection_position != 0 - - json['collection_position'] = collection_position - + collection_id = self.get_item_id('collection', collection_name) if collection_name else collection_id + if collection_id: + if not isinstance(collection_id, int): + raise ValueError("Parameter `collection_id` must be an integer. Please provide a correct value") + custom_json['collection_id'] = collection_id - - - res = self.post("/api/dashboard", json=json) + if collection_position: + if not(isinstance(collection_position, int) and collection_position > 0): + raise ValueError("Parameter `collection_position` must be a stricly positive integer. Please provide a correct value") + custom_json['collection_position'] = collection_position + + res = self.post("/api/dashboard", json=custom_json) From 22b8372d8be650f26d284e2a9221102e96100f53 Mon Sep 17 00:00:00 2001 From: THIVEND Date: Thu, 27 Oct 2022 21:48:18 +0200 Subject: [PATCH 07/10] Add return_dashbaord argument --- metabase_api/metabase_api.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/metabase_api/metabase_api.py b/metabase_api/metabase_api.py index 6885ca8..ff5c8a0 100644 --- a/metabase_api/metabase_api.py +++ b/metabase_api/metabase_api.py @@ -644,7 +644,8 @@ def create_segment(self, segment_name, column_name, column_values, segment_descr def create_dashboard( self, name, description=None, parameters=[], cache_ttl=None, - collection_id=None, collection_position=None, collection_name=None): + collection_id=None, collection_position=None, collection_name=None, + return_dashboard=False): custom_json={} if not(name and isinstance(name, str)): @@ -679,6 +680,8 @@ def create_dashboard( custom_json['collection_position'] = collection_position res = self.post("/api/dashboard", json=custom_json) + if return_dashboard: + return res From ca91056fb6b2b00b34f1bb235dad4dd9e0cbe243 Mon Sep 17 00:00:00 2001 From: THIVEND Date: Thu, 27 Oct 2022 22:01:30 +0200 Subject: [PATCH 08/10] Add description and fix collection_id setup --- metabase_api/metabase_api.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/metabase_api/metabase_api.py b/metabase_api/metabase_api.py index ff5c8a0..46b37a5 100644 --- a/metabase_api/metabase_api.py +++ b/metabase_api/metabase_api.py @@ -646,6 +646,20 @@ def create_dashboard( self, name, description=None, parameters=[], cache_ttl=None, collection_id=None, collection_position=None, collection_name=None, return_dashboard=False): + """ + Create a dashboard using the given arguments utilizing the endpoint 'POST /api/dashboard/'. + If collection is not given, the root collection is used. + + Keyword arguments: + name -- name used to create the dashboard (mandatory) + description -- description of the dashboard (default Non) + collection_name -- name of the collection to place the dashboard (default None) + collection_id -- id of the collection to place the dashboard (default None) + collection_position -- position of the dashboard in the collection(default None) + cache_ttl -- Cache Time-to-Live, multiplier for caching management (see https://www.metabase.com/docs/latest/configuring-metabase/caching#cache-time-to-live-ttl) + parameters -- Array of maps for fine-tuning your dashboard. Each map must be of the form {'id': ..., 'type': ...} + return_dashboard -- whather to return the created dashboard info (default False) + """ custom_json={} if not(name and isinstance(name, str)): @@ -670,9 +684,12 @@ def create_dashboard( collection_id = self.get_item_id('collection', collection_name) if collection_name else collection_id if collection_id: - if not isinstance(collection_id, int): + if isinstance(collection_id, None): + pass + elif isinstance(collection_id, int): + custom_json['collection_id'] = collection_id + else: raise ValueError("Parameter `collection_id` must be an integer. Please provide a correct value") - custom_json['collection_id'] = collection_id if collection_position: if not(isinstance(collection_position, int) and collection_position > 0): From bb6cf0342c798139fa0d83e6caac81d55fc3c70a Mon Sep 17 00:00:00 2001 From: THIVEND Date: Thu, 27 Oct 2022 22:05:50 +0200 Subject: [PATCH 09/10] Setup for test_create_dashboard --- tests/test_metabase_api.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_metabase_api.py b/tests/test_metabase_api.py index 04ded25..2d0d3f1 100644 --- a/tests/test_metabase_api.py +++ b/tests/test_metabase_api.py @@ -240,6 +240,11 @@ def test_create_segment(self): + def test_create_dashboard(self): + pass + + + def test_copy_card(self): t = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') newCard_id = mb.copy_card(source_card_id=166, destination_collection_id=29, destination_card_name='test_copy_card_{}'.format(t)) From 3c9594b565439065de133086c74484f7fc60e4a1 Mon Sep 17 00:00:00 2001 From: THIVEND Date: Thu, 27 Oct 2022 22:15:40 +0200 Subject: [PATCH 10/10] Minimal tests for create_dashboard --- metabase_api/metabase_api.py | 2 +- tests/test_metabase_api.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/metabase_api/metabase_api.py b/metabase_api/metabase_api.py index 46b37a5..84229a6 100644 --- a/metabase_api/metabase_api.py +++ b/metabase_api/metabase_api.py @@ -663,7 +663,7 @@ def create_dashboard( custom_json={} if not(name and isinstance(name, str)): - raise ValueError("Dashboard name incorrect. Please provide a string for dashboard name") + raise ValueError("Dashboard name incorrect. Please provide a valid string for dashboard name") custom_json['name'] = name if description: diff --git a/tests/test_metabase_api.py b/tests/test_metabase_api.py index 2d0d3f1..185242d 100644 --- a/tests/test_metabase_api.py +++ b/tests/test_metabase_api.py @@ -241,6 +241,18 @@ def test_create_segment(self): def test_create_dashboard(self): + t = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + dummy_name = 'dummy dashboard' + + with self.assertRaises(ValueError) as error: + res = mb.create_dashboard(name='') + self.assertEqual( + str(error.exception), + 'Dashboard name incorrect. Please provide a valid string for dashboard name' + ) + res = mb.create_dashboard(dummy_name) + + Metabase_API_Test.cleanup_objects['dashboard'].append(res['id']) pass