Skip to content

Commit bebfb75

Browse files
committed
Fixed some issues and added create_collection function
1 parent 429c482 commit bebfb75

3 files changed

Lines changed: 80 additions & 45 deletions

File tree

CHANGELOG.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
## Unreleased
2+
3+
## 0.2.13
4+
### Added
5+
- `create_collection` function
6+
### Changed
7+
- Fixed the issues #20 and #22.
8+
- Changed the behavior of the `copy_collection` function. Previously it would copy only the content of the source collection, but now copies the contents together with source collection itself.
9+
In other words, now a new collection with the same name as the source collection is created in the destination and the content of the source collection is copied into it.
10+
- Improved the function `make_json`.
11+
212
## 0.2.12
313
### Added
414
- `clone_card` function
515
### Changed
6-
- Fixed the issues #12, #20
7-
- Updated the `search` and `get_db_id` functions to reflect the changes in v.40 of Metabase
16+
- Fixed the issues #12.
17+
- Updated the `search` and `get_db_id` functions to reflect the changes in v.40 of Metabase.
818
- Updated the docstring of the `update_column` function to reflect the changes in v.39 of Metabase.
19+
920
## 0.2.11 (2021-05-03)
1021
### Added
1122
- `search` function (Endpoint: [`GET /api/search/`](https://www.metabase.com/docs/latest/api-documentation.html#get-apisearch))
1223
- `get_card_data` function for getting data of the questions (Endpoint: [`POST /api/card/:card-id/query/:export-format`](https://www.metabase.com/docs/latest/api-documentation.html#post-apicardcard-idqueryexport-format))
24+
1325
## 0.2.10 (2021-04-19)
1426
### Added
1527
- Basic Auth ([PR](https://github.com/vvaezian/metabase_api_python/pull/16))
28+
1629
## 0.2.9 (2021-04-05)
1730
## 0.2.8 (2021-02-01)
1831
## 0.2.7 (2020-11-22)

metabase_api/metabase_api.py

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,31 @@ def create_card(self, card_name=None, collection_name=None, collection_id=None,
458458

459459

460460

461+
def create_collection(self, collection_name, parent_collection_id=None, parent_collection_name=None, return_results=False):
462+
"""
463+
Create a collection using the given arguments utilizing the endpoint 'POST /api/collection/'.
464+
465+
Keyword arguments:
466+
collection_name -- the name used for the created collection.
467+
parent_collection_id -- id of the collection where the created collection resides in.
468+
parent_collection_name -- name of the collection where the created collection resides in (use 'Root' for the root collection).
469+
return_results -- whether to return the resuls or not.
470+
"""
471+
# making sure we have the data we need
472+
if not parent_collection_id:
473+
if not parent_collection_name:
474+
print('Either the name of id of the parent collection must be provided.')
475+
if parent_collection_name == 'Root':
476+
parent_collection_id = None
477+
else:
478+
parent_collection_id = self.get_collection_id(parent_collection_name)
479+
480+
res = self.post('/api/collection', json={'name':collection_name, 'parent_id':parent_collection_id, 'color':'#509EE3'})
481+
if return_results:
482+
return res
483+
484+
485+
461486
def create_segment(self, segment_name, column_name, column_values, segment_description='',
462487
db_name=None, db_id=None, table_name=None, table_id=None, return_segment=False):
463488
"""
@@ -718,17 +743,14 @@ def copy_collection(self, source_collection_name=None, source_collection_id=None
718743
Keyword arguments:
719744
source_collection_name -- name of the collection to copy (default None)
720745
source_collection_id -- id of the collection to copy (default None)
721-
destination_collection_name -- the name to use for the collection in the destination (default None).
746+
destination_collection_name -- the name to be used for the collection in the destination (default None).
722747
If None, it will use the name of the source collection + postfix.
723-
destination_parent_collection_name -- name of the destination parent collection (default None)
724-
destination_parent_collection_id -- id of the destination parent collection (default None)
725-
postfix -- if destination_collection_name is None, adds this string to the end of source_collection_name
726-
to make destination_collection_name.
727-
child_items_postfix -- this string is added to the end of the child items names,
728-
when saving them in the destination (default '').
729-
deepcopy_dashboards -- whether to duplicate the cards inside dashboards (default False).
730-
savinge, putin the duplicated cards in a collection called
748+
destination_parent_collection_name -- name of the destination parent collection (default None). This is the collection that would have the copied collection as a child.
749+
destination_parent_collection_id -- id of the destination parent collection (default None). This is the collection that would have the copied collection as a child.
750+
deepcopy_dashboards -- whether to duplicate the cards inside the dashboards (default False). If True, puts the duplicated cards in a collection called
731751
"[dashboard_name]'s duplicated cards" in the same path as the duplicated dashboard.
752+
postfix -- if destination_collection_name is None, adds this string to the end of source_collection_name to make destination_collection_name.
753+
child_items_postfix -- this string is added to the end of the child items' names, when saving them in the destination (default '').
732754
verbose -- prints extra information (default False)
733755
"""
734756
### Making sure we have the data that we need
@@ -749,62 +771,63 @@ def copy_collection(self, source_collection_name=None, source_collection_id=None
749771
source_collection_name = self.get_item_name(item_type='collection', item_id=source_collection_id)
750772
destination_collection_name = source_collection_name + postfix
751773

752-
# getting the info of the source collection
753-
source_collection = self.get('/api/collection/{}'.format(source_collection_id))
754-
755-
### copying the items of the source collection to the new collection
774+
### Creating a collection in the destination to hold the contents of the source collection
775+
res = self.create_collection(destination_collection_name,
776+
parent_collection_id=destination_parent_collection_id,
777+
parent_collection_name=destination_parent_collection_name,
778+
return_results=True
779+
)
780+
destination_collection_id = res['id']
781+
782+
### Getting the items to copy
756783
items = self.get('/api/collection/{}/items'.format(source_collection_id))
757-
784+
if type(items) == dict: # in Metabase version *.40.0 the format of the returned result for this endpoint changed
785+
items = items['data']
786+
787+
### copying the items of the source collection to the new collection
758788
for item in items:
759789

760-
### copying a collection
790+
## copying a collection
761791
if item['model'] == 'collection':
762792
collection_id = item['id']
763793
collection_name = item['name']
764-
destination_dashboard_name = collection_name + child_items_postfix
765-
self.verbose_print(verbose, 'Copying the collection {} ...'.format(collection_name))
766-
767-
# creating an empty collection in the destination
768-
res = self.post('/api/collection/', json={'name':collection_name,
769-
'color':'#509EE3',
770-
'parent_id':destination_parent_collection_id})
771-
created_collection_id = res['id']
772-
794+
destination_collection_name = collection_name + child_items_postfix
795+
self.verbose_print(verbose, 'Copying the collection "{}" ...'.format(collection_name))
773796
self.copy_collection(source_collection_id=collection_id,
774-
destination_parent_collection_id=created_collection_id,
797+
destination_parent_collection_id=destination_collection_id,
775798
child_items_postfix=child_items_postfix,
776799
deepcopy_dashboards=deepcopy_dashboards,
777800
verbose=verbose)
778801

779-
### copying a dashboard
802+
## copying a dashboard
780803
if item['model'] == 'dashboard':
781804
dashboard_id = item['id']
782805
dashboard_name = item['name']
783806
destination_dashboard_name = dashboard_name + child_items_postfix
784-
self.verbose_print(verbose, 'Copying the dashboard {} ...'.format(dashboard_name))
807+
self.verbose_print(verbose, 'Copying the dashboard "{}" ...'.format(dashboard_name))
785808
self.copy_dashboard(source_dashboard_id=dashboard_id,
786-
destination_collection_id=destination_parent_collection_id,
809+
destination_collection_id=destination_collection_id,
787810
destination_dashboard_name=destination_dashboard_name,
788811
deepcopy=deepcopy_dashboards)
789812

790-
### copying a card
813+
## copying a card
791814
if item['model'] == 'card':
792815
card_id = item['id']
793816
card_name = item['name']
794817
destination_card_name = card_name + child_items_postfix
795-
self.verbose_print(verbose, 'Copying the card {} ...'.format(card_name))
818+
self.verbose_print(verbose, 'Copying the card "{}" ...'.format(card_name))
796819
self.copy_card(source_card_id=card_id,
797-
destination_collection_id=destination_parent_collection_id,
820+
destination_collection_id=destination_collection_id,
798821
destination_card_name=destination_card_name)
799822

800-
### copying a pulse
823+
## copying a pulse
801824
if item['model'] == 'pulse':
802825
pulse_id = item['id']
803826
pulse_name = item['name']
804827
destination_pulse_name = pulse_name + child_items_postfix
805-
self.verbose_print(verbose, 'Copying the pulse {} ...'.format(pulse_name))
828+
self.verbose_print(verbose, 'Copying the pulse "{}" ...'.format(pulse_name))
806829
self.copy_pulse(source_pulse_id=pulse_id,
807-
destination_collection_id=destination_parent_collection_id,
830+
destination_collection_id=destination_collection_id,
808831
destination_pulse_name=destination_pulse_name)
809832

810833

@@ -823,7 +846,7 @@ def search(self, q, item_type=None, archived=False):
823846
assert archived in [True, False]
824847

825848
res = self.get(endpoint='/api/search/', params={'q':q, 'archived':archived})
826-
if type(res) == dict: # because in version *.40.0 the behaviour of this endpoint changed
849+
if type(res) == dict: # bin Metabase version *.40.0 the format of the returned result for this endpoint changed
827850
res = res['data']
828851
if item_type is not None:
829852
res = [ item for item in res if item['model'] == item_type ]
@@ -852,7 +875,8 @@ def get_card_data(self, card_name=None, card_id=None, collection_name=None, coll
852875
res = self.post("/api/card/{}/query/{}".format(card_id, data_format), 'raw')
853876

854877
if data_format == 'json':
855-
return eval(res.text.replace('null', 'None'))
878+
import json
879+
return json.loads(res.text)
856880
if data_format == 'csv':
857881
return res.text.replace('null', '')
858882

@@ -990,12 +1014,10 @@ def update_column(self, params,
9901014
@staticmethod
9911015
def make_json(raw_json, prettyprint=False):
9921016
"""Turn the string copied from the Inspect->Network window into a Dict."""
993-
json = eval(raw_json.replace('null', 'None') \
994-
.replace('false', 'False') \
995-
.replace('true', 'True')
996-
)
1017+
import json
1018+
ret_dict = json.loads(raw_json)
9971019
if prettyprint:
9981020
import pprint
999-
pprint.pprint(json)
1021+
pprint.pprint(ret_dict)
10001022

1001-
return json
1023+
return ret_dict

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setuptools.setup(
77
name="metabase-api",
8-
version="0.2.12",
8+
version="0.2.13",
99
author="Vahid Vaezian",
1010
author_email="vahid.vaezian@gmail.com",
1111
description="A Python Wrapper for Metabase API",

0 commit comments

Comments
 (0)