diff --git a/dist/python_tabular-0.0.90-py3-none-any.whl b/dist/python_tabular-0.0.90-py3-none-any.whl new file mode 100644 index 0000000..f03cc01 Binary files /dev/null and b/dist/python_tabular-0.0.90-py3-none-any.whl differ diff --git a/dist/python_tabular-0.0.90.tar.gz b/dist/python_tabular-0.0.90.tar.gz new file mode 100644 index 0000000..e3f817b Binary files /dev/null and b/dist/python_tabular-0.0.90.tar.gz differ diff --git a/docs/Best Practice Analyzer.md b/docs/Best Practice Analyzer.md index 352ecae..32c0d6f 100644 --- a/docs/Best Practice Analyzer.md +++ b/docs/Best Practice Analyzer.md @@ -2,7 +2,7 @@ ## BPA -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/best_practice_analyzer.py\#L34) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/best_practice_analyzer.py\#L35) ```python BPA( File_Path: str = 'Default' @@ -18,7 +18,7 @@ Setting BPA Class for future work... ### Download_BPA_File -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/best_practice_analyzer.py\#L8) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/best_practice_analyzer.py\#L9) ```python .Download_BPA_File( Download_Location: str = 'https: //raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json', diff --git a/docs/Examples.md b/docs/Examples.md index fe076d8..464ef0d 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -2,7 +2,7 @@ ### Return_Zero_Row_Tables -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L8) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L9) ```python .Return_Zero_Row_Tables( model: pytabular.Tabular @@ -27,7 +27,7 @@ Returns list of table names of those that are returning isna() ### Table_Last_Refresh_Times -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L22) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L23) ```python .Table_Last_Refresh_Times( model: pytabular.Tabular, group_partition: bool = True @@ -56,7 +56,7 @@ If group_partition == True and the table has multiple partitions, then df.groupb ### BPA_Violations_To_DF -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L50) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/basic_checks.py\#L51) ```python .BPA_Violations_To_DF( model: pytabular.Tabular, te2: str, bpa: str @@ -78,3 +78,36 @@ Runs BPA Analyzer from TE2 and outputs result into a DF. * **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\#L67) +```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 index 6281c7e..fd2bbba 100644 --- a/docs/Logic Utils.md +++ b/docs/Logic Utils.md @@ -2,7 +2,7 @@ ### ticks_to_datetime -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L11) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L12) ```python .ticks_to_datetime( ticks: int @@ -27,7 +27,7 @@ Converts a C# System DateTime Tick into a Python DateTime ### pandas_datatype_to_tabular_datatype -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L22) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L23) ```python .pandas_datatype_to_tabular_datatype( df: pd.DataFrame @@ -52,7 +52,7 @@ WiP takes dataframe columns and gets respective tabular column datatype. ([NumP ### pd_dataframe_to_m_expression -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L76) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L77) ```python .pd_dataframe_to_m_expression( df: pd.DataFrame @@ -94,7 +94,7 @@ Source ### remove_folder_and_contents -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L120) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L121) ```python .remove_folder_and_contents( folder_location @@ -109,3 +109,29 @@ Internal used in tabular_editor.py and best_practice_analyzer.py. * **folder_location** (str) : Folder path to remove directory and contents. + +---- + + +### remove_suffix +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/logic_utils.py\#L132) +```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/Tabular Editor 2.md b/docs/Tabular Editor 2.md index 7753d02..cf0441c 100644 --- a/docs/Tabular Editor 2.md +++ b/docs/Tabular Editor 2.md @@ -2,7 +2,7 @@ ## Tabular_Editor -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_editor.py\#L38) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_editor.py\#L39) ```python Tabular_Editor( EXE_File_Path: str = 'Default' @@ -18,7 +18,7 @@ Setting Tabular_Editor Class for future work. ### Download_Tabular_Editor -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_editor.py\#L8) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_editor.py\#L9) ```python .Download_Tabular_Editor( Download_Location: str = 'https: //github.com/TabularEditor/TabularEditor/releases/download/2.16.7/TabularEditor.Portable.zip', @@ -39,5 +39,5 @@ Runs a request.get() to retrieve the zip file from web. Will unzip response and **Returns** -* **str** : _description_ +* **str** : File path of TabularEditor.exe diff --git a/docs/Tabular.md b/docs/Tabular.md index 42e6068..b051f9a 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\#L22) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L26) ```python Tabular( CONNECTION_STR: str @@ -16,7 +16,7 @@ Tabular Class to perform operations: [Microsoft.AnalysisServices.Tabular](https: **Args** -* **CONNECTION_STR** (str) : [Connection String](https://docs.microsoft.com/en-us/analysis-services/instances/connection-string-properties-analysis-services?view=asallproducts-allversions) +* **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. @@ -24,7 +24,7 @@ Tabular Class to perform operations: [Microsoft.AnalysisServices.Tabular](https: ### .Reload_Model_Info -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L54) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L58) ```python .Reload_Model_Info() ``` @@ -39,7 +39,7 @@ Runs on __init__ iterates through details, can be called after any model changes ### .Disconnect -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L65) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L70) ```python .Disconnect() ``` @@ -54,26 +54,42 @@ Disconnects from Model ### .Refresh -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L80) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L78) ```python .Refresh( - Object: Union[str, Table, Partition, Iterable], RefreshType = RefreshType.Full, - Run: bool = True + Object: Union[str, Table, Partition, Dict[str, Any]], + RefreshType: RefreshType = RefreshType.Full, Tracing = False ) ``` --- -Input Object(s) to be refreshed in the tabular model. Combine with .SaveChanges() to actually run the refresh on the model. +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. +* **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\#L110) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L171) ```python .Update( UpdateOptions: UpdateOptions = UpdateOptions.ExpandFull @@ -95,22 +111,14 @@ Input Object(s) to be refreshed in the tabular model. Combine with .SaveChanges( ### .SaveChanges -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L121) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L182) ```python .SaveChanges() ``` ---- -TODO need to clean this up and add more flexibility. -Just a simple wrapper to call self.Model.SaveChanges() - - -**Returns** - -bool: ### .Backup_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L131) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L204) ```python .Backup_Table( table_str: str @@ -134,7 +142,7 @@ Refresh is performed from source during backup. ### .Revert_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L198) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L271) ```python .Revert_Table( table_str: str @@ -162,7 +170,7 @@ Example scenario -> ### .Query -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L263) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L336) ```python .Query( Query_Str: str @@ -175,7 +183,9 @@ Executes Query on Model and Returns Results in Pandas DataFrame **Args** -* **Query_Str** (str) : Dax Query. Note, needs full syntax (ex: EVALUATE). See https://docs.microsoft.com/en-us/dax/dax-queries +* **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** @@ -184,7 +194,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\#L293) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L377) ```python .Query_Every_Column( query_function: str = 'COUNTROWS(VALUES(_))' @@ -207,7 +217,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\#L315) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L399) ```python .Query_Every_Table( query_function: str = 'COUNTROWS(_)' @@ -230,7 +240,7 @@ It will replace the _ with the table to run. ### .Analyze_BPA -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L335) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L419) ```python .Analyze_BPA( Tabular_Editor_Exe: str, Best_Practice_Analyzer: str @@ -255,7 +265,7 @@ Takes your Tabular Model and performs TE2s BPA. Runs through Command line. ### .Create_Table -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L359) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/pytabular.py\#L443) ```python .Create_Table( df: pd.DataFrame, table_name: str diff --git a/docs/Traces.md b/docs/Traces.md index a780792..7458084 100644 --- a/docs/Traces.md +++ b/docs/Traces.md @@ -2,7 +2,7 @@ ## Base_Trace -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L10) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L11) ```python Base_Trace( Tabular_Class, Trace_Events: List[TraceEvent], @@ -29,7 +29,7 @@ Generates Trace to be run on Server. This is the base class to customize the typ ### .Build -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L37) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L38) ```python .Build() ``` @@ -44,7 +44,7 @@ Run on initialization. This will take the inputed arguments for the class and at ### .Arguments -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L62) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L63) ```python .Arguments( Trace_Events: List[TraceEvent], Trace_Event_Columns: List[TraceColumn], @@ -54,7 +54,7 @@ Run on initialization. This will take the inputed arguments for the class and at ### .Add -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L65) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L66) ```python .Add() ``` @@ -69,7 +69,7 @@ Runs on initialization. Adds built Trace to the Server. ### .Update -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L74) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L75) ```python .Update() ``` @@ -84,7 +84,7 @@ Runs on initialization. Syncs with Server. ### .Start -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L83) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L84) ```python .Start() ``` @@ -99,7 +99,7 @@ Call when you want to start the Trace ### .Stop -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L92) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L93) ```python .Stop() ``` @@ -114,7 +114,7 @@ Call when you want to stop the Trace ### .Drop -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L101) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L102) ```python .Drop() ``` @@ -129,7 +129,7 @@ Call when you want to drop the Trace ### .Query_DMV_For_Event_Categories -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L110) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L111) ```python .Query_DMV_For_Event_Categories() ``` @@ -147,7 +147,7 @@ Internal use. Called during the building process to locate allowed columns for e ## Refresh_Trace -[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L138) +[source](https://github.com/Curts0/PyTabular\blob\master\pytabular/tabular_tracing.py\#L139) ```python Refresh_Trace( Tabular_Class, @@ -158,7 +158,7 @@ Refresh_Trace( TraceColumn.CurrentTime, TraceColumn.ObjectName, TraceColumn.ObjectPath, TraceColumn.DatabaseName, TraceColumn.SessionID, TraceColumn.TextData, TraceColumn.EventClass, TraceColumn.ProgressTotal], - Handler: Callable = default_refresh_handler + Handler: Callable = refresh_handler ) ``` diff --git a/docs/index.md b/docs/index.md index 99ae859..fa477f2 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. See [Documentation Here](https://curts0.github.io/PyTabular/) +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/). 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. ### Getting Started @@ -21,25 +21,49 @@ In your python environment, import pytabular and call the main Tabular Class. On model = pytabular.Tabular(CONNECTION_STR) ``` -DAX Query +Query Model ```python - model.Query(DAX_QUERY) - # Returns a Pandas DataFrame + #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 ``` -[Refresh Tables and Partitions](https://curts0.github.io/PyTabular/Tabular/#refresh) +See [Refresh Tables and Partitions](https://curts0.github.io/PyTabular/Tabular/#refresh). ```python - #Can be str(table name only), Table object, Partition object, or an iterable combination of the three. + #You have a few options when refreshing. model.Refresh('Table Name') - tables_to_refresh = ['Table Name 1', 'Table Name 2',