diff --git a/README.md b/README.md index c41e44e..615809a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ### What is it? -PyTabular is a python package that allows for programmatic execution on your tabular models! This is possible thanks to [Pythonnet](https://pythonnet.github.io/) and Microsoft's [.Net APIs on Azure Analysis Services](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices?view=analysisservices-dotnet). The package should have the dll files included when you import it. +PyTabular is a python package that allows for programmatic execution on your tabular models! This is possible thanks to [Pythonnet](https://pythonnet.github.io/) and Microsoft's [.Net APIs on Azure Analysis Services](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices?view=analysisservices-dotnet). The package should have the dll files included when you import it. See [Documentation Here](https://curts0.github.io/PyTabular/) ### Getting Started @@ -28,17 +28,17 @@ DAX Query # Returns a Pandas DataFrame ``` -Refresh Tables and Partitions +[Refresh Tables and Partitions](https://curts0.github.io/PyTabular/Tabular/#refresh) ```python - #filter down the collection to what you want to refresh - tables_to_refresh = [table for table in model.Tables if table.Name in ['Table1','Table2','Table3']] - + #Can be str(table name only), Table object, Partition object, or an iterable combination of the three. + model.Refresh('Table Name') + tables_to_refresh = ['Table Name 1', 'Table Name 2', , ] #Queue up the tables and partitions that you want to refresh. model.Refresh(tables_to_refresh) #Once you are ready, update to execute the refresh - model.Update() + model.SaveChanges() ``` Built In Dax Query Helpers @@ -71,6 +71,4 @@ Run BPA from TE2 TE2 = pytabular.TE2() #Feel free to input your TE2 File path or this will download for you. BPA = pytabular.BPA() #Fee free to input your own BPA file or this will download for you from: https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json model.Analyze_BPA(TE2.EXE_Path,BPA.Location) #This will output a list of BPA violations... -``` - -I'm working on converting everything to the google docstring format and using those to populate the documentation. I still have some formatting issues to work through, but that should help with understanding what this package can do. \ No newline at end of file +``` \ No newline at end of file diff --git a/dist/python_tabular-0.0.20.tar.gz b/dist/python_tabular-0.0.20.tar.gz deleted file mode 100644 index 3c90be1..0000000 Binary files a/dist/python_tabular-0.0.20.tar.gz and /dev/null differ diff --git a/dist/python_tabular-0.0.20-py3-none-any.whl b/dist/python_tabular-0.0.30-py3-none-any.whl similarity index 99% rename from dist/python_tabular-0.0.20-py3-none-any.whl rename to dist/python_tabular-0.0.30-py3-none-any.whl index dc43e2e..0fdb60c 100644 Binary files a/dist/python_tabular-0.0.20-py3-none-any.whl and b/dist/python_tabular-0.0.30-py3-none-any.whl differ diff --git a/dist/python_tabular-0.0.30.tar.gz b/dist/python_tabular-0.0.30.tar.gz new file mode 100644 index 0000000..b14b3a5 Binary files /dev/null and b/dist/python_tabular-0.0.30.tar.gz differ diff --git a/docs/BPA.md b/docs/BPA.md index 73c02ff..272eff0 100644 --- a/docs/BPA.md +++ b/docs/BPA.md @@ -2,7 +2,7 @@ ## BPA -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L374) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L393) ```python BPA( rules_location: str = 'https: //raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json' @@ -11,7 +11,5 @@ BPA( --- -Best Practice Analyzer Class -Can provide Url, Json File Path, or Python List. -If nothing is provided it will default to Microsofts Analysis Services report with BPA Rules. -https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json +_summary_ + diff --git a/docs/Examples.md b/docs/Examples.md index 6aa41c6..fe076d8 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -51,3 +51,30 @@ Example to add to model model.Create_Table(p.Table_Last_Refresh_Times(model),'Re * **DataFrame** : pd dataframe with the RefreshedTime property: https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.partition.refreshedtime?view=analysisservices-dotnet#microsoft-analysisservices-tabular-partition-refreshedtime If group_partition == True and the table has multiple partitions, then df.groupby(by["tables"]).max() + +---- + + +### BPA_Violations_To_DF +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L50) +```python +.BPA_Violations_To_DF( + model: pytabular.Tabular, te2: str, bpa: str +) +``` + +--- +Runs BPA Analyzer from TE2 and outputs result into a DF. + + +**Args** + +* **model** (pytabular.Tabular) : Tabular Model Class +* **te2** (str) : TE2 Exe File Path (Can use TE2().EXE_path) +* **bpa** (str) : BPA File Location (Can use BPA().Location) + + +**Returns** + +* **DataFrame** : Super simple right now. Just splits into two columns.. The object in violation and the rule. + diff --git a/docs/TE2.md b/docs/TE2.md index 28fd238..ca9b27f 100644 --- a/docs/TE2.md +++ b/docs/TE2.md @@ -2,7 +2,7 @@ ## TE2 -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L399) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L417) ```python TE2( TE_Location = 'https: //github.com/TabularEditor/TabularEditor/releases/download/2.16.7/TabularEditor.Portable.zip' @@ -11,7 +11,6 @@ TE2( --- -TE2 Class, to use any built TabularEditor Command Line Scripts -https://docs.tabulareditor.com/te2/Command-line-Options.html -#https://github.com/TabularEditor/TabularEditor/releases/download/2.16.7/TabularEditor.Portable.zip -#https://cdn.tabulareditor.com/files/TabularEditor.2.16.7.zip +TE2 Class, to use any built TabularEditor Command Line Scripts +[TE2 Command Line Example](https://docs.tabulareditor.com/te2/Command-line-Options.html) +[TE2 Download](https://github.com/TabularEditor/TabularEditor/releases/download/2.16.7/TabularEditor.Portable.zip) diff --git a/docs/Tabular.md b/docs/Tabular.md index 1edaf25..c4fee9f 100644 --- a/docs/Tabular.md +++ b/docs/Tabular.md @@ -2,7 +2,7 @@ ## Tabular -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L20) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L21) ```python Tabular( CONNECTION_STR: str @@ -23,8 +23,23 @@ Tabular Class to perform operations:[Microsoft.AnalysisServices.Tabular](https:/ **Methods:** +### .Reload_Model_Info +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L53) +```python +.Reload_Model_Info() +``` + +--- +Runs on __init__ iterates through details, can be called after any model changes. Called in SaveChanges() + + +**Returns** + +* **bool** : True if successful + + ### .Disconnect -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L55) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L64) ```python .Disconnect() ``` @@ -39,27 +54,25 @@ Disconnects from Model ### .Refresh -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L70) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L79) ```python .Refresh( - iterable_items: List, RefreshType = RefreshType.Full + Object: Union[str, Table, Partition, Iterable], RefreshType = RefreshType.Full ) ``` --- -Input iterable Collections for the function to run through. -It will add the collection items into a Refresh Request. -To execute refresh run through Update() +Input Object(s) to be refreshed in the tabular model. Combine with .SaveChanges() to actually run the refresh on the model. **Args** -* **iterable_items** (List) : Must be refreshable Tabular objects. -* **RefreshType** (_type_, optional) : _description_. Defaults to RefreshType.Full. +* **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. ### .Update -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L82) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L100) ```python .Update( UpdateOptions: UpdateOptions = UpdateOptions.ExpandFull @@ -81,7 +94,7 @@ To execute refresh run through Update() ### .SaveChanges -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L93) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L111) ```python .SaveChanges() ``` @@ -96,7 +109,7 @@ Just a simple wrapper to call self.Model.SaveChanges() bool: ### .Backup_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L102) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L121) ```python .Backup_Table( table_str: str @@ -120,7 +133,7 @@ Refresh is performed from source during backup. ### .Revert_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L169) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L188) ```python .Revert_Table( table_str: str @@ -148,7 +161,7 @@ Example scenario -> ### .Query -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L237) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L256) ```python .Query( Query_Str: str @@ -170,7 +183,7 @@ Executes Query on Model and Returns Results in Pandas DataFrame ### .Query_Every_Column -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L267) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L286) ```python .Query_Every_Column( query_function: str = 'COUNTROWS(VALUES(_))' @@ -193,7 +206,7 @@ This will dynamically create a query to pull all columns from the model and run ### .Query_Every_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L289) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L308) ```python .Query_Every_Table( query_function: str = 'COUNTROWS(_)' @@ -216,7 +229,7 @@ It will replace the _ with the table to run. ### .Analyze_BPA -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L309) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L328) ```python .Analyze_BPA( Tabular_Editor_Exe: str, Best_Practice_Analyzer: str @@ -241,7 +254,7 @@ https://docs.tabulareditor.com/te2/Command-line-Options.html ### .Create_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L333) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L352) ```python .Create_Table( df: pd.DataFrame, table_name: str diff --git a/docs/Utils.md b/docs/Utils.md index 71342d9..1fdd606 100644 --- a/docs/Utils.md +++ b/docs/Utils.md @@ -47,3 +47,45 @@ WiP takes dataframe columns and gets respective tabular column datatype. ([NumP * **Dict** : EX {'col1': , 'col2': , 'col3': } + +---- + + +### pd_dataframe_to_m_expression +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L75) +```python +.pd_dataframe_to_m_expression( + df: pd.DataFrame +) +``` + +--- +This will take a pandas dataframe and convert to an m expression +For example this DF: + col1 col2 +0 1 3 +1 2 4 + +| +| +V + +Will convert to this expression string: +let +Source=#table({"col1","col2"}, +{ +{"1","3"},{"2","4"} +}) +in +Source + + +**Args** + +* **df** (pd.DataFrame) : Pandas DataFrame + + +**Returns** + +* **str** : Currently only returning string values in your tabular model. + diff --git a/docs/index.md b/docs/index.md index c41e44e..615809a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,7 +6,7 @@ ### What is it? -PyTabular is a python package that allows for programmatic execution on your tabular models! This is possible thanks to [Pythonnet](https://pythonnet.github.io/) and Microsoft's [.Net APIs on Azure Analysis Services](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices?view=analysisservices-dotnet). The package should have the dll files included when you import it. +PyTabular is a python package that allows for programmatic execution on your tabular models! This is possible thanks to [Pythonnet](https://pythonnet.github.io/) and Microsoft's [.Net APIs on Azure Analysis Services](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices?view=analysisservices-dotnet). The package should have the dll files included when you import it. See [Documentation Here](https://curts0.github.io/PyTabular/) ### Getting Started @@ -28,17 +28,17 @@ DAX Query # Returns a Pandas DataFrame ``` -Refresh Tables and Partitions +[Refresh Tables and Partitions](https://curts0.github.io/PyTabular/Tabular/#refresh) ```python - #filter down the collection to what you want to refresh - tables_to_refresh = [table for table in model.Tables if table.Name in ['Table1','Table2','Table3']] - + #Can be str(table name only), Table object, Partition object, or an iterable combination of the three. + model.Refresh('Table Name') + tables_to_refresh = ['Table Name 1', 'Table Name 2',
, ] #Queue up the tables and partitions that you want to refresh. model.Refresh(tables_to_refresh) #Once you are ready, update to execute the refresh - model.Update() + model.SaveChanges() ``` Built In Dax Query Helpers @@ -71,6 +71,4 @@ Run BPA from TE2 TE2 = pytabular.TE2() #Feel free to input your TE2 File path or this will download for you. BPA = pytabular.BPA() #Fee free to input your own BPA file or this will download for you from: https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json model.Analyze_BPA(TE2.EXE_Path,BPA.Location) #This will output a list of BPA violations... -``` - -I'm working on converting everything to the google docstring format and using those to populate the documentation. I still have some formatting issues to work through, but that should help with understanding what this package can do. \ No newline at end of file +``` \ No newline at end of file diff --git a/mkgendocs.yml b/mkgendocs.yml index 65be15d..1296e2d 100644 --- a/mkgendocs.yml +++ b/mkgendocs.yml @@ -22,8 +22,10 @@ pages: functions: - Return_Zero_Row_Tables - Table_Last_Refresh_Times + - BPA_Violations_To_DF - page: "Utils.md" source: 'pytabular/logic_utils.py' functions: - ticks_to_datetime - - pandas_datatype_to_tabular_datatype \ No newline at end of file + - pandas_datatype_to_tabular_datatype + - pd_dataframe_to_m_expression \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 253fbe4..d5e66ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "python_tabular" -version = "0.0.20" +version = "0.0.30" authors = [ { name="Curtis Stallings", email="curtisrstallings@gmail.com" }, ] diff --git a/pytabular/basic_checks.py b/pytabular/basic_checks.py index 399a18f..e263045 100644 --- a/pytabular/basic_checks.py +++ b/pytabular/basic_checks.py @@ -48,6 +48,16 @@ def Table_Last_Refresh_Times(model:pytabular.Tabular, group_partition:bool = Tru return df def BPA_Violations_To_DF(model:pytabular.Tabular,te2:str, bpa:str) -> pd.DataFrame: + '''Runs BPA Analyzer from TE2 and outputs result into a DF. + + Args: + model (pytabular.Tabular): Tabular Model Class + te2 (str): TE2 Exe File Path (Can use TE2().EXE_path) + bpa (str): BPA File Location (Can use BPA().Location) + + Returns: + pd.DataFrame: Super simple right now. Just splits into two columns.. The object in violation and the rule. + ''' results = model.Analyze_BPA(te2,bpa) data = [rule.replace(' violates rule ','^').replace('\"','').split('^') for rule in results] columns = ["Object","Violation"] diff --git a/pytabular/logic_utils.py b/pytabular/logic_utils.py index b5e37a5..ec89454 100644 --- a/pytabular/logic_utils.py +++ b/pytabular/logic_utils.py @@ -73,25 +73,30 @@ def dax_tableconstructor_rows_expression_generator(list_of_strings: list[str]) - return return True def pd_dataframe_to_m_expression(df:pd.DataFrame) -> str: - ''' - This will take a pandas dataframe and convert to an m expression - For example this DF: - col1 col2 - 0 1 3 - 1 2 4 - - | - | - V - - Will convert to this expression string: - let - Source=#table({"col1","col2"}, - { - {"1","3"},{"2","4"} - }) - in + '''This will take a pandas dataframe and convert to an m expression + For example this DF: + col1 col2 + 0 1 3 + 1 2 4 + + | + | + V + + Will convert to this expression string: + let + Source=#table({"col1","col2"}, + { + {"1","3"},{"2","4"} + }) + in Source + + Args: + df (pd.DataFrame): Pandas DataFrame + + Returns: + str: Currently only returning string values in your tabular model. ''' def m_list_expression_generator(list_of_strings:List[str]) -> str: ''' diff --git a/pytabular/pytabular.py b/pytabular/pytabular.py index 32de6ce..dd2e6e5 100644 --- a/pytabular/pytabular.py +++ b/pytabular/pytabular.py @@ -8,7 +8,8 @@ from Microsoft.AnalysisServices import UpdateOptions logging.debug('Importing Other Packages...') -from typing import List +from typing import List, Union +from collections.abc import Iterable import requests as r import pandas as pd import json @@ -41,10 +42,7 @@ def __init__(self,CONNECTION_STR:str): logging.debug(f'Connected to Model - {self.Model.Name}') self.DaxConnection = AdomdConnection() self.DaxConnection.ConnectionString = f"{self.Server.ConnectionString}Password='{self.Server.ConnectionInfo.Password}'" - self.Tables = [table for table in self.Model.Tables.GetEnumerator()] - 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.Reload_Model_Info() logging.debug(f'Class Initialization Completed') logging.debug(f'Registering Disconnect on Termination...') atexit.register(self.Disconnect) @@ -52,6 +50,17 @@ def __init__(self,CONNECTION_STR:str): pass def __repr__(self) -> str: return f'{self.Server.Name}::{self.Database.Name}::{self.Model.Name}\n{self.Database.EstimatedSize} Estimated Size\n{len(self.Tables)} Tables\n{len(self.Columns)} Columns\n{len(self.Partitions)} Partitions\n{len(self.Measures)} Measures' + def Reload_Model_Info(self) -> bool: + '''Runs on __init__ iterates through details, can be called after any model changes. Called in SaveChanges() + + Returns: + bool: True if successful + ''' + self.Tables = [table for table in self.Model.Tables.GetEnumerator()] + 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()] + return True def Disconnect(self) -> bool: '''Disconnects from Model @@ -67,18 +76,27 @@ def Disconnect(self) -> bool: else: logging.debug(f'Disconnect Successful') return True - def Refresh(self, iterable_items: List, RefreshType=RefreshType.Full) -> None: - '''Input iterable Collections for the function to run through. - It will add the collection items into a Refresh Request. - To execute refresh run through Update() + def Refresh(self, Object:Union[str,Table,Partition,Iterable], RefreshType=RefreshType.Full) -> None: + '''Input Object(s) to be refreshed in the tabular model. Combine with .SaveChanges() to actually run the refresh on the model. Args: - iterable_items (List): Must be refreshable Tabular objects. - RefreshType (_type_, optional): _description_. Defaults to RefreshType.Full. - ''' - for collection in iterable_items: - logging.debug(f'Adding {collection.Name} to Refresh Request') - collection.RequestRefresh(RefreshType) + 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. + ''' + logging.debug(f'Beginning RequestRefresh cadence...') + def refresh(object): + if isinstance(object,str): + logging.info(f'Requesting refresh for {object}') + table = [table for table in self.Tables if table.Name == object][0] + table.RequestRefresh(RefreshType) + else: + logging.info(f'Requesting refresh for {object.Name}') + object.RequestRefresh(RefreshType) + if isinstance(Object,Iterable): + [refresh(object) for object in Object] + else: + refresh(Object) + Object.RequestRefresh(RefreshType) 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)) @@ -96,7 +114,8 @@ def SaveChanges(self) -> bool: Returns: bool: - ''' + ''' + self.Reload_Model_Info() self.Model.SaveChanges() return True def Backup_Table(self,table_str:str) -> bool: @@ -372,11 +391,10 @@ def Create_Table(self,df:pd.DataFrame, table_name:str) -> bool: class BPA: - ''' - Best Practice Analyzer Class - Can provide Url, Json File Path, or Python List. - If nothing is provided it will default to Microsofts Analysis Services report with BPA Rules. - https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json + '''_summary_ + ''' + '''Best Practice Analyzer Class. Can provide Url, Json File Path, or Python List. If nothing is provided it will default to Microsofts Analysis Services report with BPA Rules. + [Default BPA](https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json) ''' def __init__(self,rules_location:str='https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json') -> None: ''' @@ -397,11 +415,9 @@ def __init__(self,rules_location:str='https://raw.githubusercontent.com/microsof pass #Todo... subclass with a namedtuple class TE2: - ''' - TE2 Class, to use any built TabularEditor Command Line Scripts - https://docs.tabulareditor.com/te2/Command-line-Options.html - #https://github.com/TabularEditor/TabularEditor/releases/download/2.16.7/TabularEditor.Portable.zip - #https://cdn.tabulareditor.com/files/TabularEditor.2.16.7.zip + '''TE2 Class, to use any built TabularEditor Command Line Scripts + [TE2 Command Line Example](https://docs.tabulareditor.com/te2/Command-line-Options.html) + [TE2 Download](https://github.com/TabularEditor/TabularEditor/releases/download/2.16.7/TabularEditor.Portable.zip) ''' def __init__(self,TE_Location='https://github.com/TabularEditor/TabularEditor/releases/download/2.16.7/TabularEditor.Portable.zip') -> None: logging.debug(f'Checking for TE2 in {os.getcwd()}') diff --git a/test/test_tabular.py b/test/test_tabular.py index 33b0289..afd72a6 100644 --- a/test/test_tabular.py +++ b/test/test_tabular.py @@ -6,9 +6,10 @@ aas = pytabular.Tabular(localsecret.CONNECTION_STR['FIN 500']) gen2 = pytabular.Tabular(localsecret.CONNECTION_STR['GEN2TEST']) +testing_parameters = [(aas),(gen2)] testingtable = 'PyTestTable' -pytestmark = pytest.mark.parametrize("model",[(aas),(gen2)]) +@pytest.mark.parametrize("model",testing_parameters) def test_connection(model): ''' Does a quick check to the Tabular Class @@ -16,39 +17,49 @@ def test_connection(model): ''' assert model.Server.Connected +@pytest.mark.parametrize("model",testing_parameters) def test_database(model): assert isinstance(model.Database,Database) +@pytest.mark.parametrize("model",testing_parameters) def test_query(model): - ''' - Does a quick query on the model and checks if it will return expected value - ''' df = model.Query('EVALUATE {1}') assert df.iloc[0]['[Value]'] == 1 -def remove_py_tables(model): - table_check = [table for table in model.Tables if testingtable in table.Name] + +def remove_testing_table(model): + table_check = [table for table in model.Model.Tables.GetEnumerator() if testingtable in table.Name] for table in table_check: model.Model.Tables.Remove(table) - model.Model.SaveChanges() - return True + model.SaveChanges() +@pytest.mark.parametrize("model",testing_parameters) def test_pre_table_checks(model): - assert remove_py_tables(model) == True + remove_testing_table(model) + assert len([table for table in model.Model.Tables.GetEnumerator() if testingtable in table.Name]) == 0 +@pytest.mark.parametrize("model",testing_parameters) def test_create_table(model): df = pd.DataFrame(data={'col1':[1,2,3],'col2':['four','five','six']}) - assert model.Create_Table(df,testingtable) == True + assert model.Create_Table(df,testingtable) +@pytest.mark.parametrize("model",testing_parameters) def test_backingup_table(model): - assert model.Backup_Table(testingtable) == True + model.Backup_Table(testingtable) + assert len([table for table in model.Model.Tables.GetEnumerator() if f'{testingtable}_backup' == table.Name]) == 1 +@pytest.mark.parametrize("model",testing_parameters) def test_revert_table(model): - assert model.Revert_Table(testingtable) == True + model.Revert_Table(testingtable) + assert len([table for table in model.Model.Tables.GetEnumerator() if f'{testingtable}' == table.Name]) == 1 + +@pytest.mark.parametrize("model",testing_parameters) def test_table_removal(model): - assert remove_py_tables(model) == True + remove_testing_table(model) + assert len([table for table in model.Model.Tables.GetEnumerator() if testingtable in table.Name]) == 0 +@pytest.mark.parametrize("model",testing_parameters) def test_bpa(model): te2 = pytabular.TE2().EXE_Path bpa = pytabular.BPA().Location