From 0d10d3c6397fd9b27c80b3ebccaa25e05ff1ffeb Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 2 Feb 2023 15:58:04 -0600 Subject: [PATCH 1/8] cleanup docs now github action --- docs/Best Practice Analyzer.md | 42 ----- docs/Column.md | 47 ----- docs/Examples.md | 113 ------------ docs/Logic Utils.md | 137 -------------- docs/Partition.md | 70 -------- docs/Queries.md | 49 ----- docs/Table.md | 81 --------- docs/Tabular Editor 2.md | 42 ----- docs/Tabular.md | 316 --------------------------------- docs/Traces.md | 166 ----------------- docs/contributing.md | 9 - docs/index.md | 137 -------------- 12 files changed, 1209 deletions(-) delete mode 100644 docs/Best Practice Analyzer.md delete mode 100644 docs/Column.md delete mode 100644 docs/Examples.md delete mode 100644 docs/Logic Utils.md delete mode 100644 docs/Partition.md delete mode 100644 docs/Queries.md delete mode 100644 docs/Table.md delete mode 100644 docs/Tabular Editor 2.md delete mode 100644 docs/Tabular.md delete mode 100644 docs/Traces.md delete mode 100644 docs/contributing.md delete mode 100644 docs/index.md diff --git a/docs/Best Practice Analyzer.md b/docs/Best Practice Analyzer.md deleted file mode 100644 index 2f59ff2..0000000 --- a/docs/Best Practice Analyzer.md +++ /dev/null @@ -1,42 +0,0 @@ -# - - -## BPA -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/best_practice_analyzer.py\#L41) -```python -BPA( - File_Path: str = 'Default' -) -``` - - ---- -Setting BPA Class for future work... - ----- - - -### Download_BPA_File -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/best_practice_analyzer.py\#L12) -```python -.Download_BPA_File( - Download_Location: str = 'https: //raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json', - Folder: str = 'Best_Practice_Analyzer', Auto_Remove = True -) -``` - ---- -Runs a request.get() to retrieve the json file from web. Will return and store in directory. Will also register the removal of the new directory and file when exiting program. - - -**Args** - -* **Download_Location** (_type_, optional) : F. Defaults to [Microsoft GitHub BPA]'https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json'. -* **Folder** (str, optional) : New Folder String. Defaults to 'Best_Practice_Analyzer'. -* **Auto_Remove** (bool, optional) : If you wish to Auto Remove when script exits. Defaults to True. - - -**Returns** - -* **str** : File Path for the newly downloaded BPA. - diff --git a/docs/Column.md b/docs/Column.md deleted file mode 100644 index f23c8f3..0000000 --- a/docs/Column.md +++ /dev/null @@ -1,47 +0,0 @@ -# - - -## PyColumn -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/column.py\#L11) -```python -PyColumn( - object, table -) -``` - - ---- -Wrapper for [Microsoft.AnalysisServices.Tabular.Column](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.column?view=analysisservices-dotnet). -With a few other bells and whistles added to it. WIP - - -**Args** - -* **Table** : Parent Table to the Column - - - -**Methods:** - - -### .Distinct_Count -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/column.py\#L23) -```python -.Distinct_Count( - No_Blank = False -) -``` - - ----- - - -## PyColumns -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/column.py\#L32) -```python -PyColumns( - objects -) -``` - - diff --git a/docs/Examples.md b/docs/Examples.md deleted file mode 100644 index 2dc7a32..0000000 --- a/docs/Examples.md +++ /dev/null @@ -1,113 +0,0 @@ -# - - -### Return_Zero_Row_Tables -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L11) -```python -.Return_Zero_Row_Tables( - model: pytabular.Tabular -) -``` - ---- -Returns list of table names of those that are returning isna() - - -**Args** - -* **model** (pytabular.Tabular) : Tabular Model - - -**Returns** - -* List of table names where DAX COUNTROWS('Table Name') is nan or 0. - - ----- - - -### Table_Last_Refresh_Times -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L26) -```python -.Table_Last_Refresh_Times( - model: pytabular.Tabular, group_partition: bool = True -) -``` - ---- -Returns pd.DataFrame of tables with their latest refresh time. -Optional 'group_partition' variable, default is True. -If False an extra column will be include to have the last refresh time to the grain of the partition -Example to add to model model.Create_Table(p.Table_Last_Refresh_Times(model),'RefreshTimes') - - -**Args** - -* **model** (pytabular.Tabular) : Tabular Model -* **group_partition** (bool, optional) : Whether or not you want the grain of the dataframe to be by table or by partition. Defaults to True. - - -**Returns** - -* **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\#L67) -```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. - - ----- - - -### Last_X_Interval -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L87) -```python -.Last_X_Interval( - Model: pytabular.Tabular, Measure: Union[str, pytabular.pytabular.Measure], - Column_Name: Union[str, None] = None, - Date_Column_Identifier: str = "'Date'[DATE_DTE_KEY]", - Number_Of_Intervals: int = 90, Interval: str = 'DAY' -) -``` - ---- -Pulls the Last X Interval (Ex Last 90 Days) of a specific measure. - - -**Args** - -* **Model** (pytabular.Tabular) : Tabular Model to perform query on. -* **Measure** (Union[str,pytabular.pytabular.Measure]) : Measure to query. If string, will first check for a measure in the model with that name, otherwise will assume it is a DAX Expression (Ex SUM(FactTable[ColumnValue]) ) and perform that as expression -* **Column_Name** (Union[str,None], optional) : Column Name to be outputted in DataFrame. You can provide your own otherwise will take from the Measure Name. Defaults to "Result". -* **Date_Column_Identifier** (str, optional) : Date column dax identifier. Defaults to "'Date'[DATE_DTE_KEY]". -* **Number_Of_Intervals** (int, optional) : This is used to plug in the variables for [DATESINPERIOD](https://docs.microsoft.com/en-us/dax/datesinperiod-function-dax). Defaults to 90. -* **Interval** (str, optional) : Sames as Number_Of_Intervals. Used to plug in parameters of DAX function [DATESINPERIOD](https://docs.microsoft.com/en-us/dax/datesinperiod-function-dax). Defaults to "DAY". Possible options are "DAY", "MONTH", "QUARTER", and "YEAR" - - -**Returns** - -* **DataFrame** : Pandas DataFrame of results. - diff --git a/docs/Logic Utils.md b/docs/Logic Utils.md deleted file mode 100644 index a02a9dc..0000000 --- a/docs/Logic Utils.md +++ /dev/null @@ -1,137 +0,0 @@ -# - - -### ticks_to_datetime -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L11) -```python -.ticks_to_datetime( - ticks: int -) -``` - ---- -Converts a C# System DateTime Tick into a Python DateTime - - -**Args** - -* **ticks** (int) : [C# DateTime Tick](https://docs.microsoft.com/en-us/dotnet/api/system.datetime.ticks?view=net-6.0) - - -**Returns** - -* **datetime** : [datetime.datetime](https://docs.python.org/3/library/datetime.html) - - ----- - - -### pandas_datatype_to_tabular_datatype -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L23) -```python -.pandas_datatype_to_tabular_datatype( - df: pd.DataFrame -) -``` - ---- -WiP takes dataframe columns and gets respective tabular column datatype. ([NumPy Datatypes](https://numpy.org/doc/stable/reference/generated/numpy.dtype.kind.html) and [Tabular Datatypes](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.datatype?view=analysisservices-dotnet)) - - -**Args** - -* **df** (pd.DataFrame) : Pandas DataFrame - - -**Returns** - -* **Dict** : EX {'col1': , 'col2': , 'col3': } - - ----- - - -### pd_dataframe_to_m_expression -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L89) -```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. - - ----- - - -### remove_folder_and_contents -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L140) -```python -.remove_folder_and_contents( - folder_location -) -``` - ---- -Internal used in tabular_editor.py and best_practice_analyzer.py. - - -**Args** - -* **folder_location** (str) : Folder path to remove directory and contents. - - ----- - - -### remove_suffix -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L153) -```python -.remove_suffix( - input_string, suffix -) -``` - ---- -Adding for >3.9 compatiblity. [Stackoverflow Answer](https://stackoverflow.com/questions/66683630/removesuffix-returns-error-str-object-has-no-attribute-removesuffix) - - -**Args** - -* **input_string** (str) : input string to remove suffix from -* **suffix** (str) : suffix to be removed - - -**Returns** - -* **str** : input_str with suffix removed - diff --git a/docs/Partition.md b/docs/Partition.md deleted file mode 100644 index dd9cfa8..0000000 --- a/docs/Partition.md +++ /dev/null @@ -1,70 +0,0 @@ -# - - -## PyPartition -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/partition.py\#L11) -```python -PyPartition( - object, table -) -``` - - ---- -Wrapper for [Microsoft.AnalysisServices.Partition](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.partition?view=analysisservices-dotnet). -With a few other bells and whistles added to it. WIP - - -**Args** - -* **Table** : Parent Table to the Column - - - -**Methods:** - - -### .Last_Refresh -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/partition.py\#L23) -```python -.Last_Refresh() -``` - ---- -Queries `RefreshedTime` attribute in the partition and converts from C# Ticks to Python datetime - - -**Returns** - -* **datetime** : Last Refreshed time of Partition in datetime format - - -### .Refresh -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/partition.py\#L31) -```python -.Refresh( - *args, **kwargs -) -``` - ---- -Same method from Model Refresh, you can pass through any extra parameters. For example: -`Tabular().Tables['Table Name'].Partitions[0].Refresh(Tracing = True)` - -**Returns** - -* **DataFrame** : Returns pandas dataframe with some refresh details - - ----- - - -## PyPartitions -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/partition.py\#L40) -```python -PyPartitions( - objects -) -``` - - diff --git a/docs/Queries.md b/docs/Queries.md deleted file mode 100644 index e7620d0..0000000 --- a/docs/Queries.md +++ /dev/null @@ -1,49 +0,0 @@ -# - - -## Connection -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/query.py\#L11) -```python -Connection( - Server -) -``` - - ---- -Subclass for [Adomdclient](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.adomdclient?view=analysisservices-dotnet). With some extra items on top. -Right now designed for internal use. For example, Query method in the Tabular class is just a wrapper for this class' Query method... So use that instead. - - -**Args** - -* **AdomdConnection** (_type_) : _description_ - - - -**Methods:** - - -### .Query -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/query.py\#L25) -```python -.Query( - Query_Str: str -) -``` - ---- -Executes Query on Model and Returns Results in Pandas DataFrame - - -**Args** - -* **Query_Str** (str) : Dax Query. Note, needs full syntax (ex: EVALUATE). See (DAX Queries)[https://docs.microsoft.com/en-us/dax/dax-queries]. -Will check if query string is a file. If it is, then it will perform a query on whatever is read from the file. -It is also possible to query DMV. For example. Query("select * from $SYSTEM.DISCOVER_TRACE_EVENT_CATEGORIES"). See (DMVs)[https://docs.microsoft.com/en-us/analysis-services/instances/use-dynamic-management-views-dmvs-to-monitor-analysis-services?view=asallproducts-allversions] - - -**Returns** - -* **DataFrame** : Returns dataframe with results - diff --git a/docs/Table.md b/docs/Table.md deleted file mode 100644 index ac7f627..0000000 --- a/docs/Table.md +++ /dev/null @@ -1,81 +0,0 @@ -# - - -## PyTable -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/table.py\#L13) -```python -PyTable( - object, model -) -``` - - ---- -Wrapper for [Microsoft.AnalysisServices.Tabular.Table](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.table?view=analysisservices-dotnet). -With a few other bells and whistles added to it. You can use the table to access the nested Columns and Partitions. WIP - - -**Attributes** - -* **Model** : Reference to Tabular class -* **Partitions** : Reference to Table Partitions -* **Columns** : Reference to Table Columns - - - -**Methods:** - - -### .Row_Count -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/table.py\#L36) -```python -.Row_Count() -``` - ---- -Method to return count of rows. Simple Dax Query: -`EVALUATE {COUNTROWS('Table Name')}` - - -**Returns** - -* **int** : Number of rows using [COUNTROWS](https://learn.microsoft.com/en-us/dax/countrows-function-dax). - - -### .Refresh -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/table.py\#L45) -```python -.Refresh( - *args, **kwargs -) -``` - ---- -Same method from Model Refresh, you can pass through any extra parameters. For example: -`Tabular().Tables['Table Name'].Refresh(Tracing = True)` - -**Returns** - -* **DataFrame** : Returns pandas dataframe with some refresh details - - ----- - - -## PyTables -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/table.py\#L54) -```python -PyTables( - objects -) -``` - - ---- -Iterator to handle tables. Accessible via `Tables` attribute in Tabular class. - - -**Args** - -* **PyTable** : PyTable class - diff --git a/docs/Tabular Editor 2.md b/docs/Tabular Editor 2.md deleted file mode 100644 index cd681d9..0000000 --- a/docs/Tabular Editor 2.md +++ /dev/null @@ -1,42 +0,0 @@ -# - - -## Tabular_Editor -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_editor.py\#L44) -```python -Tabular_Editor( - EXE_File_Path: str = 'Default' -) -``` - - ---- -Setting Tabular_Editor Class for future work. - ----- - - -### Download_Tabular_Editor -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_editor.py\#L11) -```python -.Download_Tabular_Editor( - Download_Location: str = 'https: //github.com/TabularEditor/TabularEditor/releases/download/2.16.7/TabularEditor.Portable.zip', - Folder: str = 'Tabular_Editor_2', Auto_Remove = True -) -``` - ---- -Runs a request.get() to retrieve the zip file from web. Will unzip response and store in directory. Will also register the removal of the new directory and files when exiting program. - - -**Args** - -* **Download_Location** (str, optional) : File path for zip of Tabular Editor 2. Defaults to [Tabular Editor 2 Github Zip Location]'https://github.com/TabularEditor/TabularEditor/releases/download/2.16.7/TabularEditor.Portable.zip'. -* **Folder** (str, optional) : New Folder Location. Defaults to 'Tabular_Editor_2'. -* **Auto_Remove** (bool, optional) : Boolean to determine auto removal of files once script exits. Defaults to True. - - -**Returns** - -* **str** : File path of TabularEditor.exe - diff --git a/docs/Tabular.md b/docs/Tabular.md deleted file mode 100644 index e40dfe9..0000000 --- a/docs/Tabular.md +++ /dev/null @@ -1,316 +0,0 @@ -# - - -## Tabular -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L34) -```python -Tabular( - CONNECTION_STR: str -) -``` - - ---- -Tabular Class to perform operations: [Microsoft.AnalysisServices.Tabular](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular?view=analysisservices-dotnet). You can use this class as your main way to interact with your model. - - -**Args** - -* **CONNECTION_STR** (str) : Valid [Connection String](https://docs.microsoft.com/en-us/analysis-services/instances/connection-string-properties-analysis-services?view=asallproducts-allversions) for connecting to a Tabular Model. - - -**Attributes** - -* **Server** (Server) : See [Server MS Docs](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.server?view=analysisservices-dotnet). -* **Catalog** (str) : Name of Database. See [Catalog MS Docs](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.connectioninfo.catalog?view=analysisservices-dotnet#microsoft-analysisservices-connectioninfo-catalog). -* **Model** (Model) : See [Model MS Docs](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.model?view=analysisservices-dotnet). -* **AdomdConnection** (AdomdConnection) : For querying. See [AdomdConnection MS Docs](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.adomdclient.adomdconnection?view=analysisservices-dotnet). Connection made from parts of the originally provided connection string. -* **Tables** (PyTables[PyTable]) : Wrappers for [Table MS Docs](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.table?view=analysisservices-dotnet). So you have the full capabilities of what the MS Docs offer and a few others. Like `Tabular().Tables['Table Name'].Row_Count()`. Or you can find a table via `Tabular().Tables[0]` or `Tabular().Tables['Table Name']` -* **Columns** (List[Column]) : Easy access list of columns from model. See [Column MS Docs](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.column?view=analysisservices-dotnet). -* **Partitions** (List[Partition]) : Easy access list of partitions from model. See [Partition MS Docs](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.partition?view=analysisservices-dotnet). -* **Measures** (List[Measure]) : Easy access list of measures from model. See [Measure MS Docs](https://learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.table.measures?view=analysisservices-dotnet#microsoft-analysisservices-tabular-table-measures). - - - -**Methods:** - - -### .Reload_Model_Info -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L83) -```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 - - -### .Is_Process -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L100) -```python -.Is_Process() -``` - ---- -Run method to check if Processing is occurring. Will query DMV $SYSTEM.DISCOVER_JOBS to see if any processing is happening. - - -**Returns** - -* **bool** : True if DMV shows Process, False if not. - - -### .Disconnect -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L113) -```python -.Disconnect() -``` - ---- -Disconnects from Model - - -**Returns** - -* **bool** : True if successful - - -### .Refresh -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L122) -```python -.Refresh( - Object: Union[str, Table, Partition, Dict[str, Any]], - RefreshType: RefreshType = RefreshType.Full, Tracing = False -) -``` - ---- -Refreshes table(s) and partition(s). - - -**Args** - -* **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. -* **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. -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. - - -**Raises** - -* **Exception** : Raises exception if unable to find table or partition via string. - - - -**Returns** - -* **WIP** : WIP - - -### .Update -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L234) -```python -.Update( - UpdateOptions: UpdateOptions = UpdateOptions.ExpandFull -) -``` - ---- -[Update Model](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.majorobject.update?view=analysisservices-dotnet#microsoft-analysisservices-majorobject-update(microsoft-analysisservices-updateoptions)) - - -**Args** - -* **UpdateOptions** (UpdateOptions, optional) : See above MS Doc link. Defaults to UpdateOptions.ExpandFull. - - -**Returns** - -* **None** : Placeholder to eventually change. - - -### .SaveChanges -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L246) -```python -.SaveChanges() -``` - - -### .Backup_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L297) -```python -.Backup_Table( - table_str: str -) -``` - ---- -USE WITH CAUTION, EXPERIMENTAL. Backs up table in memory, brings with it measures, columns, hierarchies, relationships, roles, etc. -It will add suffix '_backup' to all objects. -Refresh is performed from source during backup. - - -**Args** - -* **table_str** (str, optional) : Name of Table. - - -**Returns** - -* **bool** : Returns True if Successful, else will return error. - - -### .Revert_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L395) -```python -.Revert_Table( - table_str: str -) -``` - ---- -USE WITH CAUTION, EXPERIMENTAL. This is used in conjunction with Backup_Table(). -It will take the 'TableName_backup' and replace with the original. -Example scenario -> -1. model.Backup_Table('TableName') -2. perform any proposed changes in original 'TableName' -3. validate changes in 'TableName' -4. if unsuccessful run model.Revert_Table('TableName') - - -**Args** - -* **table_str** (str) : Name of table. - - -**Returns** - -* **bool** : Returns True if Successful, else will return error. - - -### .Query -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L491) -```python -.Query( - Query_Str: str -) -``` - ---- -Executes Query on Model and Returns Results in Pandas DataFrame - - -**Args** - -* **Query_Str** (str) : Dax Query. Note, needs full syntax (ex: EVALUATE). See (DAX Queries)[https://docs.microsoft.com/en-us/dax/dax-queries]. -Will check if query string is a file. If it is, then it will perform a query on whatever is read from the file. -It is also possible to query DMV. For example. Query("select * from $SYSTEM.DISCOVER_TRACE_EVENT_CATEGORIES"). See (DMVs)[https://docs.microsoft.com/en-us/analysis-services/instances/use-dynamic-management-views-dmvs-to-monitor-analysis-services?view=asallproducts-allversions] - - -**Returns** - -* **DataFrame** : Returns dataframe with results - - -### .Query_Every_Column -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L504) -```python -.Query_Every_Column( - query_function: str = 'COUNTROWS(VALUES(_))' -) -``` - ---- -This will dynamically create a query to pull all columns from the model and run the query function. It will replace the _ with the column to run. - - -**Args** - -* **query_function** (str, optional) : Dax query is dynamically building a query with the UNION & ROW DAX Functions. - - -**Returns** - -* **DataFrame** : Returns dataframe with results. - - -### .Query_Every_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L529) -```python -.Query_Every_Table( - query_function: str = 'COUNTROWS(_)' -) -``` - ---- -This will dynamically create a query to pull all tables from the model and run the query function. -It will replace the _ with the table to run. - - -**Args** - -* **query_function** (str, optional) : Dax query is dynamically building a query with the UNION & ROW DAX Functions. Defaults to 'COUNTROWS(_)'. - - -**Returns** - -* **DataFrame** : Returns dataframe with results - - -### .Analyze_BPA -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L550) -```python -.Analyze_BPA( - Tabular_Editor_Exe: str, Best_Practice_Analyzer: str -) -``` - ---- -Takes your Tabular Model and performs TE2s BPA. Runs through Command line. -[Tabular Editor BPA](https://docs.tabulareditor.com/te2/Best-Practice-Analyzer.html) -[Tabular Editor Command Line Options](https://docs.tabulareditor.com/te2/Command-line-Options.html) - - -**Args** - -* **Tabular_Editor_Exe** (str) : TE2 Exe File path. Feel free to use class TE2().EXE_Path or provide your own. -* **Best_Practice_Analyzer** (str) : BPA json file path. Feel free to use class BPA().Location or provide your own. Defualts to https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json - - -**Returns** - -* Assuming no failure, will return list of BPA violations. Else will return error from command line. - - -### .Create_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L583) -```python -.Create_Table( - df: pd.DataFrame, table_name: str -) -``` - ---- -Creates tables from pd.DataFrame as an M-Partition. -So will convert the dataframe to M-Partition logic via the M query table constructor. -Runs refresh and will update model. - - -**Args** - -* **df** (pd.DataFrame) : DataFrame to add to model -* **table_name** (str) : _description_ - - -**Returns** - -* **bool** : True if successful - diff --git a/docs/Traces.md b/docs/Traces.md deleted file mode 100644 index 20432e6..0000000 --- a/docs/Traces.md +++ /dev/null @@ -1,166 +0,0 @@ -# - - -## Base_Trace -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L11) -```python -Base_Trace( - Tabular_Class, Trace_Events: List[TraceEvent], - Trace_Event_Columns: List[TraceColumn], Handler: Callable -) -``` - - ---- -Generates Trace to be run on Server. -This is the base class to customize the type of Trace you are looking for. -[Server Traces](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular.server.traces?view=analysisservices-dotnet#microsoft-analysisservices-tabular-server-traces) - - -**Args** - -* **Tabular_Class** (Tabular) : Tabular Class. -* **Trace_Events** (List[TraceEvent]) : List of Trace Events. -* **Trace_Event_Columns** (List[TraceColumn]) : List of Trace Event Columns. -* **Handler** (Callable) : Function to call when Trace returns response. -[TraceEventClass](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.traceeventclass?view=analysisservices-dotnet) -[TraceEventColumn](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tracecolumn?view=analysisservices-dotnet) -Input needs to be two arguments. -One is source (Which is currently None... Need to investigate why). -Second is -[TraceEventArgs](https://docs.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.traceeventargs?view=analysisservices-dotnet) - - -**Methods:** - - -### .Build -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L56) -```python -.Build() -``` - ---- -Run on initialization. -This will take the inputed arguments for the class -and attempt to build the Trace. - - -**Returns** - -* **bool** : True if successful - - -### .Arguments -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L90) -```python -.Arguments( - Trace_Events: List[TraceEvent], Trace_Event_Columns: List[TraceColumn], - Handler: Callable -) -``` - - -### .Add -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L97) -```python -.Add() -``` - ---- -Runs on initialization. Adds built Trace to the Server. - - -**Returns** - -* **int** : Return int of placement in Server.Traces.get_Item(int) - - -### .Update -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L106) -```python -.Update() -``` - ---- -Runs on initialization. Syncs with Server. - - -**Returns** - -* **None** : Returns None. -Unless unsuccessful then it will return the error from Server. - -### .Start -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L116) -```python -.Start() -``` - ---- -Call when you want to start the Trace - - -**Returns** - -* **None** : Returns None. -Unless unsuccessful then it will return the error from Server. - -### .Stop -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L126) -```python -.Stop() -``` - ---- -Call when you want to stop the Trace - - -**Returns** - -* **None** : Returns None. -Unless unsuccessful then it will return the error from Server. - -### .Drop -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L136) -```python -.Drop() -``` - ---- -Call when you want to drop the Trace - - -**Returns** - -* **None** : Returns None. Unless unsuccessful, -then it will return the error from Server. - ----- - - -## Refresh_Trace -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L187) -```python -Refresh_Trace( - Tabular_Class, - Trace_Events: List[TraceEvent] = [TraceEventClass.ProgressReportBegin, - TraceEventClass.ProgressReportCurrent, TraceEventClass.ProgressReportEnd, - TraceEventClass.ProgressReportError], - Trace_Event_Columns: List[TraceColumn] = [TraceColumn.EventSubclass, - TraceColumn.CurrentTime, TraceColumn.ObjectName, TraceColumn.ObjectPath, - TraceColumn.DatabaseName, TraceColumn.SessionID, TraceColumn.TextData, - TraceColumn.EventClass, TraceColumn.ProgressTotal], - Handler: Callable = refresh_handler -) -``` - - ---- -Subclass of Base_Trace. For built-in Refresh Tracing. - - -**Args** - -* **Base_Trace** (_type_) : _description_ - diff --git a/docs/contributing.md b/docs/contributing.md deleted file mode 100644 index 44c3df9..0000000 --- a/docs/contributing.md +++ /dev/null @@ -1,9 +0,0 @@ -# Contributing Guidelines - -- Work will be distributed under MIT license -- Pull request into main will run a flake8 test. flake8 will need to pass before pull request will be accepted -- Updates of any kind are welcome! Even just letting me know of the issues. Or updating doc strings -- Limit any external modules, see pyproject.toml for dependencies -- Goal of project is to help connect the python world and the tabular model world for some easier programmatic execution on models. -- Docstrings follow google [docstring format](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) -- See actions, once main updated, [mkgendocs](https://github.com/davidenunes/mkgendocs) is used to auto create documentation \ No newline at end of file diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 28668a4..0000000 --- a/docs/index.md +++ /dev/null @@ -1,137 +0,0 @@ - -# PyTabular -[![PyPI version](https://badge.fury.io/py/python-tabular.svg)](https://badge.fury.io/py/python-tabular) -[![Downloads](https://pepy.tech/badge/python-tabular)](https://pepy.tech/project/python-tabular) -[![readthedocs](https://github.com/Curts0/PyTabular/actions/workflows/readthedocs.yml/badge.svg)](https://github.com/Curts0/PyTabular/actions/workflows/readthedocs.yml) -[![pages-build-deployment](https://github.com/Curts0/PyTabular/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/Curts0/PyTabular/actions/workflows/pages/pages-build-deployment) -[![flake8](https://github.com/Curts0/PyTabular/actions/workflows/flake8.yml/badge.svg?branch=master)](https://github.com/Curts0/PyTabular/actions/workflows/flake8.yml) -### What is it? - -[PyTabular](https://github.com/Curts0/PyTabular) (python-tabular in [pypi](https://pypi.org/project/python-tabular/)) 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). Current, this build is tested and working on Windows Operating System only. Help is needed to expand this for other operating systems. The package should have the dll files included when you import it. See [Documentation Here](https://curts0.github.io/PyTabular/). PyTabular is still considered alpha while I'm working on building out the proper tests and testing environments, so I can ensure some kind of stability in features. Please send bugs my way! Preferably in the issues section in Github. I want to harden this project so many can use it easily. I currently have local pytest for python 3.6 to 3.10 and run those tests through a local AAS and Gen2 model. - -### Getting Started -See the [Pypi project](https://pypi.org/project/python-tabular/) for available version. -```powershell -python3 -m pip install python-tabular -``` - -In your python environment, import pytabular and call the main Tabular Class. Only parameter needed is a solid connection string. -```python -import pytabular -model = pytabular.Tabular(CONNECTION_STR) -``` - -You can query your models with the Query method from your tabular class. For Dax Queries, it will need the full Dax syntax. See [EVALUATE example](https://dax.guide/st/evaluate/). This will return a [Pandas DataFrame](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html). If you are looking to return a single value, see below. Simply wrap your query in the the curly brackets. The method will take that single cell table and just return the individual value. You can also query your DMV. See below for example. See [PyTabular Docs for Query](https://curts0.github.io/PyTabular/Tabular/#query). -```python -#Run basic queries -DAX_QUERY = "EVALUATE TOPN(100, 'Table1')" -model.Query(DAX_QUERY) #returns pd.DataFrame() - -#or... -DMV_QUERY = "select * from $SYSTEM.DISCOVER_TRACE_EVENT_CATEGORIES" -model.Query(DMV_QUERY) #returns pd.DataFrame() - -#or... -SINGLE_VALUE_QUERY_EX = "EVALUATE {1}" -model.Query(SINGLE_VALUE_QUERY_EX) #returns 1 - -#or... -FILE_PATH = 'C:\\FILEPATHEXAMPLE\\file.dax' #or file.txt -model.Query(FILE_PATH) #Will return same logic as above, single values if possible else will return pd.DataFrame() -``` - -Refresh method to handle refreshes on your model. This is synchronous. Should be flexible enough to handle a variety of inputs. See [PyTabular Docs for Refreshing Tables and Partitions](https://curts0.github.io/PyTabular/Tabular/#refresh). Most basic way to refresh is input the table name string. The method will search for table and output exeption if unable to find it. For partitions you will need a key, value combination. Example, {'Table1':'Partition1'}. You can also take the key value pair and iterate through a group of partitions. Example, {'Table1':['Partition1','Partition2']}. Rather than providing a string, you can also input the actual class. See below for those examples, and you can acess them from the built in attributes self.Tables, self.Partitions or explore through the .Net classes yourself in self.Model.Tables. -```python -#You have a few options when refreshing. -model.Refresh('Table Name') - -#or... -model.Refresh(['Table1','Table2','Table3']) - -#or... -model.Refresh() - -#or... -model.Refresh() - -#or... -model.Refresh({'Table Name':'Partition Name'}) - -#or any kind of weird combination like -model.Refresh([{
:,'Table Name':['Partition1','Partition2']},'Table Name','Table Name2']) - -#Add Tracing=True for simple Traces tracking the refresh. -model.Refresh(['Table1','Table2'], Tracing=True) -``` - -### Use Cases - -#### If blank table, then refresh table. -This will use the function [Return_Zero_Row_Tables](https://curts0.github.io/PyTabular/Examples/#return_zero_row_tables) and the method [Refresh](https://curts0.github.io/PyTabular/Tabular/#refresh) from the Tabular class. -```python -import pytabular -model = pytabular.Tabular(CONNECTION_STR) -tables = pytabular.Return_Zero_Row_Tables() -if len(tables) > 0: - model.Refresh(tables, Tracing = True) #Add a trace in there for some fun. -``` - -#### Sneak in a refresh. -This will use the method [Is_Process](https://curts0.github.io/PyTabular/Tabular/#is_process) and the method [Refresh](https://curts0.github.io/PyTabular/Tabular/#refresh) from the Tabular class. It will check the DMV to see if any jobs are currently running classified as processing. -```python -import pytabular -model = pytabular.Tabular(CONNECTION_STR) -if model.Is_Process(): - #do what you want if there is a refresh happening -else: - model.Refresh(TABLES_OR_PARTITIONS_TO_REFRESH) -``` - -#### Show refresh times in model. -This will use the function [Table_Last_Refresh_Times](https://curts0.github.io/PyTabular/Examples/#table_last_refresh_times) and the method [Create_Table](https://curts0.github.io/PyTabular/Tabular/#create_table) from the Tabular class. It will search through the model for all tables and partitions and pull the 'RefreshedTime' property from it. It will return results into a pandas dataframe, which will then be converted into an M expression used for a new table. -```python -import pytabular -model = pytabular.Tabular(CONNECTION_STR) -df = pytabular.Table_Last_Refresh_Times(model, group_partition = False) -model.Create_Table(df, 'Refresh Times') -``` - - -#### If BPA Violation, then revert deployment. -Uses a few things. First the [BPA Class](https://curts0.github.io/PyTabular/Best%20Practice%20Analyzer/#bpa), then the [TE2 Class](https://curts0.github.io/PyTabular/Tabular%20Editor%202/), and will finish with the [Analyze_BPA](https://curts0.github.io/PyTabular/Tabular/#analyze_bpa) method. Did not want to re-invent the wheel with the amazing work done with Tabular Editor and it's BPA capabilities. -```python -import pytabular -model = pytabular.Tabular(CONNECTION_STR) -TE2 = pytabular.Tabular_Editor() #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 -results = model.Analyze_BPA(TE2.EXE,BPA.Location) - -if len(results) > 0: - #Revert deployment here! -``` - -#### Backup & Revert a Table. -USE WITH CAUTION, obviously not in PROD. I have been experimenting with this concept. Made for selfish reasons. Will probably get removed and I'll keep in my own local version. But fun to work with. Uses two methods. [Backup_Table](https://curts0.github.io/PyTabular/Tabular/#backup_table) and [Revert_Table](https://curts0.github.io/PyTabular/Tabular/#revert_table) - -```python -import pytabular -model = pytabular.Tabular(CONNECTION_STR) -model.Backup_Table('TableName') #This will backup the table with surround items (columns,measures,relationships,roles,hierarchies,etc.) and will add a suffix of '_backup' -#-----------# -#Make any changes to your original table and then revert or delete backup as necessary -#-----------# -model.Revert_Table('TableName') #This will essentially replace your original with _backup -``` - -#### Loop through and query Dax files -Let's say you have multiple dax queries you would like to store and run through as checks. The [Query](https://curts0.github.io/PyTabular/Tabular/#query) method on the Tabular class can also take file paths. Can really be any file type as it's just checking os.path.isfile(). But would suggest .dax or .txt. It will read the file that use that as the new Query_str argument. -```python -import pytabular -model = pytabular.Tabular(CONNECTION_STR) -LIST_OF_FILE_PATHS = ['C:\\FilePath\\file1.dax','C:\\FilePath\\file1.txt','C:\\FilePath\\file2.dax','C:\\FilePath\\file2.txt'] -for file_path in LIST_OF_FILE_PATHS: - model.Query(file_path) -``` - -### Contributing -See [CONTRIBUTING.md](CONTRIBUTING.md) \ No newline at end of file From 2cb453ab04ddad4243b05c3802f089e640a4a407 Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 2 Feb 2023 15:58:46 -0600 Subject: [PATCH 2/8] removing old components --- pytabular/__init__.py | 6 -- pytabular/basic_checks.py | 153 -------------------------------------- pytabular/pytabular.py | 56 +------------- 3 files changed, 4 insertions(+), 211 deletions(-) delete mode 100644 pytabular/basic_checks.py diff --git a/pytabular/__init__.py b/pytabular/__init__.py index f2de7d9..ca1a14e 100644 --- a/pytabular/__init__.py +++ b/pytabular/__init__.py @@ -54,12 +54,6 @@ logger.info("Importing specifics in module...") from .pytabular import Tabular -from .basic_checks import ( - BPA_Violations_To_DF, - Last_X_Interval, - Return_Zero_Row_Tables, - Table_Last_Refresh_Times, -) from .logic_utils import ( pd_dataframe_to_m_expression, pandas_datatype_to_tabular_datatype, diff --git a/pytabular/basic_checks.py b/pytabular/basic_checks.py deleted file mode 100644 index bde7909..0000000 --- a/pytabular/basic_checks.py +++ /dev/null @@ -1,153 +0,0 @@ -""" -All basic checks will eventually be deprecated for more intuitive methods in the right classes. -For example, instead of calling the function `Return_Zero_Row_Tables(model)`, -call the Find_Zero_Rows() method in the PyTables class. -That way you can dynamically run those methods on a subset of tables, -instead of only on the entire model. -""" -import logging -from typing import List, Union -import pytabular -from logic_utils import ticks_to_datetime -import sys -import pandas as pd -from measure import PyMeasure - -logger = logging.getLogger("PyTabular") - - -def Return_Zero_Row_Tables(model: pytabular.Tabular) -> List[str]: - """Returns list of table names of those that are returning isna() - Args: - model (pytabular.Tabular): Tabular Model - Returns: - List[str]: List of table names where DAX COUNTROWS('Table Name') is nan or 0. - """ - logger.warning( - "Return_Zero_Row_Tables() will be deprecated. \ - Instead use Zero_Row_Tables() through the PyTables class." - ) - query_function: str = "COUNTROWS(_)" - df: pd.DataFrame = model.Tables.Query_All(query_function) - return df[df[f"[{query_function}]"].isna()]["[Table]"].to_list() - - -def Table_Last_Refresh_Times( - model: pytabular.Tabular, group_partition: bool = True -) -> pd.DataFrame: - """Returns pd.DataFrame of tables with their latest refresh time. - Optional 'group_partition' variable, default is True. - If False an extra column will be include to have the last refresh time to the grain of the partition - Example to add to model model.Create_Table(p.Table_Last_Refresh_Times(model),'RefreshTimes') - Args: - model (pytabular.Tabular): Tabular Model - group_partition (bool, optional): Whether or not you want the grain of the dataframe to be by table or by partition. Defaults to True. - Returns: - pd.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() - """ - logger.info(f"Executing Basic Function {sys._getframe(0).f_code.co_name}") - logger.warning( - f"{sys._getframe(0).f_code.co_name} will be deprecated... Use Last_Refresh in PyTables class instead!" - ) - - data = { - "Tables": [partition.Table.Name for partition in model.Partitions], - "Partitions": [partition.Name for partition in model.Partitions], - "RefreshedTime": [ - ticks_to_datetime(partition.RefreshedTime.Ticks).strftime( - "%Y-%m-%dT%H:%M:%S.%fZ" - )[:-3] - for partition in model.Partitions - ], - } - df = pd.DataFrame(data) - if group_partition: - logger.debug("Grouping together to grain of Table") - return ( - df[["Tables", "RefreshedTime"]] - .groupby(by=["Tables"]) - .max() - .reset_index(drop=False) - ) - else: - logger.debug("Returning DF") - 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. - """ - logger.warning(f"{sys._getframe(0).f_code.co_name} will be deprecated...") - logger.warning( - "Honestly it was dumb I ever made this. You can use model.Analyze_BPA()" - ) - results = model.Analyze_BPA(te2, bpa) - data = [ - rule.replace(" violates rule ", "^").replace('"', "").split("^") - for rule in results - ] - columns = ["Object", "Violation"] - return pd.DataFrame(data, columns=columns) - - -def Last_X_Interval( - Model: pytabular.Tabular, - Measure: Union[str, PyMeasure], - Column_Name: Union[str, None] = None, - Date_Column_Identifier: str = "'Date'[DATE_DTE_KEY]", - Number_Of_Intervals: int = 90, - Interval: str = "DAY", -) -> pd.DataFrame: - """Pulls the Last X Interval (Ex Last 90 Days) of a specific measure. - - Args: - Model (pytabular.Tabular): Tabular Model to perform query on. - Measure (Union[str,pytabular.pytabular.Measure]): Measure to query. If string, will first check for a measure in the model with that name, otherwise will assume it is a DAX Expression (Ex SUM(FactTable[ColumnValue]) ) and perform that as expression - Column_Name (Union[str,None], optional): Column Name to be outputted in DataFrame. You can provide your own otherwise will take from the Measure Name. Defaults to "Result". - Date_Column_Identifier (str, optional): Date column dax identifier. Defaults to "'Date'[DATE_DTE_KEY]". - Number_Of_Intervals (int, optional): This is used to plug in the variables for [DATESINPERIOD](https://docs.microsoft.com/en-us/dax/datesinperiod-function-dax). Defaults to 90. - Interval (str, optional): Sames as Number_Of_Intervals. Used to plug in parameters of DAX function [DATESINPERIOD](https://docs.microsoft.com/en-us/dax/datesinperiod-function-dax). Defaults to "DAY". Possible options are "DAY", "MONTH", "QUARTER", and "YEAR" - - Returns: - pd.DataFrame: Pandas DataFrame of results. - """ - # pragma: no cover - logger.warning(f"{sys._getframe(0).f_code.co_name} will be deprecated...") - logger.warning( - "Honestly it was dumb I ever made this. Just write the darn thing in DAX" - ) - if isinstance(PyMeasure, str): - try: - Measure = [ - measure for measure in Model.Measures if measure.Name == Measure - ][-1] - Column_Name = Measure.Name if Column_Name is None else Column_Name - Expression = f"[{Measure.Name}]" - except Exception: - logging.debug("Measure is string but unable to find Measure...") - Column_Name = "Result" if Column_Name is None else Column_Name - Expression = Measure - else: - Column_Name = Measure.Name if Column_Name is None else Column_Name - Expression = f"[{Column_Name}]" - Query_Str = f""" - EVALUATE - SUMMARIZECOLUMNS( - {Date_Column_Identifier}, - KEEPFILTERS( DATESINPERIOD ( {Date_Column_Identifier}, UTCTODAY(), -{Number_Of_Intervals}, {Interval} ) ), - "{Column_Name}", {Expression} - ) - """ - logging.info( - f"Running query for {Column_Name} in the last {Number_Of_Intervals} {Interval}s..." - ) - return Model.Query(Query_Str) diff --git a/pytabular/pytabular.py b/pytabular/pytabular.py index 636802b..553935d 100644 --- a/pytabular/pytabular.py +++ b/pytabular/pytabular.py @@ -70,7 +70,7 @@ def __init__(self, CONNECTION_STR: str): for database in self.Server.Databases.GetEnumerator() if database.Name == self.Catalog or self.Catalog is None ][0] - except Exception: # pragma: no cover + except Exception: err_msg = f"Unable to find Database... {self.Catalog}" logger.error(err_msg) raise Exception(err_msg) @@ -240,7 +240,7 @@ def property_changes(Property_Changes): Xmla_Results, ) - def Backup_Table(self, table_str: str) -> bool: # pragma: no cover + def Backup_Table(self, table_str: str) -> bool: """Will be removed. This is experimental with no written pytest for it. Backs up table in memory, brings with it measures, columns, hierarchies, relationships, roles, etc. It will add suffix '_backup' to all objects. @@ -342,7 +342,7 @@ def clone_role_permissions(): self.SaveChanges() return True - def Revert_Table(self, table_str: str) -> bool: # pragma: no cover + def Revert_Table(self, table_str: str) -> bool: """Will be removed. This is experimental with no written pytest for it. This is used in conjunction with Backup_Table(). It will take the 'TableName_backup' and replace with the original. Example scenario -> @@ -461,7 +461,7 @@ def Query( if Effective_User is None: return self.Adomd.Query(Query_Str) - try: # pragma: no cover + try: # This needs a public model with effective users to properly test conn = self.Effective_Users[Effective_User] logger.debug(f"Effective user found querying as... {Effective_User}") @@ -472,54 +472,6 @@ def Query( return conn.Query(Query_Str) - def Query_Every_Column( - self, query_function: str = "COUNTROWS(VALUES(_))" - ) -> pd.DataFrame: - """Will be removed. Use `Query_All()` in your `PyColumns` instead. This will dynamically create a query to pull all columns from the model and run the query function. It will replace the _ with the column to run. - Args: - query_function (str, optional): Dax query is dynamically building a query with the UNION & ROW DAX Functions. - Returns: - pd.DataFrame: Returns dataframe with results. - """ - logger.info("Beginning execution of querying every column...") - logger.warning( - "Query_Every_Column will be deprecated... Use Query_All in PyTables class instead!" - ) - logger.debug(f"Function to be run: {query_function}") - logger.debug("Dynamically creating DAX query...") - query_str = "EVALUATE UNION(\n" - columns = [column for table in self.Tables for column in table.Columns] - 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" - query_str = f"{query_str[:-2]})" - return self.Query(query_str) - - def Query_Every_Table(self, query_function: str = "COUNTROWS(_)") -> pd.DataFrame: - """Will be removed. Use `Query_All()` in your `PyTables` instead. This will dynamically create a query to pull all tables from the model and run the query function. - It will replace the _ with the table to run. - Args: - query_function (str, optional): Dax query is dynamically building a query with the UNION & ROW DAX Functions. Defaults to 'COUNTROWS(_)'. - Returns: - pd.DataFrame: Returns dataframe with results - """ - logger.warning( - "Query_Every_Table will be deprecated... Use Query_All in PyTables class instead!" - ) - logger.info("Beginning execution of querying every table...") - logger.debug(f"Function to be run: {query_function}") - logger.debug("Dynamically creating DAX query...") - query_str = "EVALUATE UNION(\n" - for table in self.Tables: - table_name = table.get_Name() - dax_table_identifier = f"'{table_name}'" - query_str += f"ROW(\"Table\",\"{table_name}\",\"{query_function}\",{query_function.replace('_',dax_table_identifier)}),\n" - query_str = f"{query_str[:-2]})" - return self.Query(query_str) - def Analyze_BPA( self, Tabular_Editor_Exe: str, Best_Practice_Analyzer: str ) -> List[str]: From cafa87b6eff3c5ac7f3f1cc9b336dfbcf962e6ca Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 2 Feb 2023 15:59:10 -0600 Subject: [PATCH 3/8] journey to 100 codecoverage --- test/config.py | 4 +++- test/test_5column.py | 6 ++++++ test/test_9custom.py | 22 ---------------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/test/config.py b/test/config.py index 2021882..f524c27 100644 --- a/test/config.py +++ b/test/config.py @@ -27,7 +27,9 @@ def find_local_pbi(): p.logger.info(f"Opening {adventureworks_path}") subprocess.run(["powershell", f"Start-Process {adventureworks_path}"]) # Got to be a better way to wait and ensure the PBIX file is open? - p.logger.info("sleep(30)... Need a better way to wait until PBIX is loaded...") + p.logger.warning( + "sleep(30)... Need a better way to wait until PBIX is loaded..." + ) sleep(30) return p.find_local_pbi_instances()[0] diff --git a/test/test_5column.py b/test/test_5column.py index b708149..d66fba2 100644 --- a/test/test_5column.py +++ b/test/test_5column.py @@ -38,3 +38,9 @@ def test_get_sample_values(model): def test_query_every_column(model): """Tests `Query_All()` of PyColumns class.""" assert isinstance(model.Tables[testingtablename].Columns.Query_All(), pd.DataFrame) + + +@pytest.mark.parametrize("model", testing_parameters) +def test_dependencies(model): + df = model.Tables[0].Columns[1].get_dependencies() + assert isinstance(df, pd.DataFrame) diff --git a/test/test_9custom.py b/test/test_9custom.py index b42453a..80cc233 100644 --- a/test/test_9custom.py +++ b/test/test_9custom.py @@ -4,8 +4,6 @@ """ from test.config import testing_parameters, testingtablename import pytest -import pytabular as p -import pandas as pd @pytest.mark.parametrize("model", testing_parameters) @@ -36,23 +34,3 @@ def test_revert_table2(model): ) == 1 ) - - -@pytest.mark.parametrize("model", testing_parameters) -def test_query_every_table_deprecate(model): - assert len(model.Query_Every_Table()) > 0 - - -@pytest.mark.parametrize("model", testing_parameters) -def test_query_every_column_deprecate(model): - assert len(model.Query_Every_Column()) > 0 - - -@pytest.mark.parametrize("model", testing_parameters) -def test_zero_row_tables_deprecate(model): - assert isinstance(p.Return_Zero_Row_Tables(model), list) - - -@pytest.mark.parametrize("model", testing_parameters) -def test_last_refresh_times_deprecate(model): - assert isinstance(p.Table_Last_Refresh_Times(model), pd.DataFrame) From db0e5b34770613de2aaa1fcd933760818c93eb26 Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 2 Feb 2023 15:59:41 -0600 Subject: [PATCH 4/8] remove basic checks from docs --- mkdocs.yml | 1 - mkgendocs.yml | 7 ------- 2 files changed, 8 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 1899378..5738c6a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,5 +12,4 @@ nav: - Tabular Editor: Tabular Editor 2.md - PBI Helper: PBI Helper.md - Logic Utils: Logic Utils.md - - Examples: Examples.md theme: readthedocs diff --git a/mkgendocs.yml b/mkgendocs.yml index 100b3c8..99d8de4 100644 --- a/mkgendocs.yml +++ b/mkgendocs.yml @@ -56,13 +56,6 @@ pages: source: 'pytabular/pbi_helper.py' functions: - find_local_pbi_instances - - page: "Examples.md" - source: 'pytabular/basic_checks.py' - functions: - - Return_Zero_Row_Tables - - Table_Last_Refresh_Times - - BPA_Violations_To_DF - - Last_X_Interval - page: "Logic Utils.md" source: 'pytabular/logic_utils.py' functions: From 97e9c19fcd956807c1539d49d0443c606e912ce4 Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 2 Feb 2023 16:00:04 -0600 Subject: [PATCH 5/8] remove pragma no cover --- pytabular/column.py | 3 +-- pytabular/tabular_tracing.py | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pytabular/column.py b/pytabular/column.py index fa60cb4..708d7be 100644 --- a/pytabular/column.py +++ b/pytabular/column.py @@ -59,8 +59,7 @@ def get_sample_values(self, top_n: int = 3) -> pd.DataFrame: ORDER BY {column_to_sample} """ return self.Table.Model.Query(dax_query) - except Exception: # pragma: no cover - # Excluding exception from coverage.py for now... + except Exception: # This is really tech debt anyways and should be replaced... dax_query = f""" EVALUATE diff --git a/pytabular/tabular_tracing.py b/pytabular/tabular_tracing.py index 9bc4a46..0057294 100644 --- a/pytabular/tabular_tracing.py +++ b/pytabular/tabular_tracing.py @@ -80,7 +80,7 @@ def add_column(trace_event, trace_event_column): """Adds the column to trace event.""" try: trace_event.Columns.Add(trace_event_column) - except Exception: # pragma: no cover + except Exception: logger.warning(f"{trace_event} - {trace_event_column} Skipped") pass @@ -185,7 +185,7 @@ def _Query_DMV_For_Event_Categories(self): return Event_Categories -def _refresh_handler(source, args): # pragma: no cover +def _refresh_handler(source, args): """Default function called when `Refresh_Trace` is used. It will log various steps of the refresh process. """ @@ -285,7 +285,7 @@ def __init__( super().__init__(Tabular_Class, Trace_Events, Trace_Event_Columns, Handler) -def _query_monitor_handler(source, args): # pragma: no cover +def _query_monitor_handler(source, args): """ Default function used with the `Query_Monitor` trace. """ From ed31e7ab391c85d0fa425ee937b80c9f099b104a Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 2 Feb 2023 16:00:23 -0600 Subject: [PATCH 6/8] cleaner __getattr__ --- pytabular/object.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pytabular/object.py b/pytabular/object.py index 79204ca..1eae4dd 100644 --- a/pytabular/object.py +++ b/pytabular/object.py @@ -40,11 +40,8 @@ def __rich_repr__(self) -> str: Console().print(self._display) def __getattr__(self, attr): - """Searches in `self.__dict__` first, then `self._object`.""" - if attr in self.__dict__: - return getattr(self, attr) - else: - return getattr(self._object, attr) + """Searches in `self._object`""" + return getattr(self._object, attr) class PyObjects: From 08f2131b52272eccc15d9b2820140a371a7844d0 Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 2 Feb 2023 16:00:40 -0600 Subject: [PATCH 7/8] add folders to ignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 19286f7..9f1a62a 100644 --- a/.gitignore +++ b/.gitignore @@ -176,6 +176,8 @@ adhoc.py docs/_config.yml# Byte-compiled / optimized / DLL files *.py[cod] *$py.class +/Best_Practice_Analyzer +/Tabular_Editor_2 # Test files for new functionality test-notebook.ipynb From b814bb007abcc4bd2862dd45820332b8db747efb Mon Sep 17 00:00:00 2001 From: Curtis Stallings Date: Thu, 2 Feb 2023 16:01:03 -0600 Subject: [PATCH 8/8] 0.3.4 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 91624c7..b502605 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "python_tabular" -version = "0.3.3" +version = "0.3.4" authors = [ { name="Curtis Stallings", email="curtisrstallings@gmail.com" }, ]