From f703addeb19e63638a6da6cc36b2bf4b2a128cf7 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 26 Apr 2018 16:19:59 +0100 Subject: [PATCH 1/9] Added Compare worksheet Added Compate-worksheet function (in its own PS1) Updated ColorCompletion.ps1 to hold argument completers for set row, and set column, and removed duplicates from formatting.ps1 Fixed case of Path param in Open-ExcelPackage Added comments about date format (m/d/yy is trapped and translated to local date) --- ColorCompletion.ps1 | 17 +++-- Export-Excel.ps1 | 2 +- ImportExcel.psm1 | 4 +- Open-ExcelPackage.ps1 | 2 +- Set-Column.ps1 | 2 +- Set-Row.ps1 | 2 +- compare-WorkSheet.ps1 | 155 ++++++++++++++++++++++++++++++++++++++++++ formatting.ps1 | 10 +-- 8 files changed, 176 insertions(+), 18 deletions(-) create mode 100644 compare-WorkSheet.ps1 diff --git a/ColorCompletion.ps1 b/ColorCompletion.ps1 index 3c24572..17f80aa 100644 --- a/ColorCompletion.ps1 +++ b/ColorCompletion.ps1 @@ -1,4 +1,4 @@ -Function ColorCompletion { +Function ColorCompletion { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) [System.Drawing.KnownColor].GetFields() | Where-Object {$_.IsStatic -and $_.name -like "$wordToComplete*" } | Sort-Object name | ForEach-Object {New-CompletionResult $_.name $_.name @@ -7,10 +7,19 @@ Function ColorCompletion { if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue) { Register-ArgumentCompleter -CommandName Export-Excel -ParameterName TitleBackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName DataBarColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Column -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Column -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Column -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Row -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Row -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Row -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion } \ No newline at end of file diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 5748505..1f6e2f3 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -444,7 +444,7 @@ {$_ -is [DateTime]} { #region Save a date with an international valid format $TargetCell.Value = $_ - $TargetCell.Style.Numberformat.Format = 'm/d/yy h:mm' + $TargetCell.Style.Numberformat.Format = 'm/d/yy h:mm' # This is not a custom format, but a preset recognized as date and localized. Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as date" break #endregion diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index d88e8ae..8b2b355 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -3,6 +3,8 @@ . $PSScriptRoot\AddConditionalFormatting.ps1 . $PSScriptRoot\Charting.ps1 . $PSScriptRoot\ColorCompletion.ps1 +. $PSScriptRoot\ConvertExcelToImageFile.ps1 +. $PSScriptRoot\Compare-WorkSheet.ps1 . $PSScriptRoot\ConvertFromExcelData.ps1 . $PSScriptRoot\ConvertFromExcelToSQLInsert.ps1 . $PSScriptRoot\ConvertToExcelXlsx.ps1 @@ -31,7 +33,7 @@ . $PSScriptRoot\SetFormat.ps1 . $PSScriptRoot\TrackingUtils.ps1 . $PSScriptRoot\Update-FirstObjectProperties.ps1 -. $PSScriptRoot\ConvertExcelToImageFile.ps1 + New-Alias -Name Use-ExcelData -Value "ConvertFrom-ExcelData" -Force diff --git a/Open-ExcelPackage.ps1 b/Open-ExcelPackage.ps1 index ccb4b3a..6789dfd 100644 --- a/Open-ExcelPackage.ps1 +++ b/Open-ExcelPackage.ps1 @@ -11,7 +11,7 @@ This will open the file at $xlPath, select sheet1 apply formatting to two blocks of the sheet and close the package #> [OutputType([OfficeOpenXml.ExcelPackage])] - Param ([Parameter(Mandatory=$true)]$path, + Param ([Parameter(Mandatory=$true)]$Path, [switch]$KillExcel) if($KillExcel) { diff --git a/Set-Column.ps1 b/Set-Column.ps1 index 32da5bb..5246a6a 100644 --- a/Set-Column.ps1 +++ b/Set-Column.ps1 @@ -108,7 +108,7 @@ else { $cellData = $Value} if ($cellData -match "^=") { $Worksheet.Cells[$Row, $Column].Formula = $cellData } else { $Worksheet.Cells[$Row, $Column].Value = $cellData } - if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } + if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } # This is not a custom format, but a preset recognized as date and localized. }} #region Apply formatting if ($Underline) { diff --git a/Set-Row.ps1 b/Set-Row.ps1 index e66850e..9250c1a 100644 --- a/Set-Row.ps1 +++ b/Set-Row.ps1 @@ -112,7 +112,7 @@ else{$cellData = $Value} if ($cellData -match "^=") { $Worksheet.Cells[$Row, $column].Formula = $cellData } else { $Worksheet.Cells[$Row, $Column].Value = $cellData } - if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } + if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } # This is not a custom format, but a preset recognized as date and localized. }} #region Apply formatting if ($Underline) { diff --git a/compare-WorkSheet.ps1 b/compare-WorkSheet.ps1 new file mode 100644 index 0000000..b05d3e1 --- /dev/null +++ b/compare-WorkSheet.ps1 @@ -0,0 +1,155 @@ +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 'Server1.xlsx' -Differencefile 'Server2.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 a match if the software was installed on a + different date or from a different place, so Excluding Install* removes InstallDate and InstallSource. The results will be presented as a table. + .Example + compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName Services -key Name -BackgroundColor lightGreen + This time two workbooks contain the result of redirecting Get-WmiObject -Class win32_service to Export-Excel + This command compares the "services" pages and highlights the rows in the spreadsheet files. + Here the -Differencefile and -Referencefile parameter switches are assumed + .Example + compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName Services -BackgroundColor lightGreen -fontColor Red -Show + This builds on the previous example: this time Where two rows in the services have the same name, this will also highlight the changed cells in red. + This example will open the Excel files and omits the -key parameter because "Name" will be assumed to the label for the key column + .Example + compare-WorkSheet 'Pester-tests.xlsx' 'Pester-tests.xlsx' -WorkSheetName 'Server1','Server2' -Property "full Description","Executed","Result" -Key "full Description" -FontColor Red -TabColor Yellow -Show + 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 only a limited set of columns. +#> +[cmdletbinding()] + Param( + #First file to compare + [parameter(Mandatory=$true)] + $Referencefile , + #Second file to compare + [parameter(Mandatory=$true)] + $Differencefile , + #Name(s) of worksheets to compare. + $WorkSheetName = "Sheet1", + #Name of a column which is unique and will be used to add a row to the DIFF object, default is "Name" + $Key = "Name" , + #Properties to include in the DIFF - supports wildcards, default is "*" + $Property = "*" , + #Properties to exclude from the the search - supports wildcards + $ExcludeProperty , + #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, + #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 + [Switch]$Show + ) + + $oneFile = ((Resolve-Path -Path $Referencefile).path -eq (Resolve-Path -Path $Differencefile).path) + + #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 } + + #If the paths are wrong, files are locked or the worksheet names are wrong we won't be able to continue + try { + $Sheet1 = Import-Excel -Path $Referencefile -WorksheetName $WorkSheet1 -ErrorAction stop + $Sheet2 = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 -ErrorAction stop + } + 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! + $Columns = @{} + $i = 65 ; foreach ($h in $headings) {$Columns[$h] = [char]($i ++) } + + #Make a list of properties headings using the Property (default "*") and ExcludeProperty parameters + $PropList = @() + foreach ($p in $Property) {$PropList += ($headings.where({$_ -like $p}) )} + foreach ($p in $ExcludeProperty) {$PropList = $PropList.where({$_ -notlike $p}) } + $PropList = $PropList | Select-Object -Unique + if (($headings -contains $key) -and ($PropList -notcontains $Key)) {$PropList += $Key} + if ($PropList.Count -eq 0) {Write-Warning -Message "No Columns are selected with -Property = '$Property' and -excludeProperty = '$ExcludeProperty'." ; return} + + #If we add the row numbes to data and include them in the diff, inserting a row will mean all subsequent rows are different so instead ... + #... build hash tables with the "key" column as the key and the row in the spreadsheet where it appears as the value. Row 1 is headers so the first data row is 2 + $rows1 = @{} ; + $rows2 = @{} ; + if ($PropList -contains $Key) { + $i = 2 ; foreach ($row in $Sheet1) {$rows1[$row.$key] = ($i ++) } + $i = 2 ; foreach ($row in $Sheet2) {$rows2[$row.$key] = ($i ++) } + } + else {Write-Warning -Message "Could not find a column '$key' to use as a key - DIFF rows will not have numbers."} + + #Do the comparison and add file,sheet and row to the result - these are prefixed with "_" to show they are added but the addition still might fail so make sure we have some DIFF + $diff = Compare-Object $Sheet1 $Sheet2 -Property $PropList + $diff = $diff | Select-Object -Property (@( + @{n="_Side"; e={$_.SideIndicator }} + @{n="_File"; e={if ($_.SideIndicator -eq '=>') {$Differencefile} else {$Referencefile } }} , + @{n="_Sheet"; e={if ($_.SideIndicator -eq '=>') {$worksheet2 } else {$worksheet1 } }} , + @{n='_Row'; e={if ($_.$key -and $_.SideIndicator -eq '=>') {$rows2[$_.$key]} elseif ($_.$key) {$rows1[$_.$key]} else { "" } }} + ) + $PropList) #| Sort-Object -Property row,file + + #if BackgroundColor was specified, set it on extra or extra or changed rows - but remember we we only have row numbers if we have a key + if (($PropList -contains $Key) -and $BackgroundColor) { + #Differences may only exist in one file. So gather the changes for each file; open the file, update each impacted row, save the file + $updates = $diff | 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} + 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 (($PropList -contains $Key) -and $FontColor) { + $updates = $diff | Group-object -Property $Key | where {$_.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].$p -ne $u.Group[1].$p ) { + Set-Format -WorkSheet $xl1.Workbook.Worksheets[$u.Group[0]._sheet] -Range ($Columns[$p] + $u.Group[0]._Row) -FontColor $FontColor + Set-Format -WorkSheet $xl2.Workbook.Worksheets[$u.Group[1]._sheet] -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()} + } + } + + if ($show) { + Start-Process -FilePath $Referencefile + if (-not $oneFile) { Start-Process -FilePath $Differencefile } + } + else {return $diff} +} \ No newline at end of file diff --git a/formatting.ps1 b/formatting.ps1 index efc703e..5d6d26a 100644 --- a/formatting.ps1 +++ b/formatting.ps1 @@ -224,12 +224,4 @@ Function ColorCompletion{ } } -if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue) { - Register-ArgumentCompleter -CommandName Export-Excel -ParameterName TitleBackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName DataBarColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion -} \ No newline at end of file + \ No newline at end of file From 47a61f5eb399d7e02a6348365d811ab4e2840101 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Fri, 27 Apr 2018 10:33:48 +0100 Subject: [PATCH 2/9] Added paramters to Compare worksheet Now has -Gridview, and supports startrow, headernames and NoHeader (as per import Excel) and ensures the headers don't clash. --- compare-WorkSheet.ps1 | 49 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/compare-WorkSheet.ps1 b/compare-WorkSheet.ps1 index b05d3e1..bc138fc 100644 --- a/compare-WorkSheet.ps1 +++ b/compare-WorkSheet.ps1 @@ -12,23 +12,28 @@ 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 'Server1.xlsx' -Differencefile 'Server2.xlsx' -WorkSheetName Products -key IdentifyingNumber -ExcludeProperty Install* | format-table + Compare-WorkSheet -Referencefile 'Server1.xlsx' -Differencefile 'Server2.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 a match if the software was installed on a different date or from a different place, so Excluding Install* removes InstallDate and InstallSource. The results will be presented as a table. .Example - compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName Services -key Name -BackgroundColor lightGreen + Compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName Services -key Name -BackgroundColor lightGreen This time two workbooks contain the result of redirecting Get-WmiObject -Class win32_service to Export-Excel This command compares the "services" pages and highlights the rows in the spreadsheet files. Here the -Differencefile and -Referencefile parameter switches are assumed .Example - compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName Services -BackgroundColor lightGreen -fontColor Red -Show + Compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName Services -BackgroundColor lightGreen -fontColor Red -Show This builds on the previous example: this time Where two rows in the services have the same name, this will also highlight the changed cells in red. This example will open the Excel files and omits the -key parameter because "Name" will be assumed to the label for the key column .Example - compare-WorkSheet 'Pester-tests.xlsx' 'Pester-tests.xlsx' -WorkSheetName 'Server1','Server2' -Property "full Description","Executed","Result" -Key "full Description" -FontColor Red -TabColor Yellow -Show + Compare-WorkSheet 'Pester-tests.xlsx' 'Pester-tests.xlsx' -WorkSheetName 'Server1','Server2' -Property "full Description","Executed","Result" -Key "full Description" -FontColor Red -TabColor Yellow -Show 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 only a limited set of columns. + .Example + Compare-WorkSheet - 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -GridView + The "General" page has a title and two unlabelled columns with the CPU, Memory, Domain, Disk and so on + So this version starts at row 2 to skip the tiltle and labels the first column "label" and the Second "Value"; the label acts as the key + and the result is display on using grid view. Note that grid view works best when the number of columns is small. #> [cmdletbinding()] Param( @@ -46,6 +51,14 @@ $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, #If specified, highlights the DIFF rows [System.Drawing.Color]$BackgroundColor, #If specified identifies the tabs which contain DIFF rows (ignored if -backgroundColor is omitted) @@ -53,11 +66,15 @@ #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 - [Switch]$Show + [Switch]$Show, + #If specified, tries to the show the DIFF in a gridview. (Works best with few columns) + [switch]$GridView ) $oneFile = ((Resolve-Path -Path $Referencefile).path -eq (Resolve-Path -Path $Differencefile).path) + if ($Key -eq "Name" -and $NoHeader) {$key = "p1"} + #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" @@ -68,9 +85,11 @@ else {Write-Warning -Message "You must provide either a single worksheet name or two names." ; return } #If the paths are wrong, files are locked or the worksheet names are wrong we won't be able to continue + $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 -ErrorAction stop - $Sheet2 = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 -ErrorAction stop + $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 } @@ -104,7 +123,7 @@ @{n="_File"; e={if ($_.SideIndicator -eq '=>') {$Differencefile} else {$Referencefile } }} , @{n="_Sheet"; e={if ($_.SideIndicator -eq '=>') {$worksheet2 } else {$worksheet1 } }} , @{n='_Row'; e={if ($_.$key -and $_.SideIndicator -eq '=>') {$rows2[$_.$key]} elseif ($_.$key) {$rows1[$_.$key]} else { "" } }} - ) + $PropList) #| Sort-Object -Property row,file + ) + $PropList) | Sort-Object -Property row,file #if BackgroundColor was specified, set it on extra or extra or changed rows - but remember we we only have row numbers if we have a key if (($PropList -contains $Key) -and $BackgroundColor) { @@ -147,9 +166,19 @@ } } - if ($show) { + if ($show) { Start-Process -FilePath $Referencefile if (-not $oneFile) { Start-Process -FilePath $Differencefile } } - else {return $diff} + elseif ($GridView) { + if ($StartRow) {$lastrow = $StartRow} else {$lastRow = 1} + $diff | Group-Object -Property $key | foreach { + $hash = [ordered]@{row = $lastRow; $key = $_.Name; } ; + foreach ($row IN $_.Group) { + if ($row._Side -eq "=>") {$lastRow = $hash.row = $row._Row } + foreach ($p in $proplist.Where({$_ -ne $key})) {$hash[($row._Side+$P)] =$row.$P} + } + [Pscustomobject]$hash } | Sort-Object -Property row| Update-FirstObjectProperties | Out-GridView -Title "Comparing $Referencefile::$worksheet1 (=>) with $Differencefile::$WorkSheet2 (<=)" + } + else {return $diff} } \ No newline at end of file From 904145ebae9ef47e19e65a738939f5f3a6d1bd34 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Fri, 27 Apr 2018 15:57:12 +0100 Subject: [PATCH 3/9] Default parameter set was missing --- compare-WorkSheet.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compare-WorkSheet.ps1 b/compare-WorkSheet.ps1 index bc138fc..89886c9 100644 --- a/compare-WorkSheet.ps1 +++ b/compare-WorkSheet.ps1 @@ -35,7 +35,7 @@ So this version starts at row 2 to skip the tiltle and labels the first column "label" and the Second "Value"; the label acts as the key and the result is display on using grid view. Note that grid view works best when the number of columns is small. #> -[cmdletbinding()] +[cmdletbinding(DefaultParameterSetName)] Param( #First file to compare [parameter(Mandatory=$true)] From 8f2dd982c5bc75065f5e2c9e1f6b2ceda0beb8e3 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Wed, 2 May 2018 12:38:13 +0100 Subject: [PATCH 4/9] Lots of changes to the compare-worksheet module --- ColorCompletion.ps1 | 33 ++++---- compare-WorkSheet.ps1 | 181 ++++++++++++++++++++++++------------------ 2 files changed, 120 insertions(+), 94 deletions(-) diff --git a/ColorCompletion.ps1 b/ColorCompletion.ps1 index 17f80aa..b771a4d 100644 --- a/ColorCompletion.ps1 +++ b/ColorCompletion.ps1 @@ -6,20 +6,21 @@ } if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue) { - Register-ArgumentCompleter -CommandName Export-Excel -ParameterName TitleBackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName DataBarColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Column -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Column -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Column -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Row -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Row -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Row -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Export-Excel -ParameterName TitleBackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName AllDataBackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName DataBarColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Column -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Column -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Column -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Row -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Row -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-Row -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion } \ No newline at end of file diff --git a/compare-WorkSheet.ps1 b/compare-WorkSheet.ps1 index 89886c9..c938f6f 100644 --- a/compare-WorkSheet.ps1 +++ b/compare-WorkSheet.ps1 @@ -1,4 +1,4 @@ -Function Compare-Worksheet { +Function Compare-WorkSheet { <# .Synopsis Compares two worksheets with the same name in different files. @@ -26,27 +26,30 @@ This builds on the previous example: this time Where two rows in the services have the same name, this will also highlight the changed cells in red. This example will open the Excel files and omits the -key parameter because "Name" will be assumed to the label for the key column .Example - Compare-WorkSheet 'Pester-tests.xlsx' 'Pester-tests.xlsx' -WorkSheetName 'Server1','Server2' -Property "full Description","Executed","Result" -Key "full Description" -FontColor Red -TabColor Yellow -Show + 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 only a limited set of columns. .Example - Compare-WorkSheet - 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -GridView - The "General" page has a title and two unlabelled columns with the CPU, Memory, Domain, Disk and so on - So this version starts at row 2 to skip the tiltle and labels the first column "label" and the Second "Value"; the label acts as the key - and the result is display on using grid view. Note that grid view works best when the number of columns is small. + Compare-WorkSheet 'Server1.xlsx' 'Server2.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)] + [parameter(Mandatory=$true,Position=0)] $Referencefile , #Second file to compare - [parameter(Mandatory=$true)] + [parameter(Mandatory=$true,Position=1)] $Differencefile , #Name(s) of worksheets to compare. $WorkSheetName = "Sheet1", - #Name of a column which is unique and will be used to add a row to the DIFF object, default is "Name" - $Key = "Name" , #Properties to include in the DIFF - supports wildcards, default is "*" $Property = "*" , #Properties to exclude from the the search - supports wildcards @@ -58,82 +61,93 @@ [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, + [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 - [Switch]$Show, - #If specified, tries to the show the DIFF in a gridview. (Works best with few columns) - [switch]$GridView + #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 ) - - $oneFile = ((Resolve-Path -Path $Referencefile).path -eq (Resolve-Path -Path $Differencefile).path) - - if ($Key -eq "Name" -and $NoHeader) {$key = "p1"} - + + #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} + 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 } - #If the paths are wrong, files are locked or the worksheet names are wrong we won't be able to continue $params= @{ ErrorAction = [System.Management.Automation.ActionPreference]::Stop } foreach ($p in @("HeaderName","NoHeader","StartRow")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}} - try { + 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 } - + 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! - $Columns = @{} - $i = 65 ; foreach ($h in $headings) {$Columns[$h] = [char]($i ++) } + $headings | ForEach-Object -Begin {$columns = @{} ; } -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} - #Make a list of properties headings using the Property (default "*") and ExcludeProperty parameters - $PropList = @() - foreach ($p in $Property) {$PropList += ($headings.where({$_ -like $p}) )} - foreach ($p in $ExcludeProperty) {$PropList = $PropList.where({$_ -notlike $p}) } - $PropList = $PropList | Select-Object -Unique - if (($headings -contains $key) -and ($PropList -notcontains $Key)) {$PropList += $Key} - if ($PropList.Count -eq 0) {Write-Warning -Message "No Columns are selected with -Property = '$Property' and -excludeProperty = '$ExcludeProperty'." ; return} - - #If we add the row numbes to data and include them in the diff, inserting a row will mean all subsequent rows are different so instead ... - #... build hash tables with the "key" column as the key and the row in the spreadsheet where it appears as the value. Row 1 is headers so the first data row is 2 - $rows1 = @{} ; - $rows2 = @{} ; - if ($PropList -contains $Key) { - $i = 2 ; foreach ($row in $Sheet1) {$rows1[$row.$key] = ($i ++) } - $i = 2 ; foreach ($row in $Sheet2) {$rows2[$row.$key] = ($i ++) } - } - else {Write-Warning -Message "Could not find a column '$key' to use as a key - DIFF rows will not have numbers."} - - #Do the comparison and add file,sheet and row to the result - these are prefixed with "_" to show they are added but the addition still might fail so make sure we have some DIFF - $diff = Compare-Object $Sheet1 $Sheet2 -Property $PropList - $diff = $diff | Select-Object -Property (@( - @{n="_Side"; e={$_.SideIndicator }} - @{n="_File"; e={if ($_.SideIndicator -eq '=>') {$Differencefile} else {$Referencefile } }} , - @{n="_Sheet"; e={if ($_.SideIndicator -eq '=>') {$worksheet2 } else {$worksheet1 } }} , - @{n='_Row'; e={if ($_.$key -and $_.SideIndicator -eq '=>') {$rows2[$_.$key]} elseif ($_.$key) {$rows1[$_.$key]} else { "" } }} - ) + $PropList) | Sort-Object -Property row,file - - #if BackgroundColor was specified, set it on extra or extra or changed rows - but remember we we only have row numbers if we have a key - if (($PropList -contains $Key) -and $BackgroundColor) { - #Differences may only exist in one file. So gather the changes for each file; open the file, update each impacted row, save the file - $updates = $diff | Group-object -Property "_File" + #Add RowNumber, Sheetname and file name to every row + $i = $startRow + 1 ; 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 = $startRow + 1 ; 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 + $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 ($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] + $ws = $xl.Workbook.Worksheets[$row._Sheet] $range = $ws.Dimension -replace "\d+",$row._row Set-Format -WorkSheet $ws -Range $range -BackgroundColor $BackgroundColor } @@ -145,16 +159,15 @@ $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 (($PropList -contains $Key) -and $FontColor) { - $updates = $diff | Group-object -Property $Key | where {$_.count -eq 2} + if ($FontColor -and ($propList -contains $Key) ) { + $updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property $Key | where {$_.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) { + foreach ($p in $propList) { if($u.Group[0].$p -ne $u.Group[1].$p ) { Set-Format -WorkSheet $xl1.Workbook.Worksheets[$u.Group[0]._sheet] -Range ($Columns[$p] + $u.Group[0]._Row) -FontColor $FontColor Set-Format -WorkSheet $xl2.Workbook.Worksheets[$u.Group[1]._sheet] -Range ($Columns[$p] + $u.Group[1]._Row) -FontColor $FontColor @@ -165,20 +178,32 @@ if (-not $oneFile) {$xl2.Save() ; $xl2.Stream.Close() ; $xl2.Dispose()} } } + elseif ($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 ($show) { - Start-Process -FilePath $Referencefile - if (-not $oneFile) { Start-Process -FilePath $Differencefile } + if ($show) { + Start-Process -FilePath $Referencefile + if (-not $oneFile) { Start-Process -FilePath $Differencefile } + } + elseif ($GridView) { + $Sheet2 | ForEach-Object -Begin {$Rowhash = @{} } -Process {$Rowhash[$_.$key] = $_._row } + if ($StartRow) { $rowCount1 = $StartRow} else { $rowCount1 = 1} + $rowCount2 = $null + $diff | Group-Object -Property $key | Sort-Object -Property @{e={($_.group | Measure-Object -Property _row -Maximum).maximum} } | ForEach-Object { + $hash = [ordered]@{"") {$rowCount1 = $hash["Row"] = $rowCount2 + $Hash[$key] = $keyVal + foreach ($p in $propList.Where({$_ -ne $key})) { + if ($row.SideIndicator -eq "==") {$hash[("=>$P")] = $hash[("<=$P")] =$row.$P} + else {$hash[($row.SideIndicator+$P)] =$row.$P} + } + } + [Pscustomobject]$hash } | Sort-Object -Property ")" } - elseif ($GridView) { - if ($StartRow) {$lastrow = $StartRow} else {$lastRow = 1} - $diff | Group-Object -Property $key | foreach { - $hash = [ordered]@{row = $lastRow; $key = $_.Name; } ; - foreach ($row IN $_.Group) { - if ($row._Side -eq "=>") {$lastRow = $hash.row = $row._Row } - foreach ($p in $proplist.Where({$_ -ne $key})) {$hash[($row._Side+$P)] =$row.$P} - } - [Pscustomobject]$hash } | Sort-Object -Property row| Update-FirstObjectProperties | Out-GridView -Title "Comparing $Referencefile::$worksheet1 (=>) with $Differencefile::$WorkSheet2 (<=)" - } - else {return $diff} + elseif (-not $PassThru) {return ($diff | Select-Object -Property (@(@{n="_Side";e={$_.SideIndicator}},"_File" ,"_Sheet","_Row") + $propList))} + if ( $PassThru) {return $diff } } \ No newline at end of file From 6dd928097c93f34b4d2a95ed35c0f2ec8b551b9e Mon Sep 17 00:00:00 2001 From: jhoneill Date: Wed, 2 May 2018 17:03:13 +0100 Subject: [PATCH 5/9] One last bug in compare, and fixed bug #310 in Set-Format --- SetFormat.ps1 | 4 ++-- compare-WorkSheet.ps1 | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/SetFormat.ps1 b/SetFormat.ps1 index 80d043b..3c24aa4 100644 --- a/SetFormat.ps1 +++ b/SetFormat.ps1 @@ -83,7 +83,7 @@ begin { #Allow Set-Format to take Worksheet and range parameters (like Add Contitional formatting) - convert them to an address if ($WorkSheet -and $Range) {$Address = $WorkSheet.Cells[$Range] } - } + } process { if ($Address -is [Array]) { @@ -150,7 +150,7 @@ if ($Address -is [OfficeOpenXml.ExcelRow] ) {$Address.Height = $Height } elseif ($Address -is [OfficeOpenXml.ExcelRange] ) { ($Address.Start.Row)..($Address.Start.Row + $Address.Rows) | - ForEach-Object {$ws.Row($_).Height = $Height } + ForEach-Object {$Address.WorkSheet.Row($_).Height = $Height } } else {Write-Warning -Message ("Can set the height of a row or a range but not a {0} object" -f ($Address.GetType().name)) } } diff --git a/compare-WorkSheet.ps1 b/compare-WorkSheet.ps1 index c938f6f..13e741d 100644 --- a/compare-WorkSheet.ps1 +++ b/compare-WorkSheet.ps1 @@ -132,7 +132,7 @@ Sort-Object -Property "_Row","File" #if BackgroundColor was specified, set it on extra or extra or changed rows - if ($BackgroundColor ) { + 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) { @@ -160,7 +160,7 @@ } } #if font colour was specified, set it on changed properties where the same key appears in both sheets. - if ($FontColor -and ($propList -contains $Key) ) { + if ($diff -and $FontColor -and ($propList -contains $Key) ) { $updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property $Key | where {$_.count -eq 2} if ($updates) { $XL1 = Open-ExcelPackage -path $Referencefile @@ -178,7 +178,7 @@ if (-not $oneFile) {$xl2.Save() ; $xl2.Stream.Close() ; $xl2.Dispose()} } } - elseif ($FontColor) {Write-Warning -Message "To match rows to set changed cells, you must specify -Key and it must match one of the included properties" } + 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 ($show) { Start-Process -FilePath $Referencefile From 61fc2b24b7bff912da42efb80e523b11b6ac9ca6 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 3 May 2018 15:46:30 +0100 Subject: [PATCH 6/9] Color completion wasn't working and reworked gridview for compare --- ColorCompletion.ps1 | 4 +- compare-WorkSheet.ps1 | 115 +++++++++++++++++++++++++++++------------- 2 files changed, 81 insertions(+), 38 deletions(-) diff --git a/ColorCompletion.ps1 b/ColorCompletion.ps1 index b771a4d..12b6f81 100644 --- a/ColorCompletion.ps1 +++ b/ColorCompletion.ps1 @@ -7,13 +7,13 @@ if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue) { Register-ArgumentCompleter -CommandName Export-Excel -ParameterName TitleBackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName AllDataBackgroundColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName DataBarColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName AllDataBackgroundColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName TabColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion diff --git a/compare-WorkSheet.ps1 b/compare-WorkSheet.ps1 index 13e741d..92a19c8 100644 --- a/compare-WorkSheet.ps1 +++ b/compare-WorkSheet.ps1 @@ -12,25 +12,30 @@ 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 'Server1.xlsx' -Differencefile 'Server2.xlsx' -WorkSheetName Products -key IdentifyingNumber -ExcludeProperty Install* | format-table + 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 a match if the software was installed on a - different date or from a different place, so Excluding Install* removes InstallDate and InstallSource. The results will be presented as a table. + 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 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName Services -key Name -BackgroundColor lightGreen + 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 - This command compares the "services" pages and highlights the rows in the spreadsheet files. - Here the -Differencefile and -Referencefile parameter switches are assumed + 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 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName Services -BackgroundColor lightGreen -fontColor Red -Show - This builds on the previous example: this time Where two rows in the services have the same name, this will also highlight the changed cells in red. - This example will open the Excel files and omits the -key parameter because "Name" will be assumed to the label for the key column + 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 only a limited set of columns. + machine name and time the test was run the command specifies a limited set of columns should be used. .Example - Compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -GridView -ExcludeDifferent + 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, @@ -119,10 +124,12 @@ 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 - $i = $startRow + 1 ; foreach ($row in $Sheet1) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++) + $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 = $startRow + 1 ; foreach ($row in $Sheet2) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++) + $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} @@ -132,7 +139,7 @@ Sort-Object -Property "_Row","File" #if BackgroundColor was specified, set it on extra or extra or changed rows - if ($diff -and $BackgroundColor) { + 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) { @@ -160,7 +167,7 @@ } } #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) ) { + if ($diff -and $FontColor -and ($propList -contains $Key) ) { $updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property $Key | where {$_.count -eq 2} if ($updates) { $XL1 = Open-ExcelPackage -path $Referencefile @@ -178,32 +185,68 @@ 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" } + 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 ($show) { + #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 (-not $oneFile) { Start-Process -FilePath $Differencefile } + if ($GridView) { Write-Warning -Message "-GridView is ignored when -Show is specified" } } - elseif ($GridView) { - $Sheet2 | ForEach-Object -Begin {$Rowhash = @{} } -Process {$Rowhash[$_.$key] = $_._row } - if ($StartRow) { $rowCount1 = $StartRow} else { $rowCount1 = 1} - $rowCount2 = $null - $diff | Group-Object -Property $key | Sort-Object -Property @{e={($_.group | Measure-Object -Property _row -Maximum).maximum} } | ForEach-Object { - $hash = [ordered]@{"") {$rowCount1 = $hash["Row"] = $rowCount2 - $Hash[$key] = $keyVal + 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 ($row.SideIndicator -eq "==") {$hash[("=>$P")] = $hash[("<=$P")] =$row.$P} - else {$hash[($row.SideIndicator+$P)] =$row.$P} + if ($result.SideIndicator -eq "==") {$hash[("=>$P")] = $hash[("<=$P")] =$result.$P} + else {$hash[($result.SideIndicator+$P)] =$result.$P} } } - [Pscustomobject]$hash } | Sort-Object -Property ")" + [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 (-not $PassThru) {return ($diff | Select-Object -Property (@(@{n="_Side";e={$_.SideIndicator}},"_File" ,"_Sheet","_Row") + $propList))} - if ( $PassThru) {return $diff } + 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 } } \ No newline at end of file From 1e0720f57b575ce2a64f7f280330dd9db064f9ce Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 3 May 2018 17:12:20 +0100 Subject: [PATCH 7/9] fat fingers ! --- compare-WorkSheet.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compare-WorkSheet.ps1 b/compare-WorkSheet.ps1 index 92a19c8..8ee571e 100644 --- a/compare-WorkSheet.ps1 +++ b/compare-WorkSheet.ps1 @@ -112,7 +112,7 @@ #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 = @{} ; } -Process {$Columns[$_] = [char]($i ++) } + $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"} From d99dd3bbae42697f2a45d39c3b94ae83031c8bba Mon Sep 17 00:00:00 2001 From: dfinke Date: Thu, 3 May 2018 14:02:01 -0400 Subject: [PATCH 8/9] Bump version and update install module --- ImportExcel.psd1 | 2 +- InstallModule.ps1 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index 807d835..169f557 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.13' +ModuleVersion = '4.0.14' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' diff --git a/InstallModule.ps1 b/InstallModule.ps1 index 6877963..6dce7a9 100644 --- a/InstallModule.ps1 +++ b/InstallModule.ps1 @@ -25,6 +25,7 @@ Begin { 'AddConditionalFormatting.ps1', 'Charting.ps1', 'ColorCompletion.ps1', + 'compare-Worksheet.ps1', 'ConvertFromExcelData.ps1', 'ConvertFromExcelToSQLInsert.ps1', 'ConvertExcelToImageFile.ps1', From 5d2520d8e367bb62895ef4417ae5f1cf1a14b215 Mon Sep 17 00:00:00 2001 From: dfinke Date: Thu, 3 May 2018 14:07:05 -0400 Subject: [PATCH 9/9] Updated --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ea39b56..aefb734 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new +- [James O'Neill](https://twitter.com/jamesoneill) added `Compare-Worksheet` + - Compares two worksheets with the same name in different files. + #### 4/22/2018 Thanks to the community yet again - [ili101](https://github.com/ili101) for fixes and features