Powershell Excel PivotFilters.Add - excel

I've written a Powershell script to grab a CSV, create an Excel sheet from it, and create a Pivottable with a few rows and summary values. I'm also able to add a filter field but what I cannot successfully do is tell the filter field what value I want it to filter on.
The PivotFilters.Add and PivotFilters.Add2 methods always throw a "Value does not fall within the expected range" error.
Here's the relevant portions of the code:
# Add PivotTable
$WS2 = $WBK1.Worksheets.Add()
$WS2.Name = "PivotTable"
$xlPivotTableVersion15 = 5 # Excel 2013
$xlPivotTableVersion12 = 3 # Excel 2007
$xlDescending = 2
$xlDatabase = 1 # this just means local sheet data
$xlHidden = 0
$xlRowField = 1
$xlColumnField = 2
$xlFilterField = 3
$xlDataField = 4
$xlSum = -4157
$xlAverage = -4106
$xlCount = -4112
$xlRight = -4152
$pivotRange = $WS1.UsedRange
$PivotTable = $WBK1.PivotCaches().Create($xlDatabase,$pivotRange,$xlPivotTableVersion15)
$PivotTable.CreatePivotTable("R1C1","Tables1") | Out-Null
[void]$WS2.Select()
$WBK1.ShowPivotTableFieldList = $true
# configure pivottable fields
$PivotFields = $WS2.PivotTables("Tables1").PivotFields("VmName")
$PivotFields.Orientation = $xlRowField
$PivotFields = $WS2.PivotTables("Tables1").PivotFields("RAM")
$PivotFields.Orientation = $xlDataField
$PivotFields.Function = $xlAverage
$PivotFields = $WS2.PivotTables("Tables1").PivotFields("vCPU")
$PivotFields.Orientation = $xlDataField
$PivotFields.Function = $xlAverage
$PivotFields = $WS2.PivotTables("Tables1").PivotFields("Dept") # I also tried commenting these lines out
$PivotFields.Orientation = $xlFilterField # I also tried commenting these lines out
$xlValueEquals = 7
$xlCaptionEquals = 15
$WS2.PivotTables("Tables1").PivotFields("Dept").PivotFilters.Add2($xlValueEquals, "Finance")
I have tried using both PivotFilters.Add and PivotFilters.Add2 either way I always get "Value does not fall within the expected range."
I've also tried commenting out the lines annotated above as I wondered if already having a filter created was confusing it.
Any advice greatly appreciated!

OK so in the end I found that basically writing a value into the cell of the filter I created using...
$PivotFields = $WS2.PivotTables("Tables1").PivotFields("Dept")
$PivotFields.Orientation = $xlFilterField
...worked and so I could do away with the PivotFilters.Add / PivotFilters.Add2 method. If anyone does know the answer to that I'm sure someone on t'interweb would be grateful for the solution being shared.

I too never got the PivotFilters.Add method to work. What did work for me, however, was creating and manipulating a slicer.
Once created and configured (e.g. a date slicer), you can query the slicer's SlicerCacheLevel.SlicerItems array for valid values and then apply filtering / slice the data by updating the array in SlicerCache.VisibleSlicerItemsList.
Confusingly, there is a SlicerCacheLevel.VisibleSlicerItemsList that errors when you try to update it. I didn't look into further as SlicerCache.VisibleSlicerItemsList worked.

Related

Using Powershell and TextToColumns in an Excel Workbook

I have an Excel sheet with 10 columns each containing two data fields separated by a space. I want to separate the data fields into two columns. But I don't want to replicate the code 10 times. I want code I can loop through 10 times. Sounds simple right? The problem is that I can't set the ranges properly for the texttocolumns method. It seems to insist on having literal references, i.e. "A1".
Trying to use any kind of counter for the range settings throws a COMexception error.
This works:
`$range = $sheet.Range("A1").entirecolumn;
$target = $sheet.Range("A1");
$xlDelimited = 1
$xlTextQualifier = -4142
$xlTextFormat = 2
$result = $range.texttocolumns($target,$xlDelimited,$xlTextQualifier,$false,$false,$false,$false,$false,$true," ")`
This doesn't work:
`$range = $sheet.Range(1).entirecolumn;
$target = $sheet.Range(1);
$xlDelimited = 1
$xlTextQualifier = -4142
$xlTextFormat = 2
$result = $range.texttocolumns($target,$xlDelimited,$xlTextQualifier,$false,$false,$false,$false,$false,$true," ")`

