mdxpy


Namemdxpy JSON
Version 1.3.1 PyPI version JSON
download
home_pagehttps://github.com/cubewise-code/mdxpy
SummaryA simple, yet elegant MDX library for TM1
upload_time2023-04-19 16:52:45
maintainerMarius Wirtz
docs_urlNone
author
requires_python>=3.5
licenseMIT-LICENSE
keywords mdx tm1 ibm cognos tm1 planning analytics pa cognos
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ![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"
}
        
Elapsed time: 0.05615s