from __future__ import annotations # from pyadomd import * import clr import os from System.IO import FileNotFoundException from config import CURRENT_DIR_PATH, NET_FOLDER # MS.NET path AnalysisServices_path = os.path.join( CURRENT_DIR_PATH, NET_FOLDER, "Microsoft.AnalysisServices" ) AdomdClient_path = os.path.join( CURRENT_DIR_PATH, NET_FOLDER, "Microsoft.AnalysisServices.AdomdClient" ) Tabular_path = os.path.join( CURRENT_DIR_PATH, NET_FOLDER, "Microsoft.AnalysisServices.Tabular" ) # Add reference try: clr.AddReference(AdomdClient_path) clr.AddReference(Tabular_path) from Microsoft.AnalysisServices.AdomdClient import AdomdConnection, AdomdCommand from Microsoft.AnalysisServices.Tabular import ( Database, Server, Table, Column, Measure, Partition, Level, Hierarchy, Trace ) except FileNotFoundException as e: print( "========================================================================================" ) print(e.ToString()) print( "========================================================================================" ) from _type_code import adomd_type_map, convert, TypeVar, NamedTuple # Types T = TypeVar("T") class Description(NamedTuple): """ :param [name]: Column name :param [type_code]: The column data type """ name: str type_code: str class Cursor: def __init__(self, connection: AdomdConnection): self._conn = connection self._description: List[Description] = [] def close(self) -> None: """ Closes the cursor """ if self.is_closed: return self._reader.Close() def execute(self, query: str) -> Cursor: """ Executes a query against the data source :params [query]: The query to be executed """ self._cmd = AdomdCommand(query, self._conn) self._reader = self._cmd.ExecuteReader() self._field_count = self._reader.FieldCount for i in range(self._field_count): self._description.append( Description( self._reader.GetName(i), adomd_type_map[self._reader.GetFieldType(i).ToString()].type_name, ) ) return self def fetchone(self) -> Iterator[Tuple[T, ...]]: """ Fetches the current line from the last executed query """ while self._reader.Read(): yield tuple( convert( self._reader.GetFieldType(i).ToString(), self._reader[i], adomd_type_map, ) for i in range(self._field_count) ) def fetchmany(self, size=1) -> List[Tuple[T, ...]]: """ Fetches one or more lines from the last executed query :params [size]: The number of rows to fetch. If the size parameter exceeds the number of rows returned from the last executed query then fetchmany will return all rows from that query. """ l: List[Tuple[T, ...]] = [] try: for i in range(size): l.append(next(self.fetchone())) except StopIteration: pass return l def fetchall(self) -> List[Tuple[T, ...]]: """ Fetches all the rows from the last executed query """ # mypy issues with list comprehension :-( return [i for i in self.fetchone()] # type: ignore @property def is_closed(self) -> bool: try: state = self._reader.IsClosed except AttributeError: return True return state @property def description(self) -> List[Description]: return self._description def __enter__(self) -> Cursor: return self def __exit__(self, exc_type, exc_value, traceback) -> None: self.close() class Pyadomd: def __init__(self, conn_str:str): self.conn = AdomdConnection() self.conn.ConnectionString = conn_str def close(self) -> None: """ Closes the connection """ self.conn.Close() def open(self) -> None: """ Opens the connection """ self.conn.Open() def cursor(self) -> Cursor: """ Creates a cursor object """ c = Cursor(self.conn) return c @property def state(self) -> int: """ 1 = Open 0 = Closed """ return self.conn.State def __enter__(self) -> Pyadomd: self.open() return self def __exit__(self, exc_type, exc_value, traceback) -> None: self.close() class Pytabular: def __init__(self, conn_str: str): self.server = Server() self.server.Connect(conn_str) def close(self) -> None: """Closes the connection""" if self.server is not None: self.server.Disconnect() @property def databases(self) -> List[str]: """Returns a list of database names in the server""" return [db.Name for db in self.server.Databases] @property def state(self) -> int: """1 = Open, 0 = Closed""" return 1 if self.server is not None and self.server.Connected else 0 def __enter__(self) -> Pytabular: return self def __exit__(self, exc_type, exc_value, traceback) -> None: self.close() # @property # def tables(self) -> List[Table]: # """ # Returns a list of tables in the database # """ # return self.db.Model.Tables # @property # def partitions(self) -> List[Partition]: # """ # Returns a list of partitions in the database # """ # return self.db.Model.Partitions # @property # def levels(self) -> List[Level]: # """ # Returns a list of levels in the database # """ # return self.db.Model.Levels # @property # def hierarchies(self) -> List[Hierarchy]: # """ # Returns a list of hierarchies in the database # """ # return self.db.Model.Hierarchies # @property # def measures(self) -> List[Measure]: # """ # Returns a list of measures in the database # """ # return self.db.Model.Measures # @property # def traces(self) -> List[Trace]: # """ # Returns a list of traces in the database # """ # return self.db.Model.Traces if __name__ == "__main__": conn_str = "Provider=MSOLAP;Data Source=localhost" with Pytabular(conn_str) as conn: print("Connected to Analysis Services server") print("Available databases:") for db in conn.databases: print(f"- {db}")