Skip to content

PyColumns

Bases: PyObjects

Groups together multiple PyColumn().

See PyObjects class for what more it can do. You can interact with PyColumns straight from model. For ex: model.Columns. Or through individual tables model.Tables[TABLE_NAME].Columns. You can even filter down with .Find(). For example find all columns with Key in name. model.Columns.Find('Key').

Source code in pytabular/column.py
class PyColumns(PyObjects):
    """Groups together multiple `PyColumn()`.

    See `PyObjects` class for what more it can do.
    You can interact with `PyColumns` straight from model. For ex: `model.Columns`.
    Or through individual tables `model.Tables[TABLE_NAME].Columns`.
    You can even filter down with `.Find()`.
    For example find all columns with `Key` in name.
    `model.Columns.Find('Key')`.
    """

    def __init__(self, objects) -> None:
        """Init extends through to the `PyObjects()` init."""
        super().__init__(objects)

    def query_all(self, query_function: str = "COUNTROWS(VALUES(_))") -> pd.DataFrame:
        """This will dynamically all columns in `PyColumns()` class.

        It will replace the `_` with the column to run
        whatever the given `query_function` value is.

        Args:
                query_function (str, optional): Default is `COUNTROWS(VALUES(_))`.
                        The `_` gets replaced with the column in question.
                        Method will take whatever DAX query is given.

        Returns:
                pd.DataFrame: Returns dataframe with results.
        """
        logger.info("Beginning execution of querying every column...")
        logger.debug(f"Function to be run: {query_function}")
        logger.debug("Dynamically creating DAX query...")
        query_str = "EVALUATE UNION(\n"
        columns = [column for column in self]
        for column in columns:
            if column.Type != ColumnType.RowNumber:
                table_name = column.Table.get_Name()
                column_name = column.get_Name()
                dax_identifier = f"'{table_name}'[{column_name}]"
                query_str += f"ROW(\"Table\",\"{table_name}\",\
                    \"Column\",\"{column_name}\",\"{query_function}\",\
                    {query_function.replace('_',dax_identifier)}),\n"  # noqa: E231, E261
        query_str = f"{query_str[:-2]})"
        return self[0].Table.Model.query(query_str)

__init__(objects)

Init extends through to the PyObjects() init.

Source code in pytabular/column.py
def __init__(self, objects) -> None:
    """Init extends through to the `PyObjects()` init."""
    super().__init__(objects)

query_all(query_function='COUNTROWS(VALUES(_))')

This will dynamically all columns in PyColumns() class.

It will replace the _ with the column to run whatever the given query_function value is.

Parameters:

Name Type Description Default
query_function str

Default is COUNTROWS(VALUES(_)). The _ gets replaced with the column in question. Method will take whatever DAX query is given.

'COUNTROWS(VALUES(_))'

Returns:

Type Description
DataFrame

pd.DataFrame: Returns dataframe with results.

Source code in pytabular/column.py
def query_all(self, query_function: str = "COUNTROWS(VALUES(_))") -> pd.DataFrame:
    """This will dynamically all columns in `PyColumns()` class.

    It will replace the `_` with the column to run
    whatever the given `query_function` value is.

    Args:
            query_function (str, optional): Default is `COUNTROWS(VALUES(_))`.
                    The `_` gets replaced with the column in question.
                    Method will take whatever DAX query is given.

    Returns:
            pd.DataFrame: Returns dataframe with results.
    """
    logger.info("Beginning execution of querying every column...")
    logger.debug(f"Function to be run: {query_function}")
    logger.debug("Dynamically creating DAX query...")
    query_str = "EVALUATE UNION(\n"
    columns = [column for column in self]
    for column in columns:
        if column.Type != ColumnType.RowNumber:
            table_name = column.Table.get_Name()
            column_name = column.get_Name()
            dax_identifier = f"'{table_name}'[{column_name}]"
            query_str += f"ROW(\"Table\",\"{table_name}\",\
                \"Column\",\"{column_name}\",\"{query_function}\",\
                {query_function.replace('_',dax_identifier)}),\n"  # noqa: E231, E261
    query_str = f"{query_str[:-2]})"
    return self[0].Table.Model.query(query_str)

The main parent class for grouping your (Tables, Columns, Measures, Partitions, etc.).

