![Logo](./images/logo.png)
## MDXpy
A simple, yet elegant MDX library for TM1
## Install
pip install mdxpy
## Usage
Create MDX queries programmatically with the `Member`, `MdxTuple`, `MdxHierarchySet`, `MdxBuilder` classes.
Benefits of using MDXpy over hacking raw MDX queries in your code
- Faster to write
- Requires less MDX knowledge
- Eliminates syntax errors (e.g. forget `}`, `]`, `)` in a query) forever
- Makes code more robust and easier to refactor
- Escaping of `]` in object names is taken care of
### Member
`Member` is used in `MdxTuple` and `MdxHierarchySet`.
create a `Member` with the static `Member.of(*args: str)` method.
``` python
>>> member = Member.of("Product", "Product1")
>>> print(member.unique_name)
[PRODUCT].[PRODUCT].[PRODUCT1]
>>> member = Member.of("Region", "ByGeography", "UK")
>>> print(member.unique_name)
[REGION].[BYGEOGRAPHY].[UK]
```
### MdxTuple
Create a `MdxTuple` with the static `of(*args: Member)` method. The MDX expression of the tuple is generated with the `to_mdx` method.
``` python
>>> mdx_tuple = MdxTuple.of(Member.of("Product", "Product1"), Member.of("Region", "US"))
>>> print(mdx_tuple.to_mdx())
([PRODUCT].[PRODUCT].[PRODUCT1],[REGION].[REGION].[US])
>>> mdx_tuple = MdxTuple.of(Member.of("Product", "ByType", "Product1"), Member.of("Region", "ByGeography", "North America"))
>>> print(mdx_tuple.to_mdx())
([PRODUCT].[BYTYPE].[PRODUCT1],[REGION].[BYGEOGRAPHY].[North America])
```
you can add a `Member` to a `MdxTuple`
``` python
>>> mdx_tuple = MdxTuple.of(Member.of("Product", "ByType", "Product1"))
>>> mdx_tuple.add_member(Member.of("Region", "ByGeography", "North America"))
>>> print(mdx_tuple.to_mdx())
([PRODUCT].[BYTYPE].[PRODUCT1],[REGION].[BYGEOGRAPHY].[NORTHAMERICA])
```
### MdxHierarchySet
`MdxHierarchySet` is created with any of the static methods on the `MdxHierarchySet` class. The `MDX` expression of the set is generated with the `to_mdx` method.
``` python
>>> mdx_set = MdxHierarchySet.tm1_subset_all("Product")
>>> print(mdx_set.to_mdx())
{TM1SUBSETALL([Product].[Product])}
>>> mdx_set = MdxHierarchySet.tm1_subset_to_set("Region", "By Geography", "Default")
>>> print(mdx_set.to_mdx())
{TM1SUBSETTOSET([REGION].[BYGEOGRAPHY],'Default')}
>>> mdx_set = MdxHierarchySet.all_leaves("Region")
>>> print(mdx_set.to_mdx())
{TM1FILTERBYLEVEL({TM1SUBSETALL([REGION].[REGION])},0)}
>>> mdx_set = MdxHierarchySet.members([Member.of("Region", "US"), Member.of("Product", "Product1")])
>>> print(mdx_set.to_mdx())
{[REGION].[REGION].[US],[PRODUCT].[PRODUCT].[PRODUCT1]}
```
Functions on `MdxHierarchySet` can be concatenated to arbitrary length in a functional style:
``` python
>>> mdx_set = MdxHierarchySet.tm1_subset_all("Region").filter_by_level(0).filter_by_pattern("I*").tm1_sort()
>>> print(mdx_set.to_mdx())
{TM1SORT({TM1FILTERBYPATTERN({TM1FILTERBYLEVEL({TM1SUBSETALL([REGION].[REGION])},0)},'I*')},ASC)}
```
### MdxBuilder
The `MdxBuilder` is used to build MDX queries. `MdxHierarchySet` or `MdxTuple` are placed on the axes. Zero suppression can be switched on or off per axis. The actual `MDX` expression is generated with the `to_mdx` method.
``` python
>>> query = MdxBuilder.from_cube("Cube").add_hierarchy_set_to_column_axis(MdxHierarchySet.all_leaves("Product"))
>>> print(query.to_mdx())
SELECT {TM1FILTERBYLEVEL({TM1SUBSETALL([PRODUCT].[PRODUCT])},0)} ON 0
FROM [CUBE]
>>> query = MdxBuilder.from_cube("Cube").add_hierarchy_set_to_column_axis(MdxHierarchySet.member(Member.of("Product", "Product1")))
>>> print(query.to_mdx())
SELECT {[PRODUCT].[PRODUCT].[PRODUCT1]} ON 0
FROM [CUBE]
>>> query = MdxBuilder.from_cube("Cube").add_member_tuple_to_axis(0, Member.of("Product", "Product1"), Member.of("Region", "EMEA"))
>>> print(query.to_mdx())
SELECT
{([PRODUCT].[PRODUCT].[PRODUCT1],[REGION].[REGION].[EMEA])} ON 0
FROM [CUBE]
>>> query = MdxBuilder.from_cube("Cube").columns_non_empty().add_hierarchy_set_to_column_axis(MdxHierarchySet.member(Member.of("Product", "Product1")))
>>> print(query.to_mdx())
SELECT
NON EMPTY {[PRODUCT].[PRODUCT].[PRODUCT1]} ON 0
FROM [CUBE]
```
MDX queries can have any number of axes. Axis 0 _(=columns)_ must be defined.
``` python
>>> mdx = MdxBuilder.from_cube("Cube") \
.add_hierarchy_set_to_axis(0, MdxHierarchySet.member(Member.of("Region", "US"))) \
.add_hierarchy_set_to_axis(1, MdxHierarchySet.all_leaves("Product")) \
.add_hierarchy_set_to_axis(2, MdxHierarchySet.member(Member.of("Version", "Actual"))) \
.add_hierarchy_set_to_axis(3, MdxHierarchySet.tm1_subset_to_set("Time", "Time", "2020-Q1")) \
.to_mdx()
>>> print(mdx)
SELECT
{[REGION].[REGION].[US]} ON 0,
{TM1FILTERBYLEVEL({TM1SUBSETALL([PRODUCT].[PRODUCT])},0)} ON 1,
{[VERSION].[VERSION].[ACTUAL]} ON 2,
{TM1SUBSETTOSET([TIME].[TIME],'2020-Q1')} ON 3
FROM [CUBE]
```
The `CalculatedMember` class is used to define query-scoped calculated members. They are used with the `MdxBuilder` through the `with_member` function.
``` python
>>> mdx = MdxBuilder.from_cube(cube="Record Rating").with_member(
CalculatedMember.avg(
dimension="Period",
hierarchy="Period",
element="AVG 2016",
cube="Record Rating",
mdx_set=MdxHierarchySet.children(member=Member.of("Period", "2016")),
mdx_tuple=MdxTuple.of(Member.of("Chart", "Total Charts"), Member.of("Record Rating Measure", "Rating")))) \
.add_hierarchy_set_to_row_axis(
MdxHierarchySet
.children(Member.of("Record", "Total Records"))
.top_count(cube="Record Rating", mdx_tuple=MdxTuple.of(Member.of("Period", "AVG 2016")), top=5)) \
.add_member_tuple_to_columns(Member.of("Period", "AVG 2016")) \
.where(Member.of("Chart", "Total Charts"), Member.of("Record Rating Measure", "Rating")) \
.to_mdx()
>>> print(mdx)
WITH
MEMBER [PERIOD].[PERIOD].[AVG2016] AS AVG({[PERIOD].[PERIOD].[2016].CHILDREN},[Record Rating].([CHART].[CHART].[TOTALCHARTS],[RECORDRATINGMEASURE].[RECORDRATINGMEASURE].[RATING]))
SELECT
{([PERIOD].[PERIOD].[AVG2016])} ON 0,
{TOPCOUNT({[RECORD].[RECORD].[TOTALRECORDS].CHILDREN},5,[RECORDRATING].([PERIOD].[PERIOD].[AVG2016]))} ON 1
FROM [RECORDRATING]
WHERE ([CHART].[CHART].[TOTALCHARTS],[RECORDRATINGMEASURE].[RECORDRATINGMEASURE].[RATING])
```
The `DimensionProperty` class is used to query attributes in conjunction with data.
It is used with the `MdxBuilder` through the `add_properties_to_row_axis`, `add_hierarchy_set_to_column_axis` functions.
``` python
from mdxpy import DimensionProperty, MdxHierarchySet, MdxBuilder, Member
query = MdxBuilder.from_cube("Sales")
query = query.rows_non_empty()
query = query.add_hierarchy_set_to_row_axis(MdxHierarchySet.all_leaves("Product"))
query = query.add_properties_to_row_axis(DimensionProperty.of("Product", "Description"))
query = query.columns_non_empty()
query = query.add_hierarchy_set_to_column_axis(MdxHierarchySet.member(Member.of("Sales Measure", "Revenue")))
query = query.where(Member.of("Year", "2022"), Member.of("Region", "Switzerland"))
print(query.to_mdx())
>>> print(mdx)
SELECT
NON EMPTY {[salesmeasure].[salesmeasure].[revenue]} DIMENSION PROPERTIES MEMBER_NAME ON 0,
NON EMPTY {TM1FILTERBYLEVEL({TM1SUBSETALL([product].[product])},0)} DIMENSION PROPERTIES [product].[product].[description] ON 1
FROM [sales]
WHERE ([year].[year].[2022],[region].[region].[switzerland])
```
To see all samples checkout the `test.py` file
## Supported MDX Functions
- TM1SUBSETALL
- MEMBERS
- TM1SUBSETTOSET
- DEFAULTMEMBER
- PARENT
- FIRSTCHILD
- LASTCHILD
- CHILDREN
- ANCESTORS
- ANCESTOR
- DRILLDOWNLEVEL
- FILTER
- TM1FILTERBYPATTERN
- TM1FILTERBYLEVEL
- TM1SORT
- HEAD
- TAIL
- SUBSET
- TOPCOUNT
- BOTTOMCOUNT
- UNION
- INTERSECT
- EXCEPT
- ORDER
## Tests
All tests in `test.py`
## Contribution
Contribution is welcome. If you find a bug or feel like you can contribute please fork the repository, update the code and then create a pull request so we can merge in the changes.
Raw data
{
"_id": null,
"home_page": "https://github.com/cubewise-code/mdxpy",
"name": "mdxpy",
"maintainer": "Marius Wirtz",
"docs_url": null,
"requires_python": ">=3.5",
"maintainer_email": "MWirtz@cubewise.com",
"keywords": "MDX,TM1,IBM Cognos TM1,Planning Analytics,PA,Cognos",
"author": "",
"author_email": "",
"download_url": "https://files.pythonhosted.org/packages/60/ad/5803adf30573335ab170edc58fb026d86909c890eef46cf8ed5c62450b91/mdxpy-1.3.1.tar.gz",
"platform": "any",
"description": "![Logo](./images/logo.png)\r\n\r\n\r\n## MDXpy\r\n\r\nA simple, yet elegant MDX library for TM1\r\n\r\n## Install\r\n\r\n pip install mdxpy\r\n\r\n## Usage\r\n\r\nCreate MDX queries programmatically with the `Member`, `MdxTuple`, `MdxHierarchySet`, `MdxBuilder` classes.\r\n\r\nBenefits of using MDXpy over hacking raw MDX queries in your code\r\n- Faster to write\r\n- Requires less MDX knowledge\r\n- Eliminates syntax errors (e.g. forget `}`, `]`, `)` in a query) forever\r\n- Makes code more robust and easier to refactor\r\n- Escaping of `]` in object names is taken care of \r\n\r\n### Member\r\n\r\n`Member` is used in `MdxTuple` and `MdxHierarchySet`. \r\ncreate a `Member` with the static `Member.of(*args: str)` method.\r\n\r\n``` python\r\n>>> member = Member.of(\"Product\", \"Product1\")\r\n>>> print(member.unique_name)\r\n[PRODUCT].[PRODUCT].[PRODUCT1]\r\n\r\n>>> member = Member.of(\"Region\", \"ByGeography\", \"UK\")\r\n>>> print(member.unique_name)\r\n[REGION].[BYGEOGRAPHY].[UK]\r\n```\r\n\r\n### MdxTuple\r\n\r\nCreate a `MdxTuple` with the static `of(*args: Member)` method. The MDX expression of the tuple is generated with the `to_mdx` method.\r\n\r\n``` python\r\n>>> mdx_tuple = MdxTuple.of(Member.of(\"Product\", \"Product1\"), Member.of(\"Region\", \"US\"))\r\n\r\n>>> print(mdx_tuple.to_mdx())\r\n([PRODUCT].[PRODUCT].[PRODUCT1],[REGION].[REGION].[US])\r\n\r\n>>> mdx_tuple = MdxTuple.of(Member.of(\"Product\", \"ByType\", \"Product1\"), Member.of(\"Region\", \"ByGeography\", \"North America\"))\r\n\r\n>>> print(mdx_tuple.to_mdx())\r\n([PRODUCT].[BYTYPE].[PRODUCT1],[REGION].[BYGEOGRAPHY].[North America])\r\n\r\n``` \r\n\r\nyou can add a `Member` to a `MdxTuple`\r\n\r\n``` python\r\n>>> mdx_tuple = MdxTuple.of(Member.of(\"Product\", \"ByType\", \"Product1\"))\r\n\r\n>>> mdx_tuple.add_member(Member.of(\"Region\", \"ByGeography\", \"North America\"))\r\n\r\n>>> print(mdx_tuple.to_mdx())\r\n([PRODUCT].[BYTYPE].[PRODUCT1],[REGION].[BYGEOGRAPHY].[NORTHAMERICA])\r\n```\r\n\r\n### MdxHierarchySet\r\n\r\n`MdxHierarchySet` is created with any of the static methods on the `MdxHierarchySet` class. The `MDX` expression of the set is generated with the `to_mdx` method.\r\n\r\n``` python\r\n>>> mdx_set = MdxHierarchySet.tm1_subset_all(\"Product\")\r\n>>> print(mdx_set.to_mdx())\r\n{TM1SUBSETALL([Product].[Product])}\r\n\r\n>>> mdx_set = MdxHierarchySet.tm1_subset_to_set(\"Region\", \"By Geography\", \"Default\")\r\n>>> print(mdx_set.to_mdx())\r\n{TM1SUBSETTOSET([REGION].[BYGEOGRAPHY],'Default')}\r\n\r\n>>> mdx_set = MdxHierarchySet.all_leaves(\"Region\")\r\n>>> print(mdx_set.to_mdx())\r\n{TM1FILTERBYLEVEL({TM1SUBSETALL([REGION].[REGION])},0)}\r\n\r\n>>> mdx_set = MdxHierarchySet.members([Member.of(\"Region\", \"US\"), Member.of(\"Product\", \"Product1\")])\r\n>>> print(mdx_set.to_mdx())\r\n{[REGION].[REGION].[US],[PRODUCT].[PRODUCT].[PRODUCT1]}\r\n```\r\n\r\nFunctions on `MdxHierarchySet` can be concatenated to arbitrary length in a functional style:\r\n\r\n``` python\r\n>>> mdx_set = MdxHierarchySet.tm1_subset_all(\"Region\").filter_by_level(0).filter_by_pattern(\"I*\").tm1_sort()\r\n>>> print(mdx_set.to_mdx())\r\n{TM1SORT({TM1FILTERBYPATTERN({TM1FILTERBYLEVEL({TM1SUBSETALL([REGION].[REGION])},0)},'I*')},ASC)}\r\n```\r\n\r\n### MdxBuilder\r\n\r\nThe `MdxBuilder` is used to build MDX queries. `MdxHierarchySet` or `MdxTuple` are placed on the axes. Zero suppression can be switched on or off per axis. The actual `MDX` expression is generated with the `to_mdx` method. \r\n\r\n``` python\r\n>>> query = MdxBuilder.from_cube(\"Cube\").add_hierarchy_set_to_column_axis(MdxHierarchySet.all_leaves(\"Product\"))\r\n>>> print(query.to_mdx())\r\nSELECT {TM1FILTERBYLEVEL({TM1SUBSETALL([PRODUCT].[PRODUCT])},0)} ON 0\r\nFROM [CUBE] \r\n\r\n>>> query = MdxBuilder.from_cube(\"Cube\").add_hierarchy_set_to_column_axis(MdxHierarchySet.member(Member.of(\"Product\", \"Product1\")))\r\n>>> print(query.to_mdx())\r\nSELECT {[PRODUCT].[PRODUCT].[PRODUCT1]} ON 0\r\nFROM [CUBE] \r\n\r\n>>> query = MdxBuilder.from_cube(\"Cube\").add_member_tuple_to_axis(0, Member.of(\"Product\", \"Product1\"), Member.of(\"Region\", \"EMEA\"))\r\n>>> print(query.to_mdx())\r\nSELECT\r\n{([PRODUCT].[PRODUCT].[PRODUCT1],[REGION].[REGION].[EMEA])} ON 0\r\nFROM [CUBE] \r\n\r\n>>> query = MdxBuilder.from_cube(\"Cube\").columns_non_empty().add_hierarchy_set_to_column_axis(MdxHierarchySet.member(Member.of(\"Product\", \"Product1\")))\r\n>>> print(query.to_mdx())\r\nSELECT\r\nNON EMPTY {[PRODUCT].[PRODUCT].[PRODUCT1]} ON 0 \r\nFROM [CUBE]\r\n```\r\n\r\nMDX queries can have any number of axes. Axis 0 _(=columns)_ must be defined.\r\n\r\n``` python\r\n>>> mdx = MdxBuilder.from_cube(\"Cube\") \\\r\n .add_hierarchy_set_to_axis(0, MdxHierarchySet.member(Member.of(\"Region\", \"US\"))) \\\r\n .add_hierarchy_set_to_axis(1, MdxHierarchySet.all_leaves(\"Product\")) \\\r\n .add_hierarchy_set_to_axis(2, MdxHierarchySet.member(Member.of(\"Version\", \"Actual\"))) \\\r\n .add_hierarchy_set_to_axis(3, MdxHierarchySet.tm1_subset_to_set(\"Time\", \"Time\", \"2020-Q1\")) \\\r\n .to_mdx()\r\n\r\n>>> print(mdx)\r\nSELECT\r\n{[REGION].[REGION].[US]} ON 0,\r\n{TM1FILTERBYLEVEL({TM1SUBSETALL([PRODUCT].[PRODUCT])},0)} ON 1,\r\n{[VERSION].[VERSION].[ACTUAL]} ON 2,\r\n{TM1SUBSETTOSET([TIME].[TIME],'2020-Q1')} ON 3\r\nFROM [CUBE]\r\n```\r\n\r\nThe `CalculatedMember` class is used to define query-scoped calculated members. They are used with the `MdxBuilder` through the `with_member` function.\r\n\r\n``` python\r\n>>> mdx = MdxBuilder.from_cube(cube=\"Record Rating\").with_member(\r\n CalculatedMember.avg(\r\n dimension=\"Period\",\r\n hierarchy=\"Period\",\r\n element=\"AVG 2016\",\r\n cube=\"Record Rating\",\r\n mdx_set=MdxHierarchySet.children(member=Member.of(\"Period\", \"2016\")),\r\n mdx_tuple=MdxTuple.of(Member.of(\"Chart\", \"Total Charts\"), Member.of(\"Record Rating Measure\", \"Rating\")))) \\\r\n .add_hierarchy_set_to_row_axis(\r\n MdxHierarchySet\r\n .children(Member.of(\"Record\", \"Total Records\"))\r\n .top_count(cube=\"Record Rating\", mdx_tuple=MdxTuple.of(Member.of(\"Period\", \"AVG 2016\")), top=5)) \\\r\n .add_member_tuple_to_columns(Member.of(\"Period\", \"AVG 2016\")) \\\r\n .where(Member.of(\"Chart\", \"Total Charts\"), Member.of(\"Record Rating Measure\", \"Rating\")) \\\r\n .to_mdx()\r\n\r\n>>> print(mdx)\r\nWITH \r\nMEMBER [PERIOD].[PERIOD].[AVG2016] AS AVG({[PERIOD].[PERIOD].[2016].CHILDREN},[Record Rating].([CHART].[CHART].[TOTALCHARTS],[RECORDRATINGMEASURE].[RECORDRATINGMEASURE].[RATING]))\r\nSELECT\r\n{([PERIOD].[PERIOD].[AVG2016])} ON 0,\r\n{TOPCOUNT({[RECORD].[RECORD].[TOTALRECORDS].CHILDREN},5,[RECORDRATING].([PERIOD].[PERIOD].[AVG2016]))} ON 1\r\nFROM [RECORDRATING]\r\nWHERE ([CHART].[CHART].[TOTALCHARTS],[RECORDRATINGMEASURE].[RECORDRATINGMEASURE].[RATING])\r\n```\r\n\r\nThe `DimensionProperty` class is used to query attributes in conjunction with data. \r\nIt is used with the `MdxBuilder` through the `add_properties_to_row_axis`, `add_hierarchy_set_to_column_axis` functions.\r\n\r\n``` python\r\nfrom mdxpy import DimensionProperty, MdxHierarchySet, MdxBuilder, Member\r\n\r\nquery = MdxBuilder.from_cube(\"Sales\")\r\n\r\nquery = query.rows_non_empty()\r\nquery = query.add_hierarchy_set_to_row_axis(MdxHierarchySet.all_leaves(\"Product\"))\r\nquery = query.add_properties_to_row_axis(DimensionProperty.of(\"Product\", \"Description\"))\r\n\r\nquery = query.columns_non_empty()\r\nquery = query.add_hierarchy_set_to_column_axis(MdxHierarchySet.member(Member.of(\"Sales Measure\", \"Revenue\")))\r\n\r\nquery = query.where(Member.of(\"Year\", \"2022\"), Member.of(\"Region\", \"Switzerland\"))\r\n\r\nprint(query.to_mdx())\r\n\r\n>>> print(mdx)\r\nSELECT\r\nNON EMPTY {[salesmeasure].[salesmeasure].[revenue]} DIMENSION PROPERTIES MEMBER_NAME ON 0,\r\nNON EMPTY {TM1FILTERBYLEVEL({TM1SUBSETALL([product].[product])},0)} DIMENSION PROPERTIES [product].[product].[description] ON 1\r\nFROM [sales]\r\nWHERE ([year].[year].[2022],[region].[region].[switzerland])\r\n```\r\n\r\nTo see all samples checkout the `test.py` file\r\n\r\n## Supported MDX Functions\r\n\r\n- TM1SUBSETALL\r\n- MEMBERS\r\n- TM1SUBSETTOSET\r\n- DEFAULTMEMBER\r\n- PARENT\r\n- FIRSTCHILD\r\n- LASTCHILD\r\n- CHILDREN\r\n- ANCESTORS\r\n- ANCESTOR\r\n- DRILLDOWNLEVEL\r\n- FILTER\r\n- TM1FILTERBYPATTERN\r\n- TM1FILTERBYLEVEL\r\n- TM1SORT\r\n- HEAD\r\n- TAIL\r\n- SUBSET\r\n- TOPCOUNT\r\n- BOTTOMCOUNT\r\n- UNION\r\n- INTERSECT\r\n- EXCEPT\r\n- ORDER\r\n\r\n## Tests\r\n\r\nAll tests in `test.py`\r\n\r\n## Contribution\r\n\r\nContribution is welcome. If you find a bug or feel like you can contribute please fork the repository, update the code and then create a pull request so we can merge in the changes.\r\n",
"bugtrack_url": null,
"license": "MIT-LICENSE",
"summary": "A simple, yet elegant MDX library for TM1",
"version": "1.3.1",
"split_keywords": [
"mdx",
"tm1",
"ibm cognos tm1",
"planning analytics",
"pa",
"cognos"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "60ad5803adf30573335ab170edc58fb026d86909c890eef46cf8ed5c62450b91",
"md5": "1e3d6131d8cbfabc94a198d2dfff005d",
"sha256": "a9dc91e4b6bcf4dc1cf684525ce501154adb173320b3ef6fc5be864a0fe25d9f"
},
"downloads": -1,
"filename": "mdxpy-1.3.1.tar.gz",
"has_sig": false,
"md5_digest": "1e3d6131d8cbfabc94a198d2dfff005d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.5",
"size": 83444,
"upload_time": "2023-04-19T16:52:45",
"upload_time_iso_8601": "2023-04-19T16:52:45.591339Z",
"url": "https://files.pythonhosted.org/packages/60/ad/5803adf30573335ab170edc58fb026d86909c890eef46cf8ed5c62450b91/mdxpy-1.3.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-04-19 16:52:45",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "cubewise-code",
"github_project": "mdxpy",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "mdxpy"
}