From 655c05691a84eb045c925cc27a3195935d1a2348 Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 25 Aug 2022 18:10:26 -0500 Subject: [PATCH 1/3] remove local file so I don't have to rely on git --- test/test_tabular.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_tabular.py b/test/test_tabular.py index 363b3c3..ac0bfaa 100644 --- a/test/test_tabular.py +++ b/test/test_tabular.py @@ -1,11 +1,11 @@ import pytabular -from pytabular import localsecret +import local as l import pytest import pandas as pd from Microsoft.AnalysisServices.Tabular import Database -aas = pytabular.Tabular(localsecret.CONNECTION_STR['FIN 500']) -gen2 = pytabular.Tabular(localsecret.CONNECTION_STR['GEN2TEST']) +aas = pytabular.Tabular(l.AAS) +gen2 = pytabular.Tabular(l.GEN2) testing_parameters = [(aas),(gen2)] testingtable = 'PyTestTable' From 521f2d3b39c4eca1cffff3d4b20cc2c995021f15 Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 25 Aug 2022 18:13:23 -0500 Subject: [PATCH 2/3] Refactored Tabular().Refresh() to better handle partitions. And added Database.Refresh() to ReloadInfo() to recalc model size. --- pytabular/pytabular.py | 118 +++++++++++++++++++++++++++++------------ 1 file changed, 85 insertions(+), 33 deletions(-) diff --git a/pytabular/pytabular.py b/pytabular/pytabular.py index 134d3ce..e38def9 100644 --- a/pytabular/pytabular.py +++ b/pytabular/pytabular.py @@ -9,7 +9,7 @@ from Microsoft.AnalysisServices import UpdateOptions, TraceEventClass, TraceEventSubclass, TraceEventCollection, TraceColumn logger.debug('Importing Other Packages...') -from typing import List, Union, Callable +from typing import Any, Dict, List, Optional, Union, Callable from collections.abc import Iterable from collections import namedtuple import requests as r @@ -64,6 +64,7 @@ def Reload_Model_Info(self) -> bool: self.Columns = [column for table in self.Tables for column in table.Columns.GetEnumerator()] self.Partitions = [partition for table in self.Tables for partition in table.Partitions.GetEnumerator()] self.Measures = [measure for table in self.Tables for measure in table.Measures.GetEnumerator()] + self.Database.Refresh() return True def Disconnect(self) -> bool: '''Disconnects from Model @@ -72,51 +73,102 @@ def Disconnect(self) -> bool: bool: True if successful ''' logger.debug(f'Disconnecting from - {self.Server.Name}') - self.Server.Disconnect() - - if self.Server.Connected: - logger.error(f'Disconnect Unsuccessful') - return False - else: - logger.debug(f'Disconnect Successful') - return True - def Refresh(self, Object:Union[str,Table,Partition,Iterable], RefreshType=RefreshType.Full, Run:bool = True) -> None: - '''Input Object(s) to be refreshed in the tabular model. Combine with .SaveChanges() to actually run the refresh on the model. + return self.Server.Disconnect() + + def Refresh(self, + Object:Union[ + str, Table, Partition, Dict[str, Any], + Iterable[str, Table, Partition, Dict[str, Any]] + ], + RefreshType:RefreshType = RefreshType.Full, + Tracing = False) -> None: + '''Refreshes table(s) and partition(s). Args: - Object (Union[str,Table,Partition,Iterable]): Can be str(table name only), Table object, Partition object, or an iterable combination of the three. - RefreshType (_type_, optional): [RefreshType](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.refreshtype?view=analysisservices-dotnet). Defaults to RefreshType.Full. - ''' + Object (Union[ str, Table, Partition, Dict[str, Any], Iterable[str, Table, Partition, Dict[str, Any]] ]): Designed to handle a few different ways of selecting a refresh. + str == 'Table_Name' + Table == Table Object + Partition == Partition Object + Dict[str, Any] == A way to specify a partition of group of partitions. For ex: {'Table_Name':'Partition1'} or {'Table_Name':['Partition1','Partition2']}. NOTE you can also change out the strings for partition or tables objects. + RefreshType (RefreshType, optional): See [RefreshType](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.refreshtype?view=analysisservices-dotnet). Defaults to RefreshType.Full. + Tracing (bool, optional): Currently just some basic tracing to track refreshes. Defaults to False. + + Raises: + Exception: Raises exception if unable to find table or partition via string. + + + Returns: + WIP: WIP + ''' logger.debug(f'Beginning RequestRefresh cadence...') + def Refresh_Report(Property_Changes): logger.debug(f'Running Refresh Report...') for property_change in Property_Changes: if isinstance(property_change.Object,Partition) and property_change.Property_Name == 'RefreshedTime': logger.info(f'{property_change.Object.Table.Name} - {property_change.Object.Name} Refreshed! - {ticks_to_datetime(property_change.New_Value.Ticks).strftime("%m/%d/%Y, %H:%M:%S")}') return True - def refresh(object): - if isinstance(object,str): - logger.info(f'Requesting refresh for {object}') - table = [table for table in self.Tables if table.Name == object][0] - table.RequestRefresh(RefreshType) + + def refresh_table(table:Table) -> None: + logging.info(f'Requesting refresh for {table.Name}') + table.RequestRefresh(RefreshType) + + def refresh_partition(partition:Partition) -> None: + logging.info(f'Requesting refresh for {partition.Table.Name}|{partition.Name}') + partition.RequestRefresh(RefreshType) + + def refresh_dict(partition_dict:Dict) -> None: + for table in partition_dict.keys(): + table_object = find_table(table) if isinstance(table,str) else table + def handle_partitions(object): + if isinstance(object,str): + refresh_partition(find_partition(table_object, object)) + elif isinstance(object,Partition): + refresh_partition(object) + else: + [handle_partitions(obj) for obj in object] + handle_partitions(partition_dict[table]) + + + def find_table(table_str:str) -> Table: + result = self.Model.Tables.Find(table_str) + if result is None: + raise Exception(f"Unable to find table! from {table_str}") + logging.debug(f'Found table {result.Name}') + return result + + def find_partition(table:Table, partition_str:str) -> Partition: + result = table.Partitions.Find(partition_str) + if result is None: + raise Exception(f"Unable to find partition! {table.Name}|{partition_str}") + logging.debug(f'Found partition {result.Table.Name}|{result.Name}') + return result + + def refresh(Object): + if isinstance(Object,str): + refresh_table(find_table(Object)) + elif isinstance(Object, Dict): + refresh_dict(Object) + elif isinstance(Object, Table): + refresh_table(Object) + elif isinstance(Object, Partition): + refresh_partition(Object) else: - logger.info(f'Requesting refresh for {object.Name}') - object.RequestRefresh(RefreshType) - - - if isinstance(Object,Iterable) and isinstance(Object,str) == False: - [refresh(object) for object in Object] - else: - refresh(Object) - - if Run: + [refresh(object) for object in Object] + refresh(Object) + if Tracing: rt = Refresh_Trace(self) rt.Start() - m = self.SaveChanges() + + m = self.SaveChanges() + + if Tracing: rt.Stop() rt.Drop() - Refresh_Report(m.Property_Changes) - return m.Property_Changes + + Refresh_Report(m.Property_Changes) + + return m def Update(self, UpdateOptions:UpdateOptions =UpdateOptions.ExpandFull) -> None: '''[Update Model](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.majorobject.update?view=analysisservices-dotnet#microsoft-analysisservices-majorobject-update(microsoft-analysisservices-updateoptions)) @@ -137,7 +189,7 @@ def property_changes(Property_Changes): logger.info(f'Executing SaveChanges()...') Model_Save_Results = self.Model.SaveChanges() if isinstance(Model_Save_Results.Impact, type(None)): - logger.warning(f'No changes detected on save for {self.Model.Name}') + logger.warning(f'No changes detected on save for {self.Server.Name}') return None else: Property_Changes = Model_Save_Results.Impact.PropertyChanges From cb5ab69b3942dd27d96be31a3aa0ea505d28710f Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 25 Aug 2022 18:15:06 -0500 Subject: [PATCH 3/3] .70 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8d9bc20..2483fd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "python_tabular" -version = "0.0.60" +version = "0.0.70" authors = [ { name="Curtis Stallings", email="curtisrstallings@gmail.com" }, ]