Notice the magic methods. __rich_repr__() starts the baseline for displaying your model. It uses the amazing rich python package and builds your display from the self._display. Still building out the magic methods to give PyObjects more flexibility.

Source code in pytabular/object.py
class PyObjects:
    """The main parent class for grouping your (Tables, Columns, Measures, Partitions, etc.).

    Notice the magic methods. `__rich_repr__()` starts the baseline for displaying your model.
    It uses the amazing `rich` python package and
    builds your display from the `self._display`.
    Still building out the magic methods to give `PyObjects` more flexibility.
    """

    def __init__(self, objects: list[PyObject], parent=None) -> None:
        """Initialization of `PyObjects`.

        Takes the objects in something that is iterable.
        Then will build a default `rich` table display.

        Args:
            objects(list[PyObject]): .Net objects.
            parent: Parent Object. Defaults to `None`.
        """
        self._objects = objects
        self.parent = parent
        self._display = Table(title=str(self.__class__.mro()[0]))
        for index, obj in enumerate(self._objects):
            self._display.add_row(str(index), obj.Name)

    def __rich_repr__(self) -> str:
        """See [Rich Repr](https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol)."""
        Console().print(self._display)

    def __getitem__(self, object):
        """Get item from `PyObjects`.

        Checks if item is str or int.
        If string will iterate through and try to find matching name.
        Otherwise, will call into `self._objects[int]` to retrieve item.
        """
        if isinstance(object, str):
            return [pyobject for pyobject in self._objects if object == pyobject.Name][
                -1
            ]
        elif isinstance(object, slice):
            cls = type(self)
            return cls(self._objects[object])
        else:
            return self._objects[object]

    def __iter__(self):
        """Iterate through `PyObjects`."""
        yield from self._objects

    def __len__(self) -> int:
        """Get length of `PyObjects`.

        Returns:
            int: Number of PyObject in PyObjects
        """
        return len(self._objects)

    def __iadd__(self, obj):
        """Add a `PyObject` or `PyObjects` to your current `PyObjects` class.

        This is useful for building out a custom `PyObjects` class to work with.
        """
        if isinstance(obj, Iterable):
            self._objects.__iadd__(obj._objects)
        else:
            self._objects.__iadd__([obj])

        self.__init__(self._objects)
        return self

    def _first_visible_object(self):
        """Does what the method is called. Get's first `object.IsHidden is False`."""
        for object in self:
            if object.IsHidden is False:
                return object
        return None

    def find(self, object_str: str):
        """Finds any or all `PyObject` inside of `PyObjects` that match the `object_str`.

        It is case insensitive.

        Args:
            object_str (str): str to lookup in `PyObjects`

        Returns:
            PyObjects (object.PyObjects): Returns a `PyObjects` class with all `PyObject`
                where the `PyObject.Name` matches `object_str`.
        """
        items = [
            object
            for object in self._objects
            if object_str.lower() in object.Name.lower()
        ]
        return self.__class__.mro()[0](items)

    def get(self, object_str: str, alt_result: str = "") -> str:
        """Gets the object based on str.

        If the object isnt found, then an alternate result
        can be supplied as an argument.

        Args:
            object_str (str): str to lookup object
            alt_result (str): str to return when value isn't found.

        Returns:
            str: Result of the lookup, or the alternate result.
        """
        try:
            return self.__getitem__(object_str)
        except Exception as e:
            Console().print(e)

        return alt_result

find(object_str)

Finds any or all PyObject inside of PyObjects that match the object_str.

It is case insensitive.

Parameters:

Name Type Description Default
object_str str

str to lookup in PyObjects

required

Returns:

Name Type Description
PyObjects PyObjects

Returns a PyObjects class with all PyObject where the PyObject.Name matches object_str.

Source code in pytabular/object.py
def find(self, object_str: str):
    """Finds any or all `PyObject` inside of `PyObjects` that match the `object_str`.

    It is case insensitive.

    Args:
        object_str (str): str to lookup in `PyObjects`

    Returns:
        PyObjects (object.PyObjects): Returns a `PyObjects` class with all `PyObject`
            where the `PyObject.Name` matches `object_str`.
    """
    items = [
        object
        for object in self._objects
        if object_str.lower() in object.Name.lower()
    ]
    return self.__class__.mro()[0](items)