update data via macro from another workbook

I need some help with vba code. I'm self-lerning so please be understanding for simply cases ;)
I'm trying to create macro which will be looking for some records/cells in one workbook (FileA) (determined by 3 conditions) and then paste values to another workbook (FileB) and after that find in another tab in FileB some values where condition will be pasted value to match them with looking value (I belivie it could be done somehow with Vlookup but I get stuck).
Below problematic for me part of code (I'm working on some files found in work, no one use it now).
First issue is with Set Update, I don't know why it takes it as "Nothing"... conditions are true - I mean "pp1" is existing in column A in FileA.
Second issue shows when I change i start range to some "later" numbers, eg From i = 2280, macro is ignoring last line where should assign some values (again shows update2 as "nothing") but value if pp2 is existing in W column in tab data...
Dim name As String
name = "[path to file on sharepoint]"
Application.ScreenUpdating = False
Workbooks.Open Filename:=name
a = 1
For i = 2263 To 14000
If Workbooks("FileA").Sheets("Main").Cells(i, 11) = "CANCEL" And Workbooks("FileA").Sheets("Main").Cells(i, 6) = "DENIS" And Workbooks("FileA").Sheets("Main").Cells(i, 5) > 1301358454 Then
pp1 = Workbooks("FileA").Sheets("Main").Cells(i, 1)
If pp1 > 0 Then
Set Update = Workbooks("FileA").Worksheets("Main").Range("A:A").Find(pp1, lookat:=xlPart)
If Update > 0 Then
Update = Update.Row
Workbooks("FileB").Worksheets("lost").Cells(a, 1).Value = Workbooks("FileA").Worksheets("Main").Cells(Update, 5)
pp2 = Workbooks("FileB").Worksheets("lost").Cells(a, 1)
update2 = Workbooks("FileB").Worksheets("data").Range("W:W").Find(pp2, lookat:=xlPart).Row
Workbooks("FileB").Worksheets("lost").Cells(a, 5) = Workbooks("FileB").Worksheets("data").Cells(update2, 43)

How to use Autofill/Filldown with a range of values

I have been trying to get Excel to apply a formula over a set of columns and then extend the pattern across the entire set of rows.
This has led to the following code:
For i = 0 To avgsheetNames.Count - 1
If Contains(CStr(avgsheetNames(i)), "Scores") = True Then
With mainWorkBook.Worksheets(avgsheetNames(i))
strFormulas(1) = "=SUM(Aggregated_Internal_Scores!I2:I7)/6"
strFormulas(2) = "=SUM(Aggregated_Internal_Scores!J2:J7)/6"
strFormulas(3) = "=SUM(Aggregated_Internal_Scores!K2:K7)/6"
strFormulas(4) = "=SUM(Aggregated_Internal_Scores!L2:L7)/6"
strFormulas(5) = "=SUM(Aggregated_Internal_Scores!M2:M7)/6"
strFormulas(6) = "=SUM(Aggregated_Internal_Scores!N2:N7)/6"
strFormulas2(1) = "=SUM(Aggregated_Internal_Scores!I8:I13)/6"
strFormulas2(2) = "=SUM(Aggregated_Internal_Scores!J8:J13)/6"
strFormulas2(3) = "=SUM(Aggregated_Internal_Scores!K8:K13)/6"
strFormulas2(4) = "=SUM(Aggregated_Internal_Scores!L8:L13)/6"
strFormulas2(5) = "=SUM(Aggregated_Internal_Scores!M8:M13)/6"
strFormulas2(6) = "=SUM(Aggregated_Internal_Scores!N8:N13)/6"
mainWorkBook.Worksheets(avgsheetNames(i)).Range("C2:H2").Formula = strFormulas
mainWorkBook.Worksheets(avgsheetNames(i)).Range("C3:H3").Formula = strFormulas2
mainWorkBook.Worksheets(avgsheetNames(i)).Range("C2:H3").AutoFill Destination:=mainWorkBook.Worksheets(avgsheetNames(i)).Range("C2:H32")
End With
End If
As you can see I have tried to provide the pattern I am going for where the values extracted from the "Aggregated_Internal_Scores" sheet should follow the pattern I2:I7 > I8:I13 > I14:I19 and so on.
However, when the macro has been executed what I get is I2:I7 > I8:I13 > I4:I9 > I10:I15?
It seems Excel is taking the block C2:H3 as the pattern and just incrementing by 2 at the start of every block.
Can you anyone explain where I have gone wrong and how I can specify that I want the extraction of sheet values to follow a certain pattern?
Thank you in advance!
Use:
mainWorkBook.Worksheets(avgsheetNames(i)).Range("C2:H32").Formula = "=SUM(INDEX(Aggregated_Internal_Scores!I:I,(ROW($ZZ1)-1)*6+2):INDEX(Aggregated_Internal_Scores!I:I,(ROW($ZZ1)-1)*6+7))/6"
Replace everything inside the If with that.
If one has Office 365 with dynamic array formula then use:
mainWorkBook.Worksheets(avgsheetNames(i)).Range("C2:H32").Formula2 = "=SUM(INDEX(Aggregated_Internal_Scores!I:I,SEQUENCE(6,,(ROW($ZZ1)-1)*6+2))/6"

Selecting multiple fields in win32c.xlPageField

Objective: I'm using a python script to generate an excel report (contains lots of pivot tables).
Problem: Unable to figure out how to add multiple items to pivot table filter
I've figured out a cumbersome solution where I can create individual data sets that are pre-filtered so I don't need to filter them in the pivot table. However, this isn't really efficient or effective if somebody wants to swap the filter on the final excel report.
I'll use code I found online as an example:
import win32com.client
Excel = win32com.client.gencache.EnsureDispatch('Excel.Application')
win32c = win32com.client.constants
wb = Excel.Workbooks.Add()
Sheet1 = wb.Worksheets("Sheet1")
TestData = [['Country','Name','Gender','Sign','Amount'],
['CH','Max' ,'M','Plus',123.4567],
['FR','Max' ,'M','Minus',-23.4567],
['CH','Max' ,'M','Plus',12.2314],
['SP','Max' ,'M','Minus',-2.2314],
['CH','Sam' ,'M','Plus',453.7685],
['CH','Sam' ,'M','Minus',-53.7685],
['CH','Sara','F','Plus',777.666],
['CH','Sara','F','Minus',-77.666],
['DE','Hans','M','Plus',345.088],
['DE','Hans','M','Minus',-45.088],
['DE','Paul','M','Plus',222.455],
['DE','Paul','M','Minus',-22.455]]
for i, TestDataRow in enumerate(TestData):
for j, TestDataItem in enumerate(TestDataRow):
Sheet1.Cells(i+2,j+4).Value = TestDataItem
cl1 = Sheet1.Cells(2,4)
cl2 = Sheet1.Cells(2+len(TestData)-1,4+len(TestData[0])-1)
PivotSourceRange = Sheet1.Range(cl1,cl2)
PivotSourceRange.Select()
wb.Worksheets.Add()
Sheet2 = wb.Worksheets("Sheet2")
cl3=Sheet2.Cells(4,1)
PivotTargetRange= Sheet2.Range(cl3,cl3)
PivotTableName = 'ReportPivotTable'
PivotCache = wb.PivotCaches().Create(SourceType=win32c.xlDatabase, SourceData=PivotSourceRange, Version=win32c.xlPivotTableVersion14)
PivotTable = PivotCache.CreatePivotTable(TableDestination=PivotTargetRange, TableName=PivotTableName, DefaultVersion=win32c.xlPivotTableVersion14)
PivotTable.PivotFields('Name').Orientation = win32c.xlRowField
PivotTable.PivotFields('Country').Orientation = win32c.xlPageField
PivotTable.PivotFields('Country').CurrentPage = 'SP'
PivotTable.PivotFields('Gender').Orientation = win32c.xlColumnField
PivotTable.PivotFields('Sign').Orientation = win32c.xlColumnField
DataField = PivotTable.AddDataField(PivotTable.PivotFields('Amount'))
Excel.Visible = 1
wb.SaveAs('ranges_and_offsets.xlsx')
Excel.Application.Quit()
This generates a pivot table in excel and sets the Country Filter to SP. I know adding the following line will enable the multi selection option.
PivotTable.PivotFields('Country').EnableMultiplePageItems = True
At this point, I'm stuck. I would like to find a way to set Country to both SP and DE. I feel like the correct way to do this is to switch .CurrentPage to .CurrentPageList
However, I can't seem to get .CurrentPageList to work
Any help would be highly appreciated!
this maybe too late for this post, but the way I solved this was creating two loops
PivotTable.PivotFields(target_column).Position = some_position
PivotTable.PivotFields(target_column).EnableMultiplePageItems = True
for item in show_values:
PivotTable.PivotFields(target_column).PivotItems(item).Visible = True
for item in excl_values:
PivotTable.PivotFields(target_column).PivotItems(item).Visible = False
I hope it helps!

Finding the ROW value in an Excel WB using Powershell based on the MAX value

I have an Excel workbook that has values from F13 to F40, these values change every Powershell Job run we do.
I need to find the Top 3 values in the range of F13 - F40 and I need to get the Row Number of each of the three valuesToo
So far I can get the 3 values with this Code in Powershell
$EXCEL = New-Object -ComObject Excel.Application
$EXCEL.visible = $false
$basefinalwb = $EXCEL.workbooks.open("C:\relax\Book1.xlsx")
$basefinalws = $basefinalwb.worksheets.Item("Current Results")
$MAX1 = $basefinalws.cells.item(1,1).Formula = "=LARGE(F13:F40, 1)"
$MAX2 = $basefinalws.cells.item(2,1).Formula = "=LARGE(F13:F40, 2)"
$MAX3 = $basefinalws.cells.item(3,1).Formula = "=LARGE(F13:F40, 3)"
$MAX11 = $basefinalws.cells.item(1,1).value2
$MAX21 = $basefinalws.cells.item(2,1).value2
$MAX31 = $basefinalws.cells.item(3,1).value2
Write-host Max11 is $max11
Write-host Max21 is $max21
Write-host Max31 is $max31
The Results are this:
Max11 is 0.112570356472795
Max21 is 0.097560975609756
Max31 is 0.0881801125703564
Now I need to figure out which Row each of those Values came from
So I have tried this code and it returns blank
$Cell2 = $basefinalws.range("F1","F40").find("$MAX21")
$Cell2.Value2
$Cell2.Row
$Cell2
In my testing I even pulled the Value on the Cell the number came from and they match .. I just cannot figure out why "find" isn't working.
Now the only thing I can think of is that all the values in the F column are from Formulas =(D13-C13)/C13 but the above code does grab the value of the cell just fine it just can't re-find it so I can get the Row Number
If anyone knows where I am going wrong, help is appreciated or maybe an easier way to accomplish the task?
Thank you!
Since you are already finding the 3 top values with the LARGE formula, a possibly easier option than Find could be using ROW, INDEX and MATCH formulas somewhere else on the worksheet... something like this (tested with the formula =(D13-C13)/C13 in F13:F40.
...
$ROW1 = $basefinalws.cells.item(4,1).Formula = "=ROW(INDEX(F13:F40,MATCH(A1,F13:F40,0)))"
$ROW2 = $basefinalws.cells.item(5,1).Formula = "=ROW(INDEX(F13:F40,MATCH(A2,F13:F40,0)))"
$ROW3 = $basefinalws.cells.item(6,1).Formula = "=ROW(INDEX(F13:F40,MATCH(A3,F13:F40,0)))"
...
$ROW11 = $basefinalws.cells.item(4,1).value2
$ROW21 = $basefinalws.cells.item(5,1).value2
$ROW31 = $basefinalws.cells.item(6,1).value2
...
Write-Host Row11 is $Row11
Write-Host Row21 is $Row21
Write-Host Row31 is $Row31

Resources