From 8ef93de26f32921ee8c08d783b887b9a29e9ff9d Mon Sep 17 00:00:00 2001 From: jhoneill Date: Fri, 29 Jun 2018 16:41:06 +0100 Subject: [PATCH 1/6] Pester Tests & bug fix for compare-ws (see readme) --- Compare-WorkSheet.tests.ps1 | 190 +------------------------- README.md | 8 +- compare-worksheet.ps1 | 263 ------------------------------------ 3 files changed, 6 insertions(+), 455 deletions(-) diff --git a/Compare-WorkSheet.tests.ps1 b/Compare-WorkSheet.tests.ps1 index 3ddb49b..dd07aa4 100644 --- a/Compare-WorkSheet.tests.ps1 +++ b/Compare-WorkSheet.tests.ps1 @@ -1,7 +1,6 @@ -<<<<<<< HEAD -describe "Compare Worksheet" { +Describe "Compare Worksheet" { - del "$env:temp\server*.xlsx" + Remove-Item -Path "$env:temp\server*.xlsx" [System.Collections.ArrayList]$s = get-service | Select-Object -Property * $s | Export-Excel -Path $env:temp\server1.xlsx @@ -180,188 +179,3 @@ } - -======= -describe "Compare Worksheet" { - - del "$env:temp\server*.xlsx" - [System.Collections.ArrayList]$s = get-service | Select-Object -Property * - - $s | Export-Excel -Path $env:temp\server1.xlsx - - #$s is a zero based array, excel rows are 1 based and excel has a header row so Excel rows will be 2 + index in $s - $row4Displayname = $s[2].DisplayName - $s[2].DisplayName = "Changed from the orginal" - - $d = $s[-1] | Select-Object -Property * - $d.DisplayName = "Dummy Service" - $d.Name = "Dummy" - $s.Insert(3,$d) - - $row6Name = $s[5].name - $s.RemoveAt(5) - - $s | Export-Excel -Path $env:temp\server2.xlsx - #Assume default worksheet name, (sheet1) and column header for key ("name") - $comp = compare-WorkSheet "$env:temp\Server1.xlsx" "$env:temp\Server2.xlsx" - - Context "Simple comparison output" { - it "Found the right number of differences " { - $comp | should not beNullOrEmpty - $comp.Count | should be 4 - } - it "Found the data row with a changed property " { - $comp | should not beNullOrEmpty - $comp[0]._Side | should be '=>' - $comp[1]._Side | should be '<=' - $comp[0]._Row | should be 4 - $comp[1]._Row | should be 4 - $comp[1].Name | should be $comp[0].Name - $comp[1].DisplayName | should be $row4Displayname - $comp[0].DisplayName | should be "Changed from the orginal" - } - it "Found the inserted data row " { - $comp | should not beNullOrEmpty - $comp[2]._Side | should be '=>' - $comp[2]._Row | should be 5 - $comp[2].Name | should be "Dummy" - } - it "Found the deleted data row " { - $comp | should not beNullOrEmpty - $comp[3]._Side | should be '<=' - $comp[3]._Row | should be 6 - $comp[3].Name | should be $row6Name - } - } - - $null = compare-WorkSheet "$env:temp\Server1.xlsx" "$env:temp\Server2.xlsx" -BackgroundColor LightGreen - $xl1 = Open-ExcelPackage -Path "$env:temp\Server1.xlsx" - $xl2 = Open-ExcelPackage -Path "$env:temp\Server2.xlsx" - $s1Sheet = $xl1.Workbook.Worksheets[1] - $s2Sheet = $xl2.Workbook.Worksheets[1] - - Context "Setting the background to highlight different rows" { - it "set the background on the right rows " { - $s1Sheet.Cells["4:4"].Style.Fill.BackgroundColor.Rgb | should be "FF90EE90" - $s1Sheet.Cells["6:6"].Style.Fill.BackgroundColor.Rgb | should be "FF90EE90" - $s2Sheet.Cells["4:4"].Style.Fill.BackgroundColor.Rgb | should be "FF90EE90" - $s2Sheet.Cells["5:5"].Style.Fill.BackgroundColor.Rgb | should be "FF90EE90" - } - it "Didn't set other cells " { - $s1Sheet.Cells["3:3"].Style.Fill.BackgroundColor.Rgb | should not be "FF90EE90" - $s1Sheet.Cells["F4"].Style.Font.Color.Rgb | should beNullOrEmpty - $s2Sheet.Cells["F4"].Style.Font.Color.Rgb | should beNullOrEmpty - $s2Sheet.Cells["3:3"].Style.Fill.BackgroundColor.Rgb | should not be "FF90EE90" - } - } - - Close-ExcelPackage -ExcelPackage $xl1 -NoSave - Close-ExcelPackage -ExcelPackage $xl2 -NoSave - $null = compare-WorkSheet "$env:temp\Server1.xlsx" "$env:temp\Server2.xlsx" -AllDataBackgroundColor white -BackgroundColor LightGreen -FontColor DarkRed - $xl1 = Open-ExcelPackage -Path "$env:temp\Server1.xlsx" - $xl2 = Open-ExcelPackage -Path "$env:temp\Server2.xlsx" - $s1Sheet = $xl1.Workbook.Worksheets[1] - $s2Sheet = $xl2.Workbook.Worksheets[1] - - Context "Setting the forgound to highlight changed properties" { - it "Added foreground colour to the right cells " { - $s1Sheet.Cells["4:4"].Style.Fill.BackgroundColor.Rgb | should be "FF90EE90" - $s1Sheet.Cells["6:6"].Style.Fill.BackgroundColor.Rgb | should be "FF90EE90" - $s2Sheet.Cells["4:4"].Style.Fill.BackgroundColor.Rgb | should be "FF90EE90" - $s2Sheet.Cells["5:5"].Style.Fill.BackgroundColor.Rgb | should be "FF90EE90" - $s1Sheet.Cells["F4"].Style.Font.Color.Rgb | should be "FF8B0000" - $s2Sheet.Cells["F4"].Style.Font.Color.Rgb | should be "FF8B0000" - } - it "Didn't set the foreground on other cells " { - $s1Sheet.Cells["F5"].Style.Font.Color.Rgb | should beNullOrEmpty - $s2Sheet.Cells["F5"].Style.Font.Color.Rgb | should beNullOrEmpty - $s1Sheet.Cells["G4"].Style.Font.Color.Rgb | should beNullOrEmpty - $s2Sheet.Cells["G4"].Style.Font.Color.Rgb | should beNullOrEmpty - - } - } - - Close-ExcelPackage -ExcelPackage $xl1 -NoSave - Close-ExcelPackage -ExcelPackage $xl2 -NoSave - - [System.Collections.ArrayList]$s = get-service | Select-Object -Property * -ExcludeProperty Name - - $s | Export-Excel -Path $env:temp\server1.xlsx -WorkSheetname Server1 - - #$s is a zero based array, excel rows are 1 based and excel has a header row so Excel rows will be 2 + index in $s - $row4Displayname = $s[2].DisplayName - $s[2].DisplayName = "Changed from the orginal" - - $d = $s[-1] | Select-Object -Property * - $d.DisplayName = "Dummy Service" - $d.ServiceName = "Dummy" - $s.Insert(3,$d) - - $row6Name = $s[5].ServiceName - $s.RemoveAt(5) - - $s[10].ServiceType = "Changed should not matter" - - $s | Select-Object -Property ServiceName, DisplayName, StartType, ServiceType | Export-Excel -Path $env:temp\server2.xlsx -WorkSheetname server2 - #Assume default worksheet name, (sheet1) and column header for key ("name") - $comp = compare-WorkSheet "$env:temp\Server1.xlsx" "$env:temp\Server2.xlsx" -WorkSheetName Server1,Server2 -Key ServiceName -Property DisplayName,StartType -AllDataBackgroundColor AliceBlue -BackgroundColor White -FontColor Red - - $xl1 = Open-ExcelPackage -Path "$env:temp\Server1.xlsx" - $xl2 = Open-ExcelPackage -Path "$env:temp\Server2.xlsx" - - $s1Sheet = $xl1.Workbook.Worksheets["server1"] - $s2Sheet = $xl2.Workbook.Worksheets["server2"] - Context "More complex comparison output etc different worksheet names " { - it "Found the right number of differences " { - $comp | should not beNullOrEmpty - $comp.Count | should be 4 - } - it "Found the data row with a changed property " { - $comp | should not beNullOrEmpty - $comp[0]._Side | should be '=>' - $comp[1]._Side | should be '<=' - $comp[0]._Row | should be 4 - $comp[1]._Row | should be 4 - $comp[1].ServiceName | should be $comp[0].ServiceName - $comp[1].DisplayName | should be $row4Displayname - $comp[0].DisplayName | should be "Changed from the orginal" - } - it "Found the inserted data row " { - $comp | should not beNullOrEmpty - $comp[2]._Side | should be '=>' - $comp[2]._Row | should be 5 - $comp[2].ServiceName | should be "Dummy" - } - it "Found the deleted data row " { - $comp | should not beNullOrEmpty - $comp[3]._Side | should be '<=' - $comp[3]._Row | should be 6 - $comp[3].ServiceName | should be $row6Name - } - - it "set the background on the right rows " { - $s1Sheet.Cells["4:4"].Style.Fill.BackgroundColor.Rgb | should be "FFFFFFFF" - $s1Sheet.Cells["6:6"].Style.Fill.BackgroundColor.Rgb | should be "FFFFFFFF" - $s2Sheet.Cells["4:4"].Style.Fill.BackgroundColor.Rgb | should be "FFFFFFFF" - $s2Sheet.Cells["5:5"].Style.Fill.BackgroundColor.Rgb | should be "FFFFFFFF" - - $s1Sheet.Cells["E4"].Style.Font.Color.Rgb | should be "FFFF0000" - $s2Sheet.Cells["E4"].Style.Font.Color.Rgb | should be "FFFF0000" - } - it "Didn't set other cells " { - $s1Sheet.Cells["3:3"].Style.Fill.BackgroundColor.Rgb | should not be "FFFFFFFF" - $s2Sheet.Cells["3:3"].Style.Fill.BackgroundColor.Rgb | should not be "FFFFFFFF" - $s1Sheet.Cells["E5"].Style.Font.Color.Rgb | should beNullOrEmpty - $s2Sheet.Cells["E5"].Style.Font.Color.Rgb | should beNullOrEmpty - $s1Sheet.Cells["F4"].Style.Font.Color.Rgb | should beNullOrEmpty - $s2Sheet.Cells["F4"].Style.Font.Color.Rgb | should beNullOrEmpty - } - - } - Close-ExcelPackage -ExcelPackage $xl1 -NoSave -Show - Close-ExcelPackage -ExcelPackage $xl2 -NoSave -Show - - -} - ->>>>>>> 9f7884f991c80448091ef56853027f64d98b6cc7 diff --git a/README.md b/README.md index 684e57a..b6f4e87 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi - New commands - Diff , Merge and Join - `Compare-Worksheet` (introduced in 5.0) uses the built in `Compare-object` command, to output a command-line DIFF and/or colour the worksheet to show differences. For example, if my sheets are Windows services the *extra* rows or rows where the startup status has changed get highlighted - `Merge-Worksheet` (also introduced in 5.0) joins two lumps, side by highlighting the differences. So now I can have server A's services and Server Bs Services on the same page. I figured out a way to do multiple sheets. So I can have Server A,B,C,D on one page :-) that is `Merge-MultpleSheets` - For this release I've fixed heaven only knows how many typos and proof reading errors in the help for these two, but the code is unchanged - although correcting the spelling of Merge-MultipleSheets is potentially a breaking change (and it is still plural!) + For this release I've fixed heaven only knows how many typos and proof reading errors in the help for these two, the only code change is to fix a bug if two worksheets have different names, are in different files and the Comparison sends the delta in the second back before the one in first, then highlighting changed properties could throw an error. Correcting the spelling of Merge-MultipleSheets is potentially a breaking change (and it is still plural!) also fixed a bug in compare worksheet where color might not be applied correctly when the worksheets came from different files and had different name. - `Join-Worksheet` is **new** for ths release. At it's simplest it copies all the data in Worksheet A to the end of Worksheet B - Add-Worksheet @@ -68,9 +68,9 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi - Commented out the write verbose statements even if verbose is silenced they cause a significiant performance impact and if it's on they will cause a flood of messages. - Re-ordered the choices in the switch and added an option to say "If it is numeric already post it as is" - Added an option to only set the number format if doesn't match the default for the sheet. --Export-Excel Pester Tests - - I have converted examples 1-9, 11 and 13 from Export-Excel help into tests and have added some additional tests, and extra parameters to the example command to ge better test coverage. The test so far has 184 "should" conditions grouped as 58 "IT" statements; but is still a work in progress. --Compare-Worksheet pester tests +- Export-Excel Pester Tests + - I have converted examples 1-9, 11 and 13 from Export-Excel help into tests and have added some additional tests, and extra parameters to the example command to ge better test coverage. The test so far has 184 "should" conditions grouped as 58 "IT" statements; but is still a work in progress. +- Compare-Worksheet pester tests --- diff --git a/compare-worksheet.ps1 b/compare-worksheet.ps1 index 1c448cc..b6a845c 100644 --- a/compare-worksheet.ps1 +++ b/compare-worksheet.ps1 @@ -258,266 +258,3 @@ Function Compare-WorkSheet { elseif (-not $PassThru) {return ($diff | Select-Object -Property (@(@{n="_Side";e={$_.SideIndicator}},"_File" ,"_Sheet","_Row") + $propList))} if ( $PassThru) {return $diff } } -======= -Function Compare-WorkSheet { -<# - .Synopsis - Compares two worksheets with the same name in different files. - .Description - This command takes two file names, a worksheet name and a name for a key column. - It reads the worksheet from each file and decides the column names. - It builds as hashtable of the key column values and the rows they appear in - It then uses PowerShell's compare object command to compare the sheets (explicity checking all column names which have not been excluded) - For the difference rows it adds the row number for the key of that row - we have to add the key after doing the comparison, - otherwise rows will be considered as different simply because they have different row numbers - We also add the name of the file in which the difference occurs. - If -BackgroundColor is specified the difference rows will be changed to that background. - .Example - Compare-WorkSheet -Referencefile 'Server56.xlsx' -Differencefile 'Server57.xlsx' -WorkSheetName Products -key IdentifyingNumber -ExcludeProperty Install* | format-table - The two workbooks in this example contain the result of redirecting a subset of properties from Get-WmiObject -Class win32_product to Export-Excel - The command compares the "products" pages in the two workbooks, but we don't want to register a differnce if if the software was installed on a - different date or from a different place, so Excluding Install* removes InstallDate and InstallSource. - This data doesn't have a "name" column" so we specify the "IdentifyingNumber" column as the key. - The results will be presented as a table. - .Example - compare-WorkSheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -GridView - This time two workbooks contain the result of redirecting Get-WmiObject -Class win32_service to Export-Excel - Here the -Differencefile and -Referencefile parameter switches are assumed , and the default setting for -key ("Name") works for services - This will display the differences between the "services" sheets using a grid view - .Example - Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName Services -BackgroundColor lightGreen - This version of the command outputs the differences between the "services" pages and also highlights any different rows in the spreadsheet files. - .Example - Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName Services -BackgroundColor lightGreen -FontColor Red -Show - This builds on the previous example: this time Where two changed rows have the value in the "name" column (the default value for -key), - this version adds highlighting of the changed cells in red; and then opens the Excel file. - .Example - Compare-WorkSheet 'Pester-tests.xlsx' 'Pester-tests.xlsx' -WorkSheetName 'Server1','Server2' -Property "full Description","Executed","Result" -Key "full Description" - This time the reference file and the difference file are the same file and two different sheets are used. Because the tests include the - machine name and time the test was run the command specifies a limited set of columns should be used. - .Example - Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -GridView -ExcludeDifferent - The "General" page has a title and two unlabelled columns with a row forCPU, Memory, Domain, Disk and so on - So the command is instructed to starts at row 2 to skip the title and to name the columns: the first is "label" and the Second "Value"; - the label acts as the key. This time we interested the rows which are the same in both sheets, - and the result is displayed using grid view. Note that grid view works best when the number of columns is small. - .Example - Compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -BackgroundColor White -Show -AllDataBackgroundColor LightGray - This version of the previous command lightlights all the cells in lightgray and then sets the changed rows back to white; only - the unchanged rows are highlighted -#> - [cmdletbinding(DefaultParameterSetName)] - Param( - #First file to compare - [parameter(Mandatory=$true,Position=0)] - $Referencefile , - #Second file to compare - [parameter(Mandatory=$true,Position=1)] - $Differencefile , - #Name(s) of worksheets to compare. - $WorkSheetName = "Sheet1", - #Properties to include in the DIFF - supports wildcards, default is "*" - $Property = "*" , - #Properties to exclude from the the search - supports wildcards - $ExcludeProperty , - #Specifies custom property names to use, instead of the values defined in the column headers of the TopRow. - [Parameter(ParameterSetName='B', Mandatory)] - [String[]]$Headername, - #Automatically generate property names (P1, P2, P3, ..) instead of the using the values the top row of the sheet - [Parameter(ParameterSetName='C', Mandatory)] - [switch]$NoHeader, - #The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row. - [int]$Startrow = 1, - #If specified, highlights all the cells - so you can make Equal cells one colour, and Diff cells another. - [System.Drawing.Color]$AllDataBackgroundColor, - #If specified, highlights the DIFF rows - [System.Drawing.Color]$BackgroundColor, - #If specified identifies the tabs which contain DIFF rows (ignored if -backgroundColor is omitted) - [System.Drawing.Color]$TabColor, - #Name of a column which is unique and will be used to add a row to the DIFF object, default is "Name" - $Key = "Name" , - #If specified, highlights the DIFF columns in rows which have the same key. - [System.Drawing.Color]$FontColor, - #If specified opens the Excel workbooks instead of outputting the diff to the console (unless -passthru is also specified) - [Switch]$Show, - #If specified, the command tries to the show the DIFF in a Gridview and not on the console. (unless-Passthru is also specified). This Works best with few columns selected, and requires a key - [switch]$GridView, - #If specified -Passthrough full set of diff data is returned without filtering to the specified properties - [Switch]$PassThru, - #If specified the result will include equal rows as well. By default only different rows are returned - [Switch]$IncludeEqual, - #If Specified the result includes only the rows where both are equal - [Switch]$ExcludeDifferent - ) - - #if the filenames don't resolve, give up now. - try { $oneFile = ((Resolve-Path -Path $Referencefile -ErrorAction Stop).path -eq (Resolve-Path -Path $Differencefile -ErrorAction Stop).path)} - Catch { Write-Warning -Message "Could not Resolve the filenames." ; return } - - #If we have one file , we mush have two different worksheet names. If we have two files we can a single string or two strings. - if ($onefile -and ( ($WorkSheetName.count -ne 2) -or $WorkSheetName[0] -eq $WorkSheetName[1] ) ) { - Write-Warning -Message "If both the Reference and difference file are the same then worksheet name must provide 2 different names" - return - } - if ($WorkSheetName.count -eq 2) {$worksheet1 = $WorkSheetName[0] ; $WorkSheet2 = $WorkSheetName[1]} - elseif ($WorkSheetName -is [string]) {$worksheet1 = $WorkSheet2 = $WorkSheetName} - else {Write-Warning -Message "You must provide either a single worksheet name or two names." ; return } - - $params= @{ ErrorAction = [System.Management.Automation.ActionPreference]::Stop } - foreach ($p in @("HeaderName","NoHeader","StartRow")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}} - try { - $Sheet1 = Import-Excel -Path $Referencefile -WorksheetName $WorkSheet1 @params - $Sheet2 = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 @Params - } - Catch {Write-Warning -Message "Could not read the worksheet from $Referencefile and/or $Differencefile." ; return } - - #Get Column headings and create a hash table of Name to column letter. - $headings = $Sheet1[-1].psobject.Properties.name # This preserves the sequence - using get-member would sort them alphabetically! - $headings | ForEach-Object -Begin {$columns = @{} ; $i=65 } -Process {$Columns[$_] = [char]($i ++) } - - #Make a list of property headings using the Property (default "*") and ExcludeProperty parameters - if ($Key -eq "Name" -and $NoHeader) {$key = "p1"} - $propList = @() - foreach ($p in $Property) {$propList += ($headings.where({$_ -like $p}) )} - foreach ($p in $ExcludeProperty) {$propList = $propList.where({$_ -notlike $p}) } - if (($headings -contains $key) -and ($propList -notcontains $Key)) {$propList += $Key} - $propList = $propList | Select-Object -Unique - if ($propList.Count -eq 0) {Write-Warning -Message "No Columns are selected with -Property = '$Property' and -excludeProperty = '$ExcludeProperty'." ; return} - - #Add RowNumber, Sheetname and file name to every row - $FirstDataRow = $startRow + 1 - if ($Headername -or $NoHeader) {$FirstDataRow -- } - $i = $FirstDataRow ; foreach ($row in $Sheet1) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++) - Add-Member -InputObject $row -MemberType NoteProperty -Name "_Sheet" -Value $worksheet1 - Add-Member -InputObject $row -MemberType NoteProperty -Name "_File" -Value $Referencefile} - $i = $FirstDataRow ; foreach ($row in $Sheet2) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++) - Add-Member -InputObject $row -MemberType NoteProperty -Name "_Sheet" -Value $worksheet2 - Add-Member -InputObject $row -MemberType NoteProperty -Name "_File" -Value $Differencefile} - - if ($ExcludeDifferent -and -not $IncludeEqual) {$IncludeEqual = $true} - #Do the comparison and add file,sheet and row to the result - these are prefixed with "_" to show they are added the addition will fail if the sheet has these properties so split the operations - [PSCustomObject[]]$diff = Compare-Object -ReferenceObject $Sheet1 -DifferenceObject $Sheet2 -Property $propList -PassThru -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent | - Sort-Object -Property "_Row","File" - - #if BackgroundColor was specified, set it on extra or extra or changed rows - if ($diff -and $BackgroundColor) { - #Differences may only exist in one file. So gather the changes for each file; open the file, update each impacted row in the shee, save the file - $updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property "_File" - foreach ($file in $updates) { - try {$xl = Open-ExcelPackage -Path $file.name } - catch {Write-warning -Message "Can't open $($file.Name) for writing." ; return} - if ($AllDataBackgroundColor) { - $file.Group._sheet | Sort-Object -Unique | ForEach-Object { - $ws = $xl.Workbook.Worksheets[$_] - if ($headerName) {$range = "A" + $startrow + ":" + $ws.dimension.end.address} - else {$range = "A" + ($startrow + 1) + ":" + $ws.dimension.end.address} - Set-Format -WorkSheet $ws -BackgroundColor $AllDataBackgroundColor -Range $Range - } - } - foreach ($row in $file.group) { - $ws = $xl.Workbook.Worksheets[$row._Sheet] - $range = $ws.Dimension -replace "\d+",$row._row - Set-Format -WorkSheet $ws -Range $range -BackgroundColor $BackgroundColor - } - if ($TabColor) { - foreach ($tab in ($file.group._sheet | Select-Object -Unique)) { - $xl.Workbook.Worksheets[$tab].TabColor = $TabColor - } - } - $xl.save() ; $xl.Stream.Close() ; $xl.Dispose() - } - } - #if font colour was specified, set it on changed properties where the same key appears in both sheets. - if ($diff -and $FontColor -and ($propList -contains $Key) ) { - $updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property $Key | Where-Object {$_.count -eq 2} - if ($updates) { - $XL1 = Open-ExcelPackage -path $Referencefile - if ($oneFile ) {$xl2 = $xl1} - else {$xl2 = Open-ExcelPackage -path $Differencefile } - foreach ($u in $updates) { - foreach ($p in $propList) { - if ($u.group[0]._file -eq $Referencefile) { - $ws1 = $xl1.Workbook.Worksheets[$u.Group[0]._sheet] - $ws2 = $xl2.Workbook.Worksheets[$u.Group[1]._sheet] - } - else { - $ws1 = $xl2.Workbook.Worksheets[$u.Group[0]._sheet] - $ws2 = $xl1.Workbook.Worksheets[$u.Group[1]._sheet] - } - if($u.Group[0].$p -ne $u.Group[1].$p ) { - Set-Format -WorkSheet $ws1 -Range ($Columns[$p] + $u.Group[0]._Row) -FontColor $FontColor - Set-Format -WorkSheet $ws1 -Range ($Columns[$p] + $u.Group[1]._Row) -FontColor $FontColor - } - } - } - $xl1.Save() ; $xl1.Stream.Close() ; $xl1.Dispose() - if (-not $oneFile) {$xl2.Save() ; $xl2.Stream.Close() ; $xl2.Dispose()} - } - } - elseif ($diff -and $FontColor) {Write-Warning -Message "To match rows to set changed cells, you must specify -Key and it must match one of the included properties." } - - #if nothing was found write a message which wont be redirected - if (-not $diff) {Write-Host "Comparison of $Referencefile::$worksheet1 and $Differencefile::$WorkSheet2 returned no results." } - - if ($show) { - Start-Process -FilePath $Referencefile - if (-not $oneFile) { Start-Process -FilePath $Differencefile } - if ($GridView) { Write-Warning -Message "-GridView is ignored when -Show is specified" } - } - elseif ($GridView -and $propList -contains $key) { - - - if ($IncludeEqual -and -not $ExcludeDifferent) { - $GroupedRows = $diff | Group-Object -Property $key - } - else { #to get the right now numbers on the grid we need to have all the rows. - $GroupedRows = Compare-Object -ReferenceObject $Sheet1 -DifferenceObject $Sheet2 -Property $propList -PassThru -IncludeEqual | - Group-Object -Property $key - } - #Additions, deletions and unchanged rows will give a group of 1; changes will give a group of 2 . - - #If one sheet has extra rows we can get a single "==" result from compare, but with the row from the reference sheet - #but the row in the other sheet might so we will look up the row number from the key field build a hash table for that - $Sheet2 | ForEach-Object -Begin {$Rowhash = @{} } -Process {$Rowhash[$_.$key] = $_._row } - - $ExpandedDiff = ForEach ($g in $GroupedRows) { - #we're going to create a custom object from a hash table. We want the fields to be ordered - $hash = [ordered]@{} - foreach ($result IN $g.Group) { - # if result indicates equal or "in Reference" set the reference side row. If we did that on a previous result keep it. Otherwise set to "blank" - if ($result.sideindicator -ne "=>") {$hash["Row"] = $Rowhash[$g.Name] - #position the key as the next field (only appears once) - $Hash[$key] = $g.Name - #For all the other fields we care about create <=FieldName and/or =>FieldName - foreach ($p in $propList.Where({$_ -ne $key})) { - if ($result.SideIndicator -eq "==") {$hash[("=>$P")] = $hash[("<=$P")] =$result.$P} - else {$hash[($result.SideIndicator+$P)] =$result.$P} - } - } - [Pscustomobject]$hash - } - - #Sort by reference row number, and fill in any blanks in the difference-row column - $ExpandedDiff = $ExpandedDiff | Sort-Object -Property "row") {$ExpandedDiff[$i].">row" = $ExpandedDiff[$i-1].">row" } } - #Sort by difference row number, and fill in any blanks in the reference-row column - $ExpandedDiff = $ExpandedDiff | Sort-Object -Property ">row" - for ($i = 1; $i -lt $ExpandedDiff.Count; $i++) {if (-not $ExpandedDiff[$i]."row" } - elseif ( $IncludeEqual) {$ExpandedDiff = $ExpandedDiff | Sort-Object -Property "row" } - else {$ExpandedDiff = $ExpandedDiff.where({$_.side -ne "=="}) | Sort-Object -Property "row" } - $ExpandedDiff | Update-FirstObjectProperties | Out-GridView -Title "Comparing $Referencefile::$worksheet1 (<=) with $Differencefile::$WorkSheet2 (=>)" - } - elseif ($GridView ) {Write-Warning -Message "To use -GridView you must specify -Key and it must match one of the included properties." } - elseif (-not $PassThru) {return ($diff | Select-Object -Property (@(@{n="_Side";e={$_.SideIndicator}},"_File" ,"_Sheet","_Row") + $propList))} - if ( $PassThru) {return $diff } -} - ->>>>>>> 9f7884f991c80448091ef56853027f64d98b6cc7 From 512812d774a094549040955fb25ab11190df5a9e Mon Sep 17 00:00:00 2001 From: jhoneill Date: Wed, 4 Jul 2018 16:47:15 +0100 Subject: [PATCH 2/6] Move Compare-WorkSheet tests into __tests__ fixed bug where font not set in difference sheet --- .gitignore | 3 +++ .../Compare-WorkSheet.tests.ps1 | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) rename Compare-WorkSheet.tests.ps1 => __tests__/Compare-WorkSheet.tests.ps1 (96%) diff --git a/.gitignore b/.gitignore index bc21761..bf41cac 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ Network Trash Folder Temporary Items .apdisk testExport.xlsx +test.xlsx test.ps1 testPwd.xlsx test.csv @@ -55,3 +56,5 @@ test.xlsx testCCFMT.ps1 testHide.ps1 ImportExcel.zip +.vscode/launch.json + diff --git a/Compare-WorkSheet.tests.ps1 b/__tests__/Compare-WorkSheet.tests.ps1 similarity index 96% rename from Compare-WorkSheet.tests.ps1 rename to __tests__/Compare-WorkSheet.tests.ps1 index dd07aa4..70f0ca3 100644 --- a/Compare-WorkSheet.tests.ps1 +++ b/__tests__/Compare-WorkSheet.tests.ps1 @@ -1,3 +1,9 @@ +#Requires -Modules Pester + +# $here = Split-Path -Parent $MyInvocation.MyCommand.Path +# Import-Module $here -Force -Verbose +Import-Module $PSScriptRoot\..\ImportExcel.psd1 -Force + Describe "Compare Worksheet" { Remove-Item -Path "$env:temp\server*.xlsx" @@ -28,8 +34,7 @@ Describe "Compare Worksheet" { } it "Found the data row with a changed property " { $comp | should not beNullOrEmpty - $comp[0]._Side | should be '=>' - $comp[1]._Side | should be '<=' + $comp[0]._Side | should not be $comp[1]._Side $comp[0]._Row | should be 4 $comp[1]._Row | should be 4 $comp[1].Name | should be $comp[0].Name @@ -134,8 +139,7 @@ Describe "Compare Worksheet" { } it "Found the data row with a changed property " { $comp | should not beNullOrEmpty - $comp[0]._Side | should be '=>' - $comp[1]._Side | should be '<=' + $comp[0]._Side | should not be $comp[1]._Side $comp[0]._Row | should be 4 $comp[1]._Row | should be 4 $comp[1].ServiceName | should be $comp[0].ServiceName From a72d48970763354a0dcb723e3f83f2098e8a4c87 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Wed, 4 Jul 2018 16:50:47 +0100 Subject: [PATCH 3/6] bug fix for formatting font not working on second sheet --- compare-worksheet.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compare-worksheet.ps1 b/compare-worksheet.ps1 index b6a845c..0eaf1f4 100644 --- a/compare-worksheet.ps1 +++ b/compare-worksheet.ps1 @@ -185,7 +185,7 @@ Function Compare-WorkSheet { } if($u.Group[0].$p -ne $u.Group[1].$p ) { Set-Format -WorkSheet $ws1 -Range ($Columns[$p] + $u.Group[0]._Row) -FontColor $FontColor - Set-Format -WorkSheet $ws1 -Range ($Columns[$p] + $u.Group[1]._Row) -FontColor $FontColor + Set-Format -WorkSheet $ws2 -Range ($Columns[$p] + $u.Group[1]._Row) -FontColor $FontColor } } } From 06986df83a5624dd0f5289ca1e9f3f549bdcd8d2 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 5 Jul 2018 00:29:28 +0100 Subject: [PATCH 4/6] See readme.md --- Examples/Charts/Tools.xlsx | Bin 7296 -> 0 bytes Examples/PivotTableFilters/testPivot.xlsx | Bin 6013 -> 0 bytes Export-Excel.ps1 | 94 +++++++++++++++++----- Get-XYRange.ps1 | 8 +- New-ExcelChart.ps1 | 53 ++++++------ README.md | 7 +- __tests__/Compare-WorkSheet.tests.ps1 | 15 ++-- dashboard.xlsx | Bin 3526 -> 0 bytes test1.xlsx | Bin 9494 -> 0 bytes testTable.xlsx | Bin 10418 -> 0 bytes 10 files changed, 118 insertions(+), 59 deletions(-) delete mode 100644 Examples/Charts/Tools.xlsx delete mode 100644 Examples/PivotTableFilters/testPivot.xlsx delete mode 100644 dashboard.xlsx delete mode 100644 test1.xlsx delete mode 100644 testTable.xlsx diff --git a/Examples/Charts/Tools.xlsx b/Examples/Charts/Tools.xlsx deleted file mode 100644 index 714f93c18d60338574e6dcc211397b8eca337dcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7296 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 diff --git a/Examples/PivotTableFilters/testPivot.xlsx b/Examples/PivotTableFilters/testPivot.xlsx deleted file mode 100644 index 23da3b1e836d70998e148f704e36d225b7a543b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6013 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 diff --git a/test1.xlsx b/test1.xlsx deleted file mode 100644 index 56a5a9ea1df4f2da88bfb98bb3c9ed9b40371275..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9494 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 S699k diff --git a/testTable.xlsx b/testTable.xlsx deleted file mode 100644 index 44f663bba17d1dfca106554dcb3912e7b9e63c0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10418 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 From 892ba5d73f07f03837efd7750c220072cdebb49a Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 5 Jul 2018 17:34:12 +0100 Subject: [PATCH 5/6] 5 July See readme.md --- Export-Excel.ps1 | 213 +++++----- README.md | 9 +- __tests__/Export-Excel.Tests.ps1 | 694 +++++++++++++++++++++++++++++++ 3 files changed, 807 insertions(+), 109 deletions(-) create mode 100644 __tests__/Export-Excel.Tests.ps1 diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index f236a4e..495a41f 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -356,7 +356,7 @@ [Int]$TitleSize = 22, [System.Drawing.Color]$TitleBackgroundColor, [Switch]$IncludePivotTable, - [String]$PivotTableName, + [String]$PivotTableName, [String[]]$PivotRows, [String[]]$PivotColumns, $PivotData, @@ -458,17 +458,10 @@ #Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as value" break } - - {(($NoNumberConversion) -and ($NoNumberConversion -contains $Name)) -or - ($NoNumberConversion -eq '*')} { - #Save text without it to converting to number - $TargetCell.Value = $_ - #Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' unconverted" - break - } {($_ -is [String]) -and ($_[0] -eq '=')} { #region Save an Excel formula $TargetCell.Formula = $_ + if ($setNumformat) {$targetCell.Style.Numberformat.Format = $Numberformat } #Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as formula" break } @@ -481,12 +474,18 @@ #Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($_.AbsoluteUri)' as Hyperlink" break } - + {(($NoNumberConversion) -and ($NoNumberConversion -contains $Name)) -or + ($NoNumberConversion -eq '*')} { + #Save text without it to converting to number + $TargetCell.Value = $_ + #Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' unconverted" + break + } Default { #Save a value as a number if possible $number = $null - if ([Double]::TryParse( $_ , [ref]$number)) { - #was [Double]::TryParse([String]$_, [System.Globalization.NumberStyles]::Any,[System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number)) { + if ( [Double]::TryParse([String]$_, [System.Globalization.NumberStyles]::Any, [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number)) { + # as simpler version using [Double]::TryParse( $_ , [ref]$number)) was found to cause problems reverted back to the longer version $TargetCell.Value = $number if ($setNumformat) {$targetCell.Style.Numberformat.Format = $Numberformat } #Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' as number converted from '$_' with format '$Numberformat'" @@ -629,6 +628,7 @@ $ColumnIndex += 1 #endregion } + $ColumnIndex -= 1 # column index will be the last column whether isDataTypeValueType was true or false } } Catch { @@ -638,6 +638,20 @@ } End { + if ($firstTimeThru) { + $LastRow = $ws.Dimension.End.Row + $LastCol = $ws.Dimension.End.Column + $endAddress = $ws.Dimension.End.Address + } + else { + $LastRow = $Row + $LastCol = $ColumnIndex + $endAddress = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$LastRow]C[$LastCol]", 0, 0) + } + $startAddress = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$StartRow]C[$StartColumn]", 0, 0) + $dataRange = "{0}:{1}" -f $startAddress, $endAddress + + Write-Debug "Data Range '$dataRange'" if ($AutoNameRange) { Try { if (-not $script:header) { @@ -657,10 +671,10 @@ #, but start.column is the first populated one and .Columns is the count of populated ones. # if we have 5 columns from 3 to 8, headers are numbered 0..4, so that is in the for loop and used for getting the name... # but we have to add the start column on when referencing positions - foreach ($c in 0..($ws.Dimension.Columns - 1)) { + foreach ($c in 0..($LastCol - $StartColumn)) { $targetRangeName = $script:Header[$c] -replace '\W' , '_' $targetColumn = $c + $StartColumn - $theRange = $ws.Cells[$targetRow, $targetColumn, $ws.Dimension.End.Row , $targetColumn ] + $theRange = $ws.Cells[$targetRow, $targetColumn, $LastRow , $targetColumn ] if ($ws.names[$targetRangeName]) { $ws.names[$targetRangeName].Address = $theRange.FullAddressAbsolute } else {$ws.Names.Add($targetRangeName, $theRange) | Out-Null } @@ -671,87 +685,39 @@ } Catch {Write-Warning -Message "Failed adding named ranges to worksheet '$WorkSheetname': $_" } } - try { - if ($Title) { - $startAddress = $ws.Dimension.Start.address -replace "$($ws.Dimension.Start.row)`$", "$($ws.Dimension.Start.row + 1)" - } - else { - $startAddress = $ws.Dimension.Start.Address - } - - $dataRange = "{0}:{1}" -f $startAddress, $ws.Dimension.End.Address - - Write-Debug "Data Range '$dataRange'" - - if (-not [String]::IsNullOrEmpty($RangeName)) { + + if ($RangeName) { + try { if ($RangeName -match "\W") { Write-Warning -Message "At least one character in $RangeName is illegal in a range name and will be replaced with '_' . " $RangeName = $RangeName -replace '\W', '_' } #If named range exists, update it, else create it if ($ws.Names[$RangeName]) { $ws.Names[$rangename].Address = $ws.Cells[$dataRange].FullAddressAbsolute } - else {$ws.Names.Add($RangeName, $ws.Cells[$0ange]) | Out-Null } + else {$ws.Names.Add($RangeName, $ws.Cells[$dataRange]) | Out-Null } } + Catch { Write-Warning -Message "Failed adding range '$RangeName' to worksheet '$WorkSheetname': $_" } } - Catch { Write-Warning -Message "Failed adding range '$RangeName' to worksheet '$WorkSheetname': $_" } - if (-not [String]::IsNullOrEmpty($TableName)) { - try { - $csr = $StartRow - $csc = $StartColumn - $cer = $ws.Dimension.End.Row - $cec = $ws.Dimension.End.Column # was $script:Header.Count + if ($TableName) { + try { if ($TableName -match "\W") { Write-Warning -Message "At least one character in $TableName is illegal in a table name and will be replaced with '_' . " $TableName = $TableName -replace '\W', '_' } - $targetRange = $ws.Cells[$csr, $csc, $cer, $cec] #if the table exists, update it. if ($ws.Tables[$TableName]) { - $ws.Tables[$TableName].TableXml.table.ref = $targetRange.Address + $ws.Tables[$TableName].TableXml.table.ref = $dataRange $ws.Tables[$TableName].TableStyle = $TableStyle } else { - $tbl = $ws.Tables.Add($targetRange, $TableName) + $tbl = $ws.Tables.Add($ws.Cells[$dataRange], $TableName) $tbl.TableStyle = $TableStyle } Write-Verbose -Message "Defined table '$TableName' at $($targetRange.Address)" } catch {Write-Warning -Message "Failed adding table '$TableName' to worksheet '$WorkSheetname': $_"} } - if ($PivotTableDefinition) { - foreach ($item in $PivotTableDefinition.GetEnumerator()) { - $params = $item.value - if ($params.keys -notcontains "SourceRange" -and - ($params.Keys -notcontains "SourceWorkSheet" -or $params.SourceWorkSheet -eq $WorkSheetname)) {$params.SourceRange = $dataRange} - if ($params.Keys -notcontains "SourceWorkSheet") {$params.SourceWorkSheet = $ws } - if ($params.Keys -notcontains "NoTotalsInPivot" -and $NoTotalsInPivot ) {$params.NoTotalsInPivot = $true} - if ($params.Keys -notcontains "PivotDataToColumn" -and $PivotDataToColumn) {$params.PivotDataToColumn = $true} - - Add-PivotTable -ExcelPackage $pkg -PivotTableName $item.key @Params - } - } - if ($IncludePivotTable -or $IncludePivotChart) { - $params = @{ - "SourceRange" = $dataRange - } - if ($PivotTableName) {$params.PivotTableName = $PivotTableName} - else {$params.PivotTableName = $WorkSheetname + 'PivotTable'} - if ($PivotFilter) {$params.PivotFilter = $PivotFilter} - if ($PivotRows) {$params.PivotRows = $PivotRows} - if ($PivotColumns) {$Params.PivotColumns = $PivotColumns} - if ($PivotData) {$Params.PivotData = $PivotData} - if ($NoTotalsInPivot) {$params.NoTotalsInPivot = $true} - if ($PivotDataToColumn) {$params.PivotDataToColumn = $true} - if ($IncludePivotChart) { - $params.IncludePivotChart = $true - $Params.ChartType = $ChartType - if ($ShowCategory) {$params.ShowCategory = $true} - if ($ShowPercent) {$params.ShowPercent = $true} - if ($NoLegend) {$params.NoLegend = $true} - } - Add-PivotTable -ExcelPackage $pkg -SourceWorkSheet $ws @params - } if ($AutoFilter) { try { @@ -761,6 +727,40 @@ catch {Write-Warning -Message "Failed adding autofilter to worksheet '$WorkSheetname': $_"} } + if ($PivotTableDefinition) { + foreach ($item in $PivotTableDefinition.GetEnumerator()) { + $params = $item.value + if ($params.keys -notcontains "SourceRange" -and + ($params.Keys -notcontains "SourceWorkSheet" -or $params.SourceWorkSheet -eq $WorkSheetname)) {$params.SourceRange = $dataRange} + if ($params.Keys -notcontains "SourceWorkSheet") {$params.SourceWorkSheet = $ws } + if ($params.Keys -notcontains "NoTotalsInPivot" -and $NoTotalsInPivot ) {$params.NoTotalsInPivot = $true} + if ($params.Keys -notcontains "PivotDataToColumn" -and $PivotDataToColumn) {$params.PivotDataToColumn = $true} + + Add-PivotTable -ExcelPackage $pkg -PivotTableName $item.key @Params + } + } + if ($IncludePivotTable -or $IncludePivotChart) { + $params = @{ + "SourceRange" = $dataRange + } + if ($PivotTableName) {$params.PivotTableName = $PivotTableName} + else {$params.PivotTableName = $WorkSheetname + 'PivotTable'} + if ($PivotFilter) {$params.PivotFilter = $PivotFilter} + if ($PivotRows) {$params.PivotRows = $PivotRows} + if ($PivotColumns) {$Params.PivotColumns = $PivotColumns} + if ($PivotData) {$Params.PivotData = $PivotData} + if ($NoTotalsInPivot) {$params.NoTotalsInPivot = $true} + if ($PivotDataToColumn) {$params.PivotDataToColumn = $true} + if ($IncludePivotChart) { + $params.IncludePivotChart = $true + $Params.ChartType = $ChartType + if ($ShowCategory) {$params.ShowCategory = $true} + if ($ShowPercent) {$params.ShowPercent = $true} + if ($NoLegend) {$params.NoLegend = $true} + } + Add-PivotTable -ExcelPackage $pkg -SourceWorkSheet $ws @params + } + try { if ($FreezeTopRow) { $ws.View.FreezePanes(2, 1) @@ -823,7 +823,7 @@ foreach ($chartDef in $ExcelChartDefinition) { $params = @{} $chartDef.PSObject.Properties | ForEach-Object {if ($_.value -ne $null) {$params[$_.name] = $_.value}} - Add-ExcelChart @params + Add-ExcelChart -Worksheet $ws @params } foreach ($ct in $ConditionalText) { @@ -841,8 +841,8 @@ if ($CellStyleSB) { try { - $TotalRows = $ws.Dimension.Rows - $LastColumn = $ws.Dimension.Address -replace "^.*:(\w*)\d+$" , '$1' + $TotalRows = $ws.Dimension.Rows + $LastColumn = $ws.Dimension.Address -replace "^.*:(\w*)\d+$" , '$1' & $CellStyleSB $ws $TotalRows $LastColumn } catch {Write-Warning -Message "Failed processing CellStyleSB in worksheet '$WorkSheetname': $_"} @@ -1172,12 +1172,12 @@ function Add-ExcelChart { [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = "ColumnStacked", $XRange, $YRange, - [int]$Width = 500, - [int]$Height = 350, - [int]$Row = 0, - [int]$RowOffSetPixels = 10, - [int]$Column = 6, - [int]$ColumnOffSetPixels = 5, + [int]$Width = 500, + [int]$Height = 350, + [int]$Row = 0, + [int]$RowOffSetPixels = 10, + [int]$Column = 6, + [int]$ColumnOffSetPixels = 5, [OfficeOpenXml.Drawing.Chart.eLegendPosition]$LegendPostion, $LegendSize, [Switch]$legendBold, @@ -1191,19 +1191,19 @@ function Add-ExcelChart { [Switch]$XAxisTitleBold, $XAxisTitleSize , [string]$XAxisNumberformat, - [double]$XMajorUnit, - [double]$XMinorUnit, - [double]$XMaxValue, - [double]$XMinValue, + $XMajorUnit, + $XMinorUnit, + $XMaxValue, + $XMinValue, [OfficeOpenXml.Drawing.Chart.eAxisPosition]$XAxisPosition , [String]$YAxisTitleText, [Switch]$YAxisTitleBold, $YAxisTitleSize, [string]$YAxisNumberformat, - [double]$YMajorUnit, - [double]$YMinorUnit, - [double]$YMaxValue, - [double]$YMinValue, + $YMajorUnit, + $YMinorUnit, + $YMaxValue, + $YMinValue, [OfficeOpenXml.Drawing.Chart.eAxisPosition]$YAxisPosition ) try { $ChartName = 'Chart' + (Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.', '' @@ -1213,36 +1213,35 @@ function Add-ExcelChart { if ($TitleSize) {$chart.Title.Font.Size = $TitleSize} if ($NoLegend) { $chart.Legend.Remove() } - else { + else { if ($LegendPostion) {$Chart.Legend.Position = $LegendPostion} if ($LegendSize) {$chart.Legend.Font.Size = $LegendSize} - if ($legendBold) {$chart.Legend.Font.Bold = $legendBold} + if ($legendBold) {$chart.Legend.Font.Bold = $true} } - if ($XAxisTitleText) { + if ($XAxisTitleText) { $chart.XAxis.Title.Text = $XAxisTitleText - if ($XAxisTitleBold) {$chart.XAxis.Title.Font.Bold = $true} - if ($XAxisTitleSize) {$chart.XAxis.Title.Font.Size = $XAxisTitleSize} + if ($XAxisTitleBold) {$chart.XAxis.Title.Font.Bold = $true} + if ($XAxisTitleSize) {$chart.XAxis.Title.Font.Size = $XAxisTitleSize} } - if ($XAxisPosition) {$chart.XAxis.AxisPosition = $XAxisPosition} - if ($XMajorUnit) {$chart.XAxis.MajorUnit = $XMajorUnit} - if ($XMinorUnit) {$chart.XAxis.MinorUnit = $XMinorUnit} - if ($XMinValue) {$chart.XAxis.MinValue = $XMinValue} - if ($XMaxValue) {$chart.XAxis.MaxValue = $XMaxValue} - if ($XAxisNumberformat) {$chart.XAxis.Format = $XAxisNumberformat} + if ($XAxisPosition) {$chart.XAxis.AxisPosition = $XAxisPosition} + if ($XMajorUnit) {$chart.XAxis.MajorUnit = $XMajorUnit} + if ($XMinorUnit) {$chart.XAxis.MinorUnit = $XMinorUnit} + if ($XMinValue -ne $null) {$chart.XAxis.MinValue = $XMinValue} + if ($XMaxValue -ne $null) {$chart.XAxis.MaxValue = $XMaxValue} + if ($XAxisNumberformat) {$chart.XAxis.Format = $XAxisNumberformat} - if ($YAxisTitleText) { + if ($YAxisTitleText) { $chart.YAxis.Title.Text = $YAxisTitleText if ($YAxisTitleBold) {$chart.YAxis.Title.Font.Bold = $true} if ($YAxisTitleSize) {$chart.YAxis.Title.Font.Size = $YAxisTitleSize} } - if ($YAxisPosition) {$chart.YAxis.AxisPosition = $YAxisPosition} - if ($YMajorUnit) {$chart.YAxis.MajorUnit = $YMajorUnit} - if ($YMinorUnit) {$chart.YAxis.MinorUnit = $YMinorUnit} - if ($YMinValue) {$chart.YAxis.MinValue = $YMinValue} - if ($YMaxValue) {$chart.YAxis.MaxValue = $YMaxValue} - if ($YAxisNumberformat) {$chart.YAxis.Format = $YAxisNumberformat} - + if ($YAxisPosition) {$chart.YAxis.AxisPosition = $YAxisPosition} + if ($YMajorUnit) {$chart.YAxis.MajorUnit = $YMajorUnit} + if ($YMinorUnit) {$chart.YAxis.MinorUnit = $YMinorUnit} + if ($YMinValue-ne $null) {$chart.YAxis.MinValue = $YMinValue} + if ($YMaxValue-ne $null) {$chart.YAxis.MaxValue = $YMaxValue} + if ($YAxisNumberformat) {$chart.YAxis.Format = $YAxisNumberformat} if ($chart.Datalabel -ne $null) { $chart.Datalabel.ShowCategory = [boolean]$ShowCategory $chart.Datalabel.ShowPercent = [boolean]$ShowPercent diff --git a/README.md b/README.md index 60c58dc..45ecff4 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,16 @@ To install to your personal modules folder (e.g. ~\Documents\WindowsPowerShell\M iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfinke/ImportExcel/master/Install.ps1') ``` -# What's new to 4th July 18 +# What's new to 5th July 18 - Moved chart creatation into its own function (Add-Excel chart) within Export-Excel.ps1. Renamed New-Excelchart to New-ExcelChartDefinition to make it clearer that it is not making anything in the workbook (but for compatiblity put an alias of New-ExcelChart in so existing code does not break). Found -Header does nothing, so removed it. -- Fixed a bug introduced into Compare-Worksheet by the change descibed in the June changes below, this meant the font color was only being set in one sheet, when a row was changed. Also found that the PowerShell ISE and shell return Compare-Object resuls in different sequences which broke some tests. Applied a sort to ensure things are in a predictable order. +- Added paramters for managing Axes and legend +- Fixed a bug introduced into Compare-Worksheet by the change descibed in the June changes below, this meant the font color was only being set in one sheet, when a row was changed. Also found that the PowerShell ISE and shell return Compare-Object resuls in different sequences which broke some tests. Applied a sort to ensure things are in a predictable order. (#375) +- Fixed some bad code which had been checked-in in-error and caused adding charts to break. (This was not seen outside Github #377) - Added chart tests to Export-Excel.tests.ps1. - Removed (2) calls to Get-ExcelColumnName +- Fixed an issue in Export-Excel where formulas were inserted as strings if "NoNumberConversion" is applied (#374), and made sure formatting is applied to formula cells +- Reverted the [double]::tryParse in export excel to the previous way, as the shorter way, although quicker was not behaving correctly with with the number formats in certain regions. (also #374) +- Changed Table, Range and AutoRangeNames to apply to whole data area if no data has been inserted OR to inserted data only if it has. (#376) # New in June 18 - New commands - Diff , Merge and Join diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 new file mode 100644 index 0000000..14d3765 --- /dev/null +++ b/__tests__/Export-Excel.Tests.ps1 @@ -0,0 +1,694 @@ +#Requires -Modules Pester + +# $here = Split-Path -Parent $MyInvocation.MyCommand.Path +# Import-Module $here -Force -Verbose +Import-Module $PSScriptRoot\..\ImportExcel.psd1 -Force + +if (Get-process -Name Excel,xlim -ErrorAction SilentlyContinue) { Write-Warning -Message "You need to close Excel before running the tests." ; return} +Describe ExportExcel { + + Context "#Example 1 # Creates and opens a file with the right number of rows and columns" { + $path = "$env:TEMP\Test.xlsx" + Remove-item -Path $path -ErrorAction SilentlyContinue + $processes = Get-Process + $propertyNames = $Processes[0].psobject.properties.name + $rowcount = $Processes.Count + $Processes | Export-Excel $path -show + + it "Created a new file " { + Test-Path -Path $path -ErrorAction SilentlyContinue | Should be $true + } + + it "Started Excel to display the file " { + Get-process -Name Excel, xlim -ErrorAction SilentlyContinue | Should not benullorempty + } + + Start-Sleep -Seconds 5 ; + + #Open-ExcelPackage with -Create is tested in Export-Excel + #This is a test of using it with -KillExcel + #TODO Need to test opening pre-existing file with no -create switch (and graceful failure when file does not exist) somewhere else + $Excel = Open-ExcelPackage -Path $path -KillExcel + it "Killed Excel when Open-Excelpackage was told to " { + Get-process -Name Excel, xlim -ErrorAction SilentlyContinue | Should benullorempty + } + + it "Created 1 worksheet " { + $Excel.Workbook.Worksheets.count | Should be 1 + } + + $ws = $Excel.Workbook.Worksheets[1] + it "Created the worksheet with the expected name, number of rows and number of columns " { + $ws.Name | Should be "sheet1" + $ws.Dimension.Columns | Should be $propertyNames.Count + $ws.Dimension.Rows | Should be ($rowcount + 1) + } + + $headingNames = $ws.cells["1:1"].Value + it "Created the worksheet with the correct header names " { + foreach ($p in $propertyNames) { + $headingnames -contains $p | Should be $true + } + } + + it "Formatted the process StartTime field as 'local short date' " { + $STHeader = $ws.cells["1:1"].where( {$_.Value -eq "StartTime"})[0] + $STCell = $STHeader.Address -replace '1$', '2' + $ws.cells[$stcell].Style.Numberformat.NumFmtID | Should be 22 + } + + it "Formatted the process ID field as 'General' " { + $IDHeader = $ws.cells["1:1"].where( {$_.Value -eq "ID"})[0] + $IDCell = $IDHeader.Address -replace '1$', '2' + $ws.cells[$IDcell].Style.Numberformat.NumFmtID | Should be 0 + } + } + + Context " # NoAliasOrScriptPropeties -ExcludeProperty and -DisplayPropertySet work" { + $path = "$env:TEMP\Test.xlsx" + Remove-item -Path $path -ErrorAction SilentlyContinue + $processes = Get-Process + $propertyNames = $Processes[0].psobject.properties.where( {$_.MemberType -eq 'Property'}).name + $rowcount = $Processes.Count + #TestCreating a range with a name which needs illegal chars removing + $warnVar = $null + $Processes | Export-Excel $path -NoAliasOrScriptPropeties -RangeName "No Spaces" -WarningVariable warnvar -WarningAction SilentlyContinue + + $Excel = Open-ExcelPackage -Path $path + $ws = $Excel.Workbook.Worksheets[1] + it "Created a new file with alias & Script Properties removed. " { + $ws.Name | Should be "sheet1" + $ws.Dimension.Columns | Should be $propertyNames.Count + $ws.Dimension.Rows | Should be ($rowcount + 1 ) # +1 for the header. + } + it "Created a Range - even though the name given was invalid. " { + $ws.Names["No_spaces"] | Should not beNullOrEmpty + $ws.Names["No_spaces"].End.Column | Should be $propertyNames.Count + $ws.names["No_spaces"].End.Row | Should be ($rowcount + 1 ) # +1 for the header. + $warnVar.Count | Should be 1 + } + #This time use clearsheet instead of deleting the file + $Processes | Export-Excel $path -NoAliasOrScriptPropeties -ExcludeProperty SafeHandle, modules, MainModule, StartTime, Threads -ClearSheet + + $Excel = Open-ExcelPackage -Path $path + $ws = $Excel.Workbook.Worksheets[1] + it "Created a new file with a further 5 properties excluded and cleared the old sheet " { + $ws.Name | Should be "sheet1" + $ws.Dimension.Columns | Should be ($propertyNames.Count - 5) + $ws.Dimension.Rows | Should be ($rowcount + 1) # +1 for the header + } + + $propertyNames = $Processes[0].psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames + Remove-item -Path $path -ErrorAction SilentlyContinue + $Processes | Export-Excel $path -DisplayPropertySet + + $Excel = Open-ExcelPackage -Path $path + $ws = $Excel.Workbook.Worksheets[1] + it "Created a new file with just the members of the Display Property Set " { + $ws.Name | Should be "sheet1" + $ws.Dimension.Columns | Should be $propertyNames.Count + $ws.Dimension.Rows | Should be ($rowcount + 1) + } + } + + Context "#Example 2 # Exports a list of numbers and applies number format " { + + $path = "$env:TEMP\Test.xlsx" + Remove-item -Path $path -ErrorAction SilentlyContinue + #testing -ReturnRange switch + $returnedRange = Write-Output -1 668 34 777 860 -0.5 119 -0.1 234 788 | Export-Excel -NumberFormat '[Blue]$#,##0.00;[Red]-$#,##0.00' -Path $path -ReturnRange + it "Created a new file and returned the expected range " { + Test-Path -Path $path -ErrorAction SilentlyContinue | Should be $true + $returnedRange | Should be "A1:A10" + } + + $Excel = Open-ExcelPackage -Path $path + it "Created 1 worksheet " { + $Excel.Workbook.Worksheets.count | Should be 1 + } + + $ws = $Excel.Workbook.Worksheets[1] + it "Created the worksheet with the expected name, number of rows and number of columns " { + $ws.Name | Should be "sheet1" + $ws.Dimension.Columns | Should be 1 + $ws.Dimension.Rows | Should be 10 + } + + it "Set the default style for the sheet as expected " { + $ws.cells.Style.Numberformat.Format | Should be '[Blue]$#,##0.00;[Red]-$#,##0.00' + } + + it "Set the default style and value for Cell A1 as expected " { + $ws.cells[1, 1].Style.Numberformat.Format | Should be '[Blue]$#,##0.00;[Red]-$#,##0.00' + $ws.cells[1, 1].Value | Should be -1 + } + } + + Context "#Examples 3 & 4 # Setting cells for different data types Also added test for URI type" { + + $path = "$env:TEMP\Test.xlsx" + Remove-item -Path $path -ErrorAction SilentlyContinue + [PSCustOmobject][Ordered]@{ + Date = Get-Date + Formula1 = '=SUM(F2:G2)' + String1 = 'My String' + String2 = 'a' + IPAddress = '10.10.25.5' + Number1 = '07670' + Number2 = '0,26' + Number3 = '1.555,83' + Number4 = '1.2' + Number5 = '-31' + PhoneNr1 = '+32 44' + PhoneNr2 = '+32 4 4444 444' + PhoneNr3 = '+3244444444' + Link = [uri]"https://github.com/dfinke/ImportExcel" + } | Export-Excel -NoNumberConversion IPAddress, Number1 -Path $path + it "Created a new file " { + Test-Path -Path $path -ErrorAction SilentlyContinue | Should be $true + } + + $Excel = Open-ExcelPackage -Path $path + it "Created 1 worksheet " { + $Excel.Workbook.Worksheets.count | Should be 1 + } + + $ws = $Excel.Workbook.Worksheets[1] + it "Created the worksheet with the expected name, number of rows and number of columns " { + $ws.Name | Should be "sheet1" + $ws.Dimension.Columns | Should be 14 + $ws.Dimension.Rows | Should be 2 + } + + it "Set a date in Cell A2 " { + $ws.Cells[2, 1].Value.Gettype().name | Should be 'DateTime' + } + + it "Set a formula in Cell B2 " { + $ws.Cells[2, 2].Formula | Should be '=SUM(F2:G2)' + } + + it "Set strings in Cells E2 and F2 " { + $ws.Cells[2, 5].Value.GetType().name | Should be 'String' + $ws.Cells[2, 6].Value.GetType().name | Should be 'String' + } + + it "Set a number in Cell I2 " { + ($ws.Cells[2, 9].Value -is [valuetype] ) | Should be $true + } + + it "Set a hyperlink in Cell N2 " { + $ws.Cells[2, 14].Hyperlink | Should be "https://github.com/dfinke/ImportExcel" + } + } + + Context "# # Setting cells for different data types with -noHeader" { + + $path = "$env:TEMP\Test.xlsx" + Remove-item -Path $path -ErrorAction SilentlyContinue + [PSCustOmobject][Ordered]@{ + Date = Get-Date + Formula1 = '=SUM(F1:G1)' + String1 = 'My String' + String2 = 'a' + IPAddress = '10.10.25.5' + Number1 = '07670' + Number2 = '0,26' + Number3 = '1.555,83' + Number4 = '1.2' + Number5 = '-31' + PhoneNr1 = '+32 44' + PhoneNr2 = '+32 4 4444 444' + PhoneNr3 = '+3244444444' + Link = [uri]"https://github.com/dfinke/ImportExcel" + } | Export-Excel -NoNumberConversion IPAddress, Number1 -Path $path -NoHeader + it "Created a new file " { + Test-Path -Path $path -ErrorAction SilentlyContinue | Should be $true + } + + $Excel = Open-ExcelPackage -Path $path + it "Created 1 worksheet " { + $Excel.Workbook.Worksheets.count | Should be 1 + } + + $ws = $Excel.Workbook.Worksheets[1] + it "Created the worksheet with the expected name, number of rows and number of columns " { + $ws.Name | Should be "sheet1" + $ws.Dimension.Columns | Should be 14 + $ws.Dimension.Rows | Should be 1 + } + + it "Set a date in Cell A1 " { + $ws.Cells[1, 1].Value.Gettype().name | Should be 'DateTime' + } + + it "Set a formula in Cell B1 " { + $ws.Cells[1, 2].Formula | Should be '=SUM(F1:G1)' + } + + it "Set strings in Cells E1 and F1 " { + $ws.Cells[1, 5].Value.GetType().name | Should be 'String' + $ws.Cells[1, 6].Value.GetType().name | Should be 'String' + } + + it "Set a number in Cell I1 " { + ($ws.Cells[1, 9].Value -is [valuetype] ) | Should be $true + } + + it "Set a hyperlink in Cell N1 " { + $ws.Cells[1, 14].Hyperlink | Should be "https://github.com/dfinke/ImportExcel" + } + } + + Context "#Example 5 # Adding a single conditional format " { + ### TODO New-ConditionalText doesn't a lot of options in Add-ConditionalFormat. + # It would be good to pull the logic out of Export-Excel and have EE call Add-ConditionalFormat. + $ct = New-ConditionalText -ConditionalType GreaterThan 525 -ConditionalTextColor DarkRed -BackgroundColor LightPink + it "Created a Conditional format description " { + $ct.BackgroundColor -is [System.Drawing.Color] | Should be $true + $ct.ConditionalTextColor -is [System.Drawing.Color] | Should be $true + $ct.ConditionalType -in [enum]::GetNames( [OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType] ) | + Should be $true + } + + $path = "$env:TEMP\Test.xlsx" + Remove-item -Path $path -ErrorAction SilentlyContinue + Write-Output 489 668 299 777 860 151 119 497 234 788 | Export-Excel -Path $path -ConditionalText $ct + + it "Created a new file " { + Test-Path -Path $path -ErrorAction SilentlyContinue | Should be $true + } + + #ToDo need to test applying conitional formatting to a pre-existing worksheet + $Excel = Open-ExcelPackage -Path $path + $ws = $Excel.Workbook.Worksheets[1] + + it "Added one block of conditional formating for the data range " { + $ws.ConditionalFormatting.Count | Should be 1 + $ws.ConditionalFormatting[0].Address | Should be ($ws.Dimension.Address) + } + + $cf = $ws.ConditionalFormatting[0] + it "Set the conditional formatting properties correctly " { + $cf.Formula | Should be $ct.Text + $cf.Type.ToString() | Should be $ct.ConditionalType + #$cf.Style.Fill.BackgroundColor | Should be $ct.BackgroundColor + # $cf.Style.Font.Color | Should be $ct.ConditionalTextColor - have to compare r.g.b + + + } + } + + Context "#Example 6 # Adding multiple conditional formats using short form syntax. " { + #this is a test of adding more than one conditional block and using the minimal syntax for new-ConditionalText = + $path = "$env:TEMP\Test.xlsx" + Remove-item -Path $path -ErrorAction SilentlyContinue + + #Testing -Passthrough + $Excel = Get-Service | Select-Object Name, Status, DisplayName, ServiceName | + Export-Excel $path -PassThru -ConditionalText $( + New-ConditionalText Stop DarkRed LightPink + New-ConditionalText Running Blue Cyan + ) + $ws = $Excel.Workbook.Worksheets[1] + it "Added two blocks of conditional formating for the data range " { + $ws.ConditionalFormatting.Count | Should be 2 + $ws.ConditionalFormatting[0].Address | Should be ($ws.Dimension.Address) + $ws.ConditionalFormatting[1].Address | Should be ($ws.Dimension.Address) + } + it "Set the conditional formatting properties correctly " { + $ws.ConditionalFormatting[0].Text | Should be "Stop" + $ws.ConditionalFormatting[1].Text | Should be "Running" + $ws.ConditionalFormatting[0].Type | Should be "ContainsText" + $ws.ConditionalFormatting[1].Type | Should be "ContainsText" + #Add RGB Comparison + } + Close-ExcelPackage -ExcelPackage $Excel + } + + context "#Example 7 # Update-FirstObjectProperties works " { + $Array = @() + + $Obj1 = [PSCustomObject]@{ + Member1 = 'First' + Member2 = 'Second' + } + + $Obj2 = [PSCustomObject]@{ + Member1 = 'First' + Member2 = 'Second' + Member3 = 'Third' + } + + $Obj3 = [PSCustomObject]@{ + Member1 = 'First' + Member2 = 'Second' + Member3 = 'Third' + Member4 = 'Fourth' + } + + $Array = $Obj1, $Obj2, $Obj3 + $newarray = $Array | Update-FirstObjectProperties + it "Outputs as many objects as it input " { + $newarray.Count | Should be $Array.Count + } + it "Added properties to item 0 " { + $newarray[0].psobject.Properties.name.Count | Should be 4 + $newarray[0].Member1 | Should be 'First' + $newarray[0].Member2 | Should be 'Second' + $newarray[0].Member3 | Should beNullOrEmpty + $newarray[0].Member4 | Should beNullOrEmpty + } + } + + Context "#Examples 8 & 9 # Adding Pivot tables and charts from parameters" { + $path = "$env:TEMP\Test.xlsx" + #This time we are not deleting the XLSX file so this Should create a new, named, sheet. + $Excel = Get-Process | Select-Object -first 50 -Property Name, cpu, pm, handles, company | Export-Excel $path -WorkSheetname Processes -PassThru + #Testing -passthru and adding the Pivot as a second step. Want to save and re-open it ... + Export-Excel -ExcelPackage $Excel -WorkSheetname Processes -IncludePivotTable -PivotRows Company -PivotData PM + + $Excel = Open-ExcelPackage $path + $PTws = $Excel.Workbook.Worksheets["ProcessesPivotTable"] + $wCount = $Excel.Workbook.Worksheets.Count + it "Added the named sheet and pivot table to the workbook " { + $PTws | Should not beNullOrEmpty + $PTws.PivotTables.Count | Should be 1 + $Excel.Workbook.Worksheets["Processes"] | Should not beNullOrEmpty + $Excel.Workbook.Worksheets.Count | Should beGreaterThan 2 + $excel.Workbook.Worksheets["Processes"].Dimension.rows | Should be 51 #50 data + 1 header + } + $pt = $PTws.PivotTables[0] + it "Built the expected Pivot table " { + $pt.RowFields.Count | Should be 1 + $pt.RowFields[0].Name | Should be "Company" + $pt.DataFields.Count | Should be 1 + $pt.DataFields[0].Function | Should be "Count" + $pt.DataFields[0].Field.Name | Should be "PM" + $PTws.Drawings.Count | Should be 0 + } + #using the already open sheet add the pivot chart + $warnvar = $null + Export-Excel -ExcelPackage $Excel -WorkSheetname Processes -IncludePivotTable -PivotRows Company -PivotData PM -IncludePivotChart -ChartType PieExploded3D -WarningAction SilentlyContinue -WarningVariable warnvar + $Excel = Open-ExcelPackage $path + it "Added a chart to the pivot table without rebuilding " { + $ws = $Excel.Workbook.Worksheets["ProcessesPivotTable"] + $Excel.Workbook.Worksheets.Count | Should be $wCount + $ws.Drawings.count | Should be 1 + $ws.Drawings[0].ChartType.ToString() | Should be "PieExploded3D" + } + it "Generated a message on re-processing the Pivot table " { + $warnVar | Should not beNullOrEmpty + } + $warnVar = $null + Get-Process | Select-Object -Last 50 -Property Name, cpu, pm, handles, company | Export-Excel $path -WorkSheetname Processes -Append -IncludePivotTable -PivotRows Company -PivotData PM -IncludePivotChart -ChartType PieExploded3D -WarningAction SilentlyContinue -WarningVariable warnvar + $Excel = Open-ExcelPackage $path + $pt = $Excel.Workbook.Worksheets["ProcessesPivotTable"].PivotTables[0] + it "Appended to the Worksheet and Extended the Pivot table " { + $Excel.Workbook.Worksheets.Count | Should be $wCount + $excel.Workbook.Worksheets["Processes"].Dimension.rows | Should be 101 #appended 50 rows to the previous total + $pt.CacheDefinition.CacheDefinitionXml.pivotCacheDefinition.cacheSource.worksheetSource.ref | + Should be "A1:E101" + } + it "Generated a message on extending the Pivot table " { + $warnVar | Should not beNullOrEmpty + } + } + + Context " # Add-Worksheet inserted sheets, moved them correctly, and copied a sheet" { + $path = "$env:TEMP\Test.xlsx" + + $Excel = Open-ExcelPackage $path + #At this point Sheets Should be in the order Sheet1, Processes, ProcessesPivotTable + $null = Add-WorkSheet -ExcelPackage $Excel -WorkSheetname "Processes" -MoveToEnd # order now Sheet1, ProcessesPivotTable, Processes + $null = Add-WorkSheet -ExcelPackage $Excel -WorkSheetname "NewSheet" -MoveAfter "*" -CopySource ($excel.Workbook.Worksheets["Sheet1"]) # Now its NewSheet, Sheet1, ProcessesPivotTable, Processes + $null = Add-WorkSheet -ExcelPackage $Excel -WorkSheetname "Sheet1" -MoveAfter "*" # Now its NewSheet, ProcessesPivotTable, Processes, Sheet1 + $null = Add-WorkSheet -ExcelPackage $Excel -WorkSheetname "Another" -MoveToStart # Now its Another, NewSheet, ProcessesPivotTable, Processes, Sheet1 + $null = Add-WorkSheet -ExcelPackage $Excel -WorkSheetname "OneLast" -MoveBefore "ProcessesPivotTable" # Now its Another, NewSheet, Onelast, ProcessesPivotTable, Processes, Sheet1 + Close-ExcelPackage $Excel + + $Excel = Open-ExcelPackage $path + + it "Got the Sheets in the right order " { + $excel.Workbook.Worksheets[1].Name | Should be "Another" + $excel.Workbook.Worksheets[2].Name | Should be "NewSheet" + $excel.Workbook.Worksheets[3].Name | Should be "Onelast" + $excel.Workbook.Worksheets[4].Name | Should be "ProcessesPivotTable" + $excel.Workbook.Worksheets[5].Name | Should be "Processes" + $excel.Workbook.Worksheets[6].Name | Should be "Sheet1" + } + + it "Cloned 'Sheet1' to 'NewSheet' " { + $newWs = $excel.Workbook.Worksheets["NewSheet"] + $newWs.Dimension.Address | Should be ($excel.Workbook.Worksheets["Sheet1"].Dimension.Address) + $newWs.ConditionalFormatting.Count | Should be ($excel.Workbook.Worksheets["Sheet1"].ConditionalFormatting.Count) + $newWs.ConditionalFormatting[0].Address.Address | Should be ($excel.Workbook.Worksheets["Sheet1"].ConditionalFormatting[0].Address.Address) + $newWs.ConditionalFormatting[0].Formula | Should be ($excel.Workbook.Worksheets["Sheet1"].ConditionalFormatting[0].Formula) + } + + } + + Context " # Create and append with Start row and Start Column, inc ranges and Pivot table" { + $path = "$env:TEMP\Test.xlsx" + #Catch warning + $warnVar = $null + #Test Append with no existing sheet. Test adding a named pivot table from a command line parameter + get-process | Select-Object -first 10 -Property Name, cpu, pm, handles, company | export-excel -StartRow 3 -StartColumn 3 -AutoFilter -AutoNameRange -BoldTopRow -IncludePivotTable -PivotRows Company -PivotData PM -PivotTableName 'PTOffset' -Path $path -WorkSheetname withOffset -append + get-process | Select-Object -last 10 -Property Name, cpu, pm, handles, company | export-excel -StartRow 3 -StartColumn 3 -AutoFilter -AutoNameRange -BoldTopRow -IncludePivotTable -PivotRows Company -PivotData PM -PivotTableName 'PTOffset' -Path $path -WorkSheetname withOffset -append -WarningAction SilentlyContinue -WarningVariable warnvar + $Excel = Open-ExcelPackage $path + $dataWs = $Excel.Workbook.Worksheets["withOffset"] + $pt = $Excel.Workbook.Worksheets["PTOffset"].PivotTables[0] + it "Created and appended to a sheet offset from the top left corner " { + $dataWs.Cells[1, 1].Value | Should beNullOrEmpty + $dataWs.Cells[2, 2].Value | Should beNullOrEmpty + $dataWs.Cells[3, 3].Value | Should not beNullOrEmpty + $dataWs.Cells[3, 3].Style.Font.Bold | Should be $true + $dataWs.Dimension.End.Row | Should be 23 + $dataWs.names[0].end.row | Should be 23 + $dataWs.names[0].name | Should be 'Name' + $dataWs.names.Count | Should be 6 + $dataWs.cells[$dataws.Dimension].AutoFilter | Should be true + $pt.CacheDefinition.CacheDefinitionXml.pivotCacheDefinition.cacheSource.worksheetSource.ref | + Should be "C3:G23" + } + it "Generated a message on extending the Pivot table " { + $warnVar | Should not beNullOrEmpty + } + } + + Context "#Example 11 # Create and append with title, inc ranges and Pivot table" { + $path = "$env:TEMP\Test.xlsx" + $ptDef = [ordered]@{} + $ptDef += New-PivotTableDefinition -PivotTableName "PT1" -SourceWorkSheet 'Sheet1' -PivotRows "Status" -PivotData @{'Status' = 'Count'} -PivotFilter "StartType" -IncludePivotChart -ChartType BarClustered3D -ChartTitle "Services by status" -ChartHeight 512 -ChartWidth 768 -ChartRow 10 -ChartColumn 0 -NoLegend + $ptDef += New-PivotTableDefinition -PivotTableName "PT2" -SourceWorkSheet 'Sheet2' -PivotRows "Company" -PivotData @{'Company' = 'Count'} -IncludePivotChart -ChartType PieExploded3D -ShowPercent -WarningAction SilentlyContinue + + it "Built a pivot definition using New-PivotTableDefinition " { + $ptDef.PT1.SourceWorkSheet | Should be 'Sheet1' + $ptDef.PT1.PivotRows | Should be 'Status' + $ptDef.PT1.PivotData.Status | Should be 'Count' + $ptDef.PT1.PivotFilter | Should be 'StartType' + $ptDef.PT1.IncludePivotChart | Should be $true + $ptDef.PT1.ChartType.tostring() | Should be 'BarClustered3D' + } + Remove-Item -Path $path + #Catch warning + $warnvar = $null + Get-Service | Select-Object -Property Status, Name, DisplayName, StartType | Export-Excel -Path $path -AutoSize -TableName "All Services" -TableStyle Medium1 -WarningAction SilentlyContinue -WarningVariable warnvar + Get-Process | Select-Object -Property Name, Company, Handles, CPU, VM | Export-Excel -Path $path -AutoSize -WorkSheetname 'sheet2' -TableName "Processes" -TableStyle Light1 -Title "Processes" -TitleFillPattern Solid -TitleBackgroundColor AliceBlue -TitleBold -TitleSize 22 -PivotTableDefinition $ptDef + $Excel = Open-ExcelPackage $path + $ws1 = $Excel.Workbook.Worksheets["Sheet1"] + $ws2 = $Excel.Workbook.Worksheets["Sheet2"] + + + it "Set Column widths (with autosize) " { + $ws1.Column(2).Width | Should not be $ws1.DefaultColWidth + $ws2.Column(1).width | Should not be $ws2.DefaultColWidth + } + + it "Added tables to both sheets (handling illegal chars) and a title in sheet 2 " { + $warnvar.count | Should be 1 + $ws1.tables.Count | Should be 1 + $ws2.tables.Count | Should be 1 + $ws1.Tables[0].Address.Start.Row | Should be 1 + $ws2.Tables[0].Address.Start.Row | Should be 2 #Title in row 1 + $ws1.Tables[0].Address.End.Address | Should be $ws1.Dimension.End.Address + $ws2.Tables[0].Address.End.Address | Should be $ws2.Dimension.End.Address + $ws2.Tables[0].Name | Should be "Processes" + $ws2.Tables[0].StyleName | Should be "TableStyleLight1" + $ws2.Cells["A1"].Value | Should be "Processes" + $ws2.Cells["A1"].Style.Font.Bold | Should be $true + $ws2.Cells["A1"].Style.Font.Size | Should be 22 + $ws2.Cells["A1"].Style.Fill.PatternType.tostring() | Should be "solid" + $ws2.Cells["A1"].Style.Fill.BackgroundColor.Rgb | Should be "fff0f8ff" + } + + $ptsheet1 = $Excel.Workbook.Worksheets["Pt1"] + $ptsheet2 = $Excel.Workbook.Worksheets["Pt2"] + $PT1 = $ptsheet1.PivotTables[0] + $PT2 = $ptsheet2.PivotTables[0] + $PC1 = $ptsheet1.Drawings[0] + $PC2 = $ptsheet2.Drawings[0] + it "Created the correct pivot tables and charts from the definitions. " { + + $PT1.CacheDefinition.CacheDefinitionXml.pivotCacheDefinition.cacheSource.worksheetSource.ref | + Should be ("A1:" + $ws1.Dimension.End.Address) + $PT2.CacheDefinition.CacheDefinitionXml.pivotCacheDefinition.cacheSource.worksheetSource.ref | + Should be ("A2:" + $ws2.Dimension.End.Address) #Title in row 1 + + $pt1.PageFields[0].Name | Should be 'StartType' + $pt1.RowFields[0].Name | Should be 'Status' + $pt1.DataFields[0].Field.name | Should be 'Status' + $pt1.DataFields[0].Function | Should be 'Count' + $pc1.ChartType | Should be 'BarClustered3D' + $pc1.From.Column | Should be 0 #chart 1 at 0,10 chart 2 at 4,0 (default) + $pc2.From.Column | Should be 4 + $pc1.From.Row | Should be 10 + $pc2.From.Row | Should be 0 + $pc1.Legend.Font | Should beNullOrEmpty #Best check for legend removed. + $pc2.Legend.Font | Should not beNullOrEmpty + $pc1.Title.Text | Should be 'Services by status' + $pc2.DataLabel.ShowPercent | Should be $true + } + } + + Context "#Example 13 # Formatting and another way to do a pivot. " { + $path = "$env:TEMP\Test.xlsx" + Remove-Item $path + $excel = Get-Process | Select-Object -Property Name, Company, Handles, CPU, PM, NPM, WS | Export-Excel -Path $path -ClearSheet -WorkSheetname "Processes" -FreezeTopRowFirstColumn -PassThru + $sheet = $excel.Workbook.Worksheets["Processes"] + $sheet.Column(1) | Set-Format -Bold -AutoFit + $sheet.Column(2) | Set-Format -Width 29 -WrapText + $sheet.Column(3) | Set-Format -HorizontalAlignment Right -NFormat "#,###" + Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NFormat "#,###" + Set-Format -Address $sheet.Column(4) -HorizontalAlignment Right -NFormat "#,##0.0" -Bold + Set-Format -Address $sheet.Row(1) -Bold -HorizontalAlignment Center + Add-ConditionalFormatting -WorkSheet $sheet -Range "D2:D1048576" -DataBarColor Red + Add-ConditionalFormatting -WorkSheet $sheet -Range "G2:G1048576" -RuleType GreaterThan -ConditionValue "104857600" -ForeGroundColor Red + foreach ($c in 5..9) {Set-Format $sheet.Column($c) -AutoFit } + Add-PivotTable -PivotTableName "PT_Procs" -ExcelPackage $excel -SourceWorkSheet "Processes" -PivotRows Company -PivotData @{'Name' = 'Count'} -IncludePivotChart -ChartType ColumnClustered -NoLegend + Close-ExcelPackage $excel + + $excel = Open-ExcelPackage $path + $sheet = $excel.Workbook.Worksheets["Processes"] + + it "Applied the formating " { + $sheet | Should not beNullOrEmpty + $sheet.Column(1).wdith | Should not be $sheet.DefaultColWidth + $sheet.Column(7).wdith | Should not be $sheet.DefaultColWidth + $sheet.Column(1).style.font.bold | Should be $true + $sheet.Column(2).style.wraptext | Should be $true + $sheet.Column(2).width | Should be 29 + $sheet.Column(3).style.horizontalalignment | Should be 'right' + $sheet.Column(4).style.horizontalalignment | Should be 'right' + $sheet.Cells["A1"].Style.HorizontalAlignment | Should be 'Center' + $sheet.Cells['E2'].Style.HorizontalAlignment | Should be 'right' + $sheet.Cells['A1'].Style.Font.Bold | Should be $true + $sheet.Cells['D2'].Style.Font.Bold | Should be $true + $sheet.Cells['E2'].style.numberformat.format | Should be '#,###' + $sheet.Column(3).style.numberformat.format | Should be '#,###' + $sheet.Column(4).style.numberformat.format | Should be '#,##0.0' + $sheet.ConditionalFormatting.Count | Should be 2 + $sheet.ConditionalFormatting[0].type | Should be 'Databar' + $sheet.ConditionalFormatting[0].Color.name | Should be 'ffff0000' + $sheet.ConditionalFormatting[0].Address.Address | Should be 'D2:D1048576' + $sheet.ConditionalFormatting[1].type | Should be 'GreaterThan' + $sheet.ConditionalFormatting[1].Formula | Should be '104857600' + $sheet.ConditionalFormatting[1].Style.Font.Color.Color.Name | Should be 'ffff0000' + } + it "Froze the panes " { + $sheet.view.Panes.Count | Should be 3 + } + $ptsheet1 = $Excel.Workbook.Worksheets["Pt_procs"] + + it "Created the pivot table " { + $ptsheet1 | Should not beNullOrEmpty + $ptsheet1.PivotTables[0].DataFields[0].Field.Name | Should be "Name" + $ptsheet1.PivotTables[0].DataFields[0].Function | Should be "Count" + $ptsheet1.PivotTables[0].RowFields[0].Name | Should be "Company" + $ptsheet1.PivotTables[0].CacheDefinition.CacheDefinitionXml.pivotCacheDefinition.cacheSource.worksheetSource.ref | + Should be $sheet.Dimension.address + } + } + + Context " # Chart from MultiSeries.ps1 in the Examples\charts Directory" { + $path = "$env:TEMP\Test.xlsx" + Remove-Item -Path $path -ErrorAction SilentlyContinue + $data = invoke-sum (Get-Process) Company Handles, PM, VirtualMemorySize + it "used Invoke-Sum to create a data set " { + $data | Should not beNullOrEmpty + $data.count | Should beGreaterThan 1 + $data[1].Name | Should not beNullOrEmpty + $data[1].Handles | Should not beNullOrEmpty + $data[1].PM | Should not beNullOrEmpty + $data[1].VirtualMemorySize | Should not beNullOrEmpty + } + $c = New-ExcelChartDefinition -Title Stats -ChartType LineMarkersStacked -XRange "Processes[Name]" -YRange "Processes[PM]", "Processes[VirtualMemorySize]" -SeriesHeader 'PM', 'VMSize' + + it "Created the Excel chart definition " { + $c | Should not beNullOrEmpty + $c.ChartType.gettype().name | Should be "eChartType" + $c.ChartType.tostring() | Should be "LineMarkersStacked" + $c.yrange -is [array] | Should be $true + $c.yrange.count | Should be 2 + $c.yrange[0] | Should be "Processes[PM]" + $c.yrange[1] | Should be "Processes[VirtualMemorySize]" + $c.xrange | Should be "Processes[Name]" + $c.Title | Should be "Stats" + $c.Nolegend | Should not be $true + $c.ShowCategory | Should not be $true + $c.ShowPercent | Should not be $true + } + $data | Export-Excel $path -AutoSize -TableName Processes -ExcelChartDefinition $c + $excel = Open-ExcelPackage -Path $path + $drawings = $excel.Workbook.Worksheets[1].drawings + it "Used the Excel chart definition with Export-Excel " { + $drawings.count | Should be 1 + $drawings[0].ChartType | Should be "LineMarkersStacked" + $drawings[0].Series.count | Should be 2 + $drawings[0].Series[0].Series | Should be "'Sheet1'!Processes[PM]" + $drawings[0].Series[0].XSeries | Should be "'Sheet1'!Processes[Name]" + $drawings[0].Series[1].Series | Should be "'Sheet1'!Processes[VirtualMemorySize]" + $drawings[0].Series[1].XSeries | Should be "'Sheet1'!Processes[Name]" + $drawings[0].Title.text | Should be "Stats" + } + Close-ExcelPackage $excel + } + + Context " # variation of plot.ps1 from Examples Directory using Add chart outside ExportExcel" { + $path = "$env:TEMP\Test.xlsx" + $excel = 0..360 | ForEach-Object {[pscustomobject][ordered]@{x = $_; Sinx = "=Sin(Radians(x)) "}} | Export-Excel -AutoNameRange -Path $path -WorkSheetname SinX -ClearSheet -PassThru + Add-ExcelChart -Worksheet $excel.Workbook.Worksheets["Sinx"] -XRange "X" -YRange "Sinx" -Title "Graph of Sine X" -ChartType line -SeriesHeader "Sin(x)" -Column 2 -ColumnOffSetPixels 35 -TitleBold -TitleSize 14 -XAxisTitleText "Degrees" -XAxisTitleBold -XAxisTitleSize 12 -XMajorUnit 30 -XMinorUnit 10 -XMinValue 0 -XMaxValue 361 -Width 800 -YMinValue -1.25 -YMaxValue 1.25 -YMajorUnit 0.25 -YAxisNumberformat "0.00" -LegendPostion Bottom -LegendSize 8 -legendBold + $d = $excel.Workbook.Worksheets["Sinx"].Drawings[0] + It "Controled the axes and title and legend of the chart" { + $d.XAxis.MaxValue | Should be 361 + $d.XAxis.MajorUnit | Should be 30 + $d.XAxis.MinorUnit | Should be 10 + $d.XAxis.Title.Text | Should be "degrees" + $d.XAxis.Title.Font.bold | Should be $true + $d.XAxis.Title.Font.Size | Should be 12 + $d.XAxis.MajorUnit | Should be 30 + $d.XAxis.MinorUnit | Should be 10 + $d.XAxis.MinValue | Should be 0 + $d.XAxis.MaxValue | Should be 361 + $d.YAxis.Format | Should be "0.00" + $d.Title.Text | Should be "Graph of Sine X" + $d.Title.Font.Bold | Should be $true + $d.Title.Font.Size | Should be 14 + $d.yAxis.MajorUnit | Should be 0.25 + $d.yAxis.MaxValue | Should be 1.25 + $d.yaxis.MinValue | Should be -1.25 + $d.Legend.Position.ToString() | Should be "Bottom" + $d.Legend.Font.Bold | Should be $true + $d.Legend.Font.Size | Should be 8 + $d.ChartType.tostring() | Should be "line" + $d.From.Column | Should be 2 + } + Close-ExcelPackage -ExcelPackage $excel -nosave + } + + ## To do + ## More Charts , pivot options & other FreezePanes settings ? + ## Style script block + ## Rezip ? + +} From bd6a5a3fafd1390beeb1356421eca017790b83c3 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 5 Jul 2018 17:40:05 +0100 Subject: [PATCH 6/6] ConvertToSQLInsert out of sync --- ConvertFromExcelToSQLInsert.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ConvertFromExcelToSQLInsert.ps1 b/ConvertFromExcelToSQLInsert.ps1 index a1fd394..0f4708a 100644 --- a/ConvertFromExcelToSQLInsert.ps1 +++ b/ConvertFromExcelToSQLInsert.ps1 @@ -103,11 +103,13 @@ function ConvertFrom-ExcelToSQLInsert { [string[]]$Header, [switch]$NoHeader, [switch]$DataOnly, - [switch]$ConvertEmptyStringsToNull + [switch]$ConvertEmptyStringsToNull, + [switch]$UseMSSQLSyntax ) $null = $PSBoundParameters.Remove('TableName') $null = $PSBoundParameters.Remove('ConvertEmptyStringsToNull') + $null = $PSBoundParameters.Remove('UseMSSQLSyntax') $params = @{} + $PSBoundParameters @@ -115,6 +117,10 @@ function ConvertFrom-ExcelToSQLInsert { param($propertyNames, $record) $ColumnNames = "'" + ($PropertyNames -join "', '") + "'" + if($UseMSSQLSyntax) { + $ColumnNames = "[" + ($PropertyNames -join "], [") + "]" + } + $values = foreach ($propertyName in $PropertyNames) { if ($ConvertEmptyStringsToNull.IsPresent -and [string]::IsNullOrEmpty($record.$propertyName)) { 'NULL'