From eec13674dcf566324ca04e78fecd7f5df575887a Mon Sep 17 00:00:00 2001 From: jhoneill Date: Fri, 6 Jul 2018 18:11:08 +0100 Subject: [PATCH 01/21] More tests; More parameters in Join Worksheet --- .../Join-Worksheet/Join-Worksheet.sample.ps1 | 43 + Export-Excel.Tests.ps1 | 620 ------------- Export-Excel.ps1 | 52 +- Join-Worksheet.ps1 | 40 +- README.md | 846 +----------------- ToDo.md | 3 + __tests__/Export-Excel.Tests.ps1 | 39 +- 7 files changed, 152 insertions(+), 1491 deletions(-) create mode 100644 Examples/Join-Worksheet/Join-Worksheet.sample.ps1 delete mode 100644 Export-Excel.Tests.ps1 diff --git a/Examples/Join-Worksheet/Join-Worksheet.sample.ps1 b/Examples/Join-Worksheet/Join-Worksheet.sample.ps1 new file mode 100644 index 0000000..ca81895 --- /dev/null +++ b/Examples/Join-Worksheet/Join-Worksheet.sample.ps1 @@ -0,0 +1,43 @@ +#Get rid of pre-exisiting sheet +$path = "$Env:TEMP\test.xlsx" +remove-item -Path $path -ErrorAction SilentlyContinue + +#Create simple pages for 3 stores with product ID, Product Name, quanity price and total + +@" +ID,Product,Quantity,Price,Total +12001,Nails,37,3.99,147.63 +12002,Hammer,5,12.10,60.5 +12003,Saw,12,15.37,184.44 +12010,Drill,20,8,160 +12011,Crowbar,7,23.48,164.36 +"@ | ConvertFrom-Csv| Export-Excel -Path $path -WorkSheetname Oxford + +@" +ID,Product,Quantity,Price,Total +12001,Nails,53,3.99,211.47 +12002,Hammer,6,12.10,72.60 +12003,Saw,10,15.37,153.70 +12010,Drill,10,8,80 +12012,Pliers,2,14.99,29.98 +"@ | ConvertFrom-Csv| Export-Excel -Path $path -WorkSheetname Abingdon + + +@" +ID,Product,Quantity,Price,Total +12001,Nails,20,3.99,79.80 +12002,Hammer,2,12.10,24.20 +12010,Drill,11,8,88 +12012,Pliers,3,14.99,44.97 +"@ | ConvertFrom-Csv| Export-Excel -Path $path -WorkSheetname Banbury + +#define a pivot table with a chart to show a sales by store, broken down by product +$ptdef = New-PivotTableDefinition -PivotTableName "Summary" -PivotRows "Store" -PivotColumns "Product" -PivotData @{"Total"="SUM"} -IncludePivotChart -ChartTitle "Sales Breakdown" -ChartType ColumnStacked -ChartColumn 10 + +#Join the 3 worksheets. +#Name the combined page "Total" and Name the column with the sheet names "store" (as the sheets 'Oxford','Abingdon' and 'Banbury' are the names of the stores +#Format the data as a table named "Summary", using the style "Light1", put the column headers in bold +#Put in a title and freeze to top of the sheet including title and colmun headings +#Add the Pivot table. +#Show the result +Join-Worksheet -Path $path -WorkSheetName "Total" -Clearsheet -FromLabel "Store" -TableName "Summary" -TableStyle Light1 -AutoSize -BoldTopRow -FreezePane 2,1 -Title "Store Sales Summary" -TitleBold -TitleSize 14 -PivotTableDefinition $ptdef -show diff --git a/Export-Excel.Tests.ps1 b/Export-Excel.Tests.ps1 deleted file mode 100644 index 1d63afc..0000000 --- a/Export-Excel.Tests.ps1 +++ /dev/null @@ -1,620 +0,0 @@ -#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 -Skip "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 - } - } - - ## To do - ## More pivot options & other FreezePanes settings ? - ## Charts - ## Style script block - ## Rezip ? - -} diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 495a41f..c5814b2 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -465,7 +465,7 @@ #Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as formula" break } - { $_ -is [Uri] } { + { [System.Uri]::IsWellFormedUriString($_ , [System.UriKind]::Absolute) } { # Save a hyperlink $TargetCell.Value = $_.AbsoluteUri $TargetCell.HyperLink = $_ @@ -484,7 +484,8 @@ Default { #Save a value as a number if possible $number = $null - if ( [Double]::TryParse([String]$_, [System.Globalization.NumberStyles]::Any, [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number)) { + if (($NoNumberConversion -NotContains $Name) -and ($NoNumberConversion -ne '*') -and + [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 } @@ -708,7 +709,7 @@ #if the table exists, update it. if ($ws.Tables[$TableName]) { $ws.Tables[$TableName].TableXml.table.ref = $dataRange - $ws.Tables[$TableName].TableStyle = $TableStyle + $ws.Tables[$TableName].TableStyle = $TableStyle } else { $tbl = $ws.Tables.Add($ws.Cells[$dataRange], $TableName) @@ -743,36 +744,35 @@ $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 ($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} + $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) - Write-Verbose -Message "Froze top row" - } - - if ($FreezeTopRowFirstColumn) { + #Allow single switch or two seperate ones. + if ($FreezeTopRowFirstColumn -or ($FreezeTopRow -and $FreezeFirstColumn)) { $ws.View.FreezePanes(2, 2) Write-Verbose -Message "Froze top row and first column" } - - if ($FreezeFirstColumn) { + elseif ($FreezeTopRow) { + $ws.View.FreezePanes(2, 1) + Write-Verbose -Message "Froze top row" + } + elseif ($FreezeFirstColumn) { $ws.View.FreezePanes(1, 2) Write-Verbose -Message "Froze first column" } @@ -791,8 +791,8 @@ } catch {Write-Warning -Message "Failed adding Freezing the panes in worksheet '$WorkSheetname': $_"} - if ($BoldTopRow) { - try { + if ($BoldTopRow) { #it sets bold as far as there are populated cells: for whole row could do $ws.row($x).style.font.bold = $true + try { if ($Title) { $range = $ws.Dimension.Address -replace '\d+', ($StartRow + 1) } @@ -859,7 +859,7 @@ if ($PassThru) { $pkg } else { - if ($ReturnRange) {$ws.Dimension.Address } + if ($ReturnRange) {$dataRange } $pkg.Save() Write-Verbose -Message "Saved workbook $($pkg.File)" diff --git a/Join-Worksheet.ps1 b/Join-Worksheet.ps1 index c0ba310..fe4aa02 100644 --- a/Join-Worksheet.ps1 +++ b/Join-Worksheet.ps1 @@ -37,9 +37,11 @@ param ( # Path to a new or existing .XLSX file. [Parameter(ParameterSetName = "Default", Position = 0)] + [Parameter(ParameterSetName = "Table" , Position = 0)] [String]$Path , # An object representing an Excel Package - usually this is returned by specifying -Passthru allowing multiple commands to work on the same Workbook without saving and reloading each time. - [Parameter(Mandatory = $true, ParameterSetName = "Package")] + [Parameter(Mandatory = $true, ParameterSetName = "PackageDefault")] + [Parameter(Mandatory = $true, ParameterSetName = "PackageTable")] [OfficeOpenXml.ExcelPackage]$ExcelPackage, # The name of a sheet within the workbook where the other sheets will be joined together - "Combined" by default. $WorkSheetName = 'Combined', @@ -62,6 +64,8 @@ # Freezes panes at specified coordinates (in the form RowNumber , ColumnNumber). [Int[]]$FreezePane, #Enables the 'Filter' in Excel on the complete header row. So users can easily sort, filter and/or search the data in the select column from within Excel. + [Parameter(ParameterSetName = 'Default')] + [Parameter(ParameterSetName = 'PackageDefault')] [Switch]$AutoFilter, #Makes the top Row boldface. [Switch]$BoldTopRow, @@ -77,10 +81,36 @@ [Switch]$TitleBold, #Sets the point size for the title. [Int]$TitleSize = 22, - # Hashtable(s) with Sheet PivotRows, PivotColumns, PivotData, IncludePivotChart and ChartType values to specify a definition for one or more pivot table(s). + #Hashtable(s) with Sheet PivotRows, PivotColumns, PivotData, IncludePivotChart and ChartType values to specify a definition for one or more pivot table(s). [Hashtable]$PivotTableDefinition, - # A hashtable containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. + #A hashtable containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. [Object[]]$ExcelChartDefinition, + [Object[]]$ConditionalFormat, + #Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered. + [Object[]]$ConditionalText, + #Makes each column a named range. + [switch]$AutoNameRange, + #Makes the data in the worksheet a named range. + [ValidateScript( { + if (-not $_) { throw 'RangeName is null or empty.' } + elseif ($_[0] -notmatch '[a-z]') { throw 'RangeName starts with an invalid character.' } + else { $true } + })] + [String]$RangeName, + [ValidateScript( { + if (-not $_) { throw 'Tablename is null or empty.' } + elseif ($_[0] -notmatch '[a-z]') { throw 'Tablename starts with an invalid character.' } + else { $true } + })] + [Parameter(ParameterSetName = 'Table' , Mandatory = $true)] + [Parameter(ParameterSetName = 'PackageTable' , Mandatory = $true)] + # Makes the data in the worksheet a table with a name applies a style to it. Name must not contain spaces. + [String]$TableName, + [Parameter(ParameterSetName = 'Table')] + [Parameter(ParameterSetName = 'PackageTable')] + [OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6', + #Selects the style for the named table - defaults to 'Medium6'. + [switch]$ReturnRange, #Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. [switch]$Show, #If specified, an object representing the unsaved Excel package will be returned, it then needs to be saved. @@ -89,6 +119,7 @@ #region get target worksheet, select it and move it to the end. if ($Path -and -not $ExcelPackage) {$ExcelPackage = Open-ExcelPackage -path $Path } $destinationSheet = Add-WorkSheet -ExcelPackage $ExcelPackage -WorkSheetname $WorkSheetName -ClearSheet:$Clearsheet + foreach ($w in $ExcelPackage.Workbook.Worksheets) {$w.view.TabSelected = $false} $destinationSheet.View.TabSelected = $true $ExcelPackage.Workbook.Worksheets.MoveToEnd($WorkSheetName) #row to insert at will be 1 on a blank sheet and lastrow + 1 on populated one @@ -152,9 +183,10 @@ 'Path', 'Clearsheet', 'NoHeader', 'FromLabel', 'LabelBlocks', 'HideSource', 'Title', 'TitleFillPattern', 'TitleBackgroundColor', 'TitleBold', 'TitleSize' | ForEach-Object {[void]$params.Remove($_)} if ($params.Keys.Count) { + if ($Title) { $params.StartRow = 2} $params.WorkSheetName = $WorkSheetName $params.ExcelPackage = $ExcelPackage - Export-Excel @Params + Export-Excel @Params } else { Close-ExcelPackage -ExcelPackage $ExcelPackage diff --git a/README.md b/README.md index 45ecff4..6423db5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -<<<<<<< HEAD PowerShell Import-Excel - @@ -32,16 +31,17 @@ 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 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. -- Added paramters for managing Axes and legend +# What's new to 6th 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 that -Header does nothing, so it isn't Add-Excel chart and there is a message that does nothing in New-ExcelChartDefinition . +- Added paramters for managing Axes and legend (these are currently in Add-ExcelChart but not new-ExcelChartDefinition) - 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) +- Reverted the [double]::tryParse in export excel to the previous way, as the shorter way 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) This means that if there are multiple inserts only inserted data is touched, rather than going as far down and/or right as the furthest used cell. Added a test for this. +- Added more of the Parameters from Export-Excel to Join-worksheet, join just calls export with these parameters so there is no code behind them (#383) # New in June 18 - New commands - Diff , Merge and Join @@ -841,837 +841,3 @@ You can also find EPPLus on [Nuget](https://www.nuget.org/packages/EPPlus/). * Using `-IncludePivotTable`, if that pivot table name exists, you'll get an error. * Investigating a solution * *Workaround* delete the Excel file first, then do the export -======= -PowerShell Import-Excel -- - -Install from the [PowerShell Gallery](https://www.powershellgallery.com/packages/ImportExcel/). - -This PowerShell Module allows you to read and write Excel files without installing Microsoft Excel on your system. No need to bother with the cumbersome Excel COM-object. Creating Tables, Pivot Tables, Charts and much more has just become a lot easier. - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/testimonial.png) - -# How to Vidoes -* [PowerShell Excel Module - ImportExcel](https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5uoqS92stXioZw-u-ze_NtvSo0k0K0kq) - -Installation -- -#### [PowerShell V5](https://www.microsoft.com/en-us/download/details.aspx?id=50395) and Later -You can install the `ImportExcel` module directly from the PowerShell Gallery - -* [Recommended] Install to your personal PowerShell Modules folder -```PowerShell -Install-Module ImportExcel -scope CurrentUser -``` -* [Requires Elevation] Install for Everyone (computer PowerShell Modules folder) -```PowerShell -Install-Module ImportExcel -``` - -#### PowerShell V4 and Earlier -To install to your personal modules folder (e.g. ~\Documents\WindowsPowerShell\Modules), run: - -```PowerShell -iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfinke/ImportExcel/master/Install.ps1') -``` - -# What's new - -- 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!) - 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 - - I have moved this from ImportExcel.psm1 to ExportExcel.ps1 and it now can move a new worksheet to the right place, and can copy an existing worksheet (from the same or a different workbook) to a new one, and I set the Set return-type to aid intellisense -- New-PivotTableDefinition - - Now Supports `-PivotFilter` and `-PivotDataToColumn`, `-ChartHeight/width` `-ChartRow/Column`, `-ChartRow/ColumnPixelOffset` parameters -- Set-Format - - Fixed a bug where the `-address` parameter had to be named, although the examples in `export-excel` help showed it working by position (which works now. ) -- Export-Excel - - I've done some re-factoring - 1. I "flattened out" small "called-once" functions , add-title, convert-toNumber and Stop-ExcelProcess. - 2. It now uses Add-Worksheet, Open-ExcelPackage and Add-ConditionalFormat instead of duplicating their functionality. - 3. I've moved the PivotTable functionality (which was doubled up) out to a new function "Add-PivotTable" which supports some extra parameters PivotFilter and PivotDataToColumn, ChartHeight/width ChartRow/Column, ChartRow/ColumnPixelOffsets. - 4. I've made the try{} catch{} blocks cover smaller blocks of code to give a better idea where a failure happend, some of these now Warn instead of throwing - I'd rather save the data with warnings than throw it away because we can't add a chart. Along with this I've added some extra write-verbose messages - - Bad column-names specified for Pivots now generate warnings instead of throwing. - - Fixed issues when pivottables / charts already exist and an export tries to create them again. - - Fixed issue where AutoNamedRange, NamedRange, and TableName do not work when appending to a sheet which already contains the range(s) / table - - Fixed issue where AutoNamedRange may try to create ranges with an illegal name. - - Added check for illegal characters in RangeName or Table Name (replace them with "_"), changed tablename validation to allow spaces and applied same validation to RangeName - - Fixed a bug where BoldTopRow is always bolds row 1 even if the export is told to start at a lower row. - - Fixed a bug where titles throw pivot table creation out of alignment. - - Fixed a bug where Append can overwrite the last rows of data if the initial export had blank rows at the top of the sheet. - - Removed the need to specify a fill type when specifying a title background color - - Added MoveToStart, MoveToEnd, MoveBefore and MoveAfter Parameters - these go straight through to Add worksheet - - Added "NoScriptOrAliasProperties" "DisplayPropertySet" switches (names subject to change) - combined with ExcludeProperty these are a quick way to reduce the data exported (and speed things up) - - Added PivotTableName Switch (in line with 5.0.1 release) - - Add-CellValue now understands URI item properties. If a property is of type URI it is created as a hyperlink to speed up Add-CellValue - - 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 - ---- - - -- [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 - - Removed `[PSPlot]` as OutputType. Fixes it throwing an error -- [Nasir Zubair](https://github.com/nzubair) added `ConvertEmptyStringsToNull` to the function `ConvertFrom-ExcelToSQLInsert` - - If specified, cells without any data are replaced with NULL, instead of an empty string. This is to address behviors in certain DBMS where an empty string is insert as 0 for INT column, instead of a NULL value. - - -#### 4/10/2018 --New parameter `-ReZip`. It ReZips the xlsx so it can be imported to PowerBI - -Thanks to [Justin Grote](https://github.com/JustinGrote) for finding and fixing the error that Excel files created do not import to PowerBI online. Plus, thank you to [CrashM](https://github.com/CrashM) for confirming the fix. - -Super helpful! - -#### 3/31/2018 -- Updated `Set-Format` - * Added parameters to set borders for cells, including top, bottm, left and right - * Added parameters to set `value` and `formula` - -```powershell -$data = @" -From,To,RDollars,RPercent,MDollars,MPercent,Revenue,Margin -Atlanta,New York,3602000,.0809,955000,.09,245,65 -New York,Washington,4674000,.105,336000,.03,222,16 -Chicago,New York,4674000,.0804,1536000,.14,550,43 -New York,Philadelphia,12180000,.1427,-716000,-.07,321,-25 -New York,San Francisco,3221000,.0629,1088000,.04,436,21 -New York,Phoneix,2782000,.0723,467000,.10,674,33 -"@ -``` - -![](https://github.com/dfinke/ImportExcel/blob/master/images/CustomReport.png?raw=true) - - -- Added `-PivotFilter` parameter, allows you to set up a filter so you can drill down into a subset of the overall dataset. - -```powershell -$data =@" -Region,Area,Product,Units,Cost -North,A1,Apple,100,.5 -South,A2,Pear,120,1.5 -East,A3,Grape,140,2.5 -West,A4,Banana,160,3.5 -North,A1,Pear,120,1.5 -North,A1,Grape,140,2.5 -"@ -``` - -![](https://github.com/dfinke/ImportExcel/blob/master/images/PivotTableFilter.png?raw=true) - - -#### 3/14/2018 -- Thank you to [James O'Neill](https://twitter.com/jamesoneill), fixed bugs with ChangeDatabase parameter which would prevent it working - -#### -* Added -Force to New-Alias -* Add example to set the background color of a column -* Supports excluding Row Grand Totals for PivotTables -* Allow xlsm files to be read -* Fix `Set-Column.ps1`, `Set-Row.ps1`, `SetFormat.ps1`, `formatting.ps1` **$falsee** and **$BorderRound** -#### 1/1/2018 -* Added switch `[Switch]$NoTotalsInPivot`. Allows hiding of the row totals in the pivot table. -Thanks you to [jameseholt](https://github.com/jameseholt) for the request. - -```powershell - get-process | where Company | select Company, Handles, WorkingSet | - export-excel C:\temp\testColumnGrand.xlsx ` - -Show -ClearSheet -KillExcel ` - -IncludePivotTable -PivotRows Company -PivotData @{"Handles"="average"} -NoTotalsInPivot -``` - -* Fixed when using certain a `ChartType` for the Pivot Table Chart, would throw an error -* Fixed - when you specify a file, and the directory does not exit, it now creates it - -#### 11/23/2017 -More great additions and thanks to [James O'Neill](https://twitter.com/jamesoneill) - -* Added `Convert-XlRangeToImage` Gets the specified part of an Excel file and exports it as an image -* Fixed a typo in the message at line 373. -* Now catch an attempt to both clear the sheet and append to it. -* Fixed some issues when appending to sheets where the header isn't in row 1 or the data doesn't start in column 1. -* Added support for more settings when creating a pivot chart. -* Corrected a typo PivotTableName was PivtoTableName in definition of New-PivotTableDefinition -* Add-ConditionalFormat and Set-Format added to the parameters so each has the choice of working more like the other. -* Added Set-Row and Set-Column - fill a formula down or across. -* Added Send-SQLDataToExcel. Insert a rowset and then call Export-Excel for ranges, charts, pivots etc - -#### 10/30/2017 -Huge thanks to [James O'Neill](https://twitter.com/jamesoneill). PowerShell aficionado. He always brings a flare when working with PowerShell. This is no exception. - -(Check out the examples `help Export-Excel -Examples`) - -* New parameter `Package` allows an ExcelPackage object returned by `-passThru` to be passed in -* New parameter `ExcludeProperty` to remove unwanted properties without needing to go through `select-object` -* New parameter `Append` code to read the existing headers and move the insertion point below the current data -* New parameter `ClearSheet` which removes the worksheet and any past data - -* Remove any existing Pivot table before trying to [re]create it -* Check for inserting a pivot table so if `-InsertPivotChart` is specified it implies `-InsertPivotTable` - -(Check out the examples `help Export-Excel -Examples`) - -* New function `Export-Charts` (requires Excel to be installed) - Export Excel charts out as JPG files -* New function `Add-ConditionalFormatting` Adds contitional formatting to worksheet -* New function `Set-Format` Applies Number, font, alignment and colour formatting to a range of Excel Cells -* `ColorCompletion` an argument completer for `Colors` for params across functions - -I also worked out the parameters so you can do this, which is the same as passing `-Now`. It creates an Excel file name for you, does an auto fit and sets up filters. - -`ps | select Company, Handles | Export-Excel` - -#### 10/13/2017 -Added `New-PivotTableDefinition`. You can create and wire up a PivotTable to a WorkSheet. You can also create as many PivotTable Worksheets to point a one Worksheet. Or, you create many Worksheets and many corresponding PivotTable Worksheets. - -Here you can create a WorkSheet with the data from `Get-Service`. Then create four PivotTables, pointing to the data each pivoting on a differnt dimension and showing a differnet chart - -```powershell -$base = @{ - SourceWorkSheet = 'gsv' - PivotData = @{'Status' = 'count'} - IncludePivotChart = $true -} - -$ptd = [ordered]@{} - -$ptd += New-PivotTableDefinition @base servicetype -PivotRows servicetype -ChartType Area3D -$ptd += New-PivotTableDefinition @base status -PivotRows status -ChartType PieExploded3D -$ptd += New-PivotTableDefinition @base starttype -PivotRows starttype -ChartType BarClustered3D -$ptd += New-PivotTableDefinition @base canstop -PivotRows canstop -ChartType ConeColStacked - -Get-Service | Export-Excel -path $file -WorkSheetname gsv -Show -PivotTableDefinition $ptd -``` - -#### 10/4/2017 -Thanks to https://github.com/ili101 : -- Fix Bug, Unable to find type [PSPlot] -- Fix Bug, AutoFilter with TableName create corrupted Excel file. - -#### 10/2/2017 -Thanks to [Jeremy Brun](https://github.com/jeremytbrun) -Fixed issues related to use of -Title parameter combined with column formatting parameters. -- [Issue #182](https://github.com/dfinke/ImportExcel/issues/182) -- [Issue #89](https://github.com/dfinke/ImportExcel/issues/89) - -#### 9/28/2017 (Version 4.0.1) -- Added a new parameter called `Password` to import password protected files -- Added even more `Pester` tests for a more robust and bug free module -- Renamed parameter 'TopRow' to 'StartRow' - This allows us to be more concise when new parameters ('StartColumn', ..) will be added in the future Your code will not break after the update, because we added an alias for backward compatibility - -Special thanks to [robinmalik](https://github.com/robinmalik) for providing us with [the code](https://github.com/dfinke/ImportExcel/issues/174) to implement this new feature. A high five to [DarkLite1](https://github.com/DarkLite1) for the implementation. - -#### 9/12/2017 (Version 4.0.0) - -Super thanks and hat tip to [DarkLite1](https://github.com/DarkLite1). There is now a new and improved `Import-Excel`, not only in functionality, but also improved readability, examples and more. Not only that, he's been running it in production in his company for a number of weeks! - -*Added* `Update-FirstObjectProperties` Updates the first object to contain all the properties of the object with the most properties in the array. Check out the help. - - -***Breaking Changes***: Due to a big portion of the code that is rewritten some slightly different behavior can be expected from the `Import-Excel` function. This is especially true for importing empty Excel files with or without using the `TopRow` parameter. To make sure that your code is still valid, please check the examples in the help or the accompanying `Pester` test file. - - -Moving forward, we are planning to include automatic testing with the help of `Pester`, `Appveyor` and `Travis`. From now on any changes in the module will have to be accompanied by the corresponding `Pester` tests to avoid breakages of code and functionality. This is in preparation for new features coming down the road. - -#### 7/3/2017 -Thanks to [Mikkel Nordberg](https://www.linkedin.com/in/mikkelnordberg). He contributed a `ConvertTo-ExcelXlsx`. To use it, Excel needs to be installed. The function converts the older Excel file format ending in `.xls` to the new format ending in `.xlsx`. - -#### 6/15/2017 -Huge thank you to [DarkLite1](https://github.com/DarkLite1)! Refactoring of code, adding help, adding features, fixing bugs. Specifically this long outstanding one: - -[Export-Excel: Numeric values not correct](https://github.com/dfinke/ImportExcel/issues/168) - -It is fantastic to work with people like `DarkLite1` in the community, to help make the module so much better. A hat to you. - -Another shout out to [Damian Reeves](https://twitter.com/DamReev)! His questions turn into great features. He asked if it was possible to import an Excel worksheet and transform the data into SQL `INSERT` statements. We can now answer that question with a big YES! - -```PowerShell -ConvertFrom-ExcelToSQLInsert People .\testSQLGen.xlsx -``` - -``` -INSERT INTO People ('First', 'Last', 'The Zip') Values('John', 'Doe', '12345'); -INSERT INTO People ('First', 'Last', 'The Zip') Values('Jim', 'Doe', '12345'); -INSERT INTO People ('First', 'Last', 'The Zip') Values('Tom', 'Doe', '12345'); -INSERT INTO People ('First', 'Last', 'The Zip') Values('Harry', 'Doe', '12345'); -INSERT INTO People ('First', 'Last', 'The Zip') Values('Jane', 'Doe', '12345'); -``` -## Bonus Points -Use the underlying `ConvertFrom-ExcelData` function and you can use a scriptblock to format the data however you want. - -```PowerShell -ConvertFrom-ExcelData .\testSQLGen.xlsx { - param($propertyNames, $record) - - $reportRecord = @() - foreach ($pn in $propertyNames) { - $reportRecord += "{0}: {1}" -f $pn, $record.$pn - } - $reportRecord +="" - $reportRecord -join "`r`n" -} -``` -Generates - -``` -First: John -Last: Doe -The Zip: 12345 - -First: Jim -Last: Doe -The Zip: 12345 - -First: Tom -Last: Doe -The Zip: 12345 - -First: Harry -Last: Doe -The Zip: 12345 - -First: Jane -Last: Doe -The Zip: 12345 -``` - -#### 2/2/2017 -Thank you to [DarkLite1](https://github.com/DarkLite1) for more updates -* TableName with parameter validation, throws an error when the TableName: - - Starts with something else then a letter - - Is NULL or empty - - Contains spaces -- Numeric parsing now uses `CurrentInfo` to use the system settings - -#### 2/14/2017 -Big thanks to [DarkLite1](https://github.com/DarkLite1) for some great updates -* `-DataOnly` switch added to `Import-Excel`. When used it will only generate objects for rows that contain text values, not for empty rows or columns. - -* `Get-ExcelWorkBookInfo` - retrieves information of an Excel workbook. -``` - Get-ExcelWorkbookInfo .\Test.xlsx - - CorePropertiesXml : #document - Title : - Subject : - Author : Konica Minolta User - Comments : - Keywords : - LastModifiedBy : Bond, James (London) GBR - LastPrinted : 2017-01-21T12:36:11Z - Created : 17/01/2017 13:51:32 - Category : - Status : - ExtendedPropertiesXml : #document - Application : Microsoft Excel - HyperlinkBase : - AppVersion : 14.0300 - Company : Secret Service - Manager : - Modified : 10/02/2017 12:45:37 - CustomPropertiesXml : #document -``` - -#### 12/22/2016 -- Added `-Now` switch. This short cuts the process, automatically creating a temp file and enables the `-Show`, `-AutoFilter`, `-AutoSize` switches. - -```PowerShell -Get-Process | Select Company, Handles | Export-Excel -Now -``` - -- Added ScriptBlocks for coloring cells. Check out [Examples](https://github.com/dfinke/ImportExcel/tree/master/Examples/FormatCellStyles) - -```PowerShell -Get-Process | - Select-Object Company,Handles,PM, NPM| - Export-Excel $xlfile -Show -AutoSize -CellStyleSB { - param( - $workSheet, - $totalRows, - $lastColumn - ) - - Set-CellStyle $workSheet 1 $LastColumn Solid Cyan - - foreach($row in (2..$totalRows | Where-Object {$_ % 2 -eq 0})) { - Set-CellStyle $workSheet $row $LastColumn Solid Gray - } - - foreach($row in (2..$totalRows | Where-Object {$_ % 2 -eq 1})) { - Set-CellStyle $workSheet $row $LastColumn Solid LightGray - } - } -``` -![](https://github.com/dfinke/ImportExcel/blob/master/images/CellFormatting.png?raw=true) - -#### 9/28/2016 -[Fixed](https://github.com/dfinke/ImportExcel/pull/126) PowerShell 3.0 compatibility. Thanks to [headsphere](https://github.com/headsphere). He used `$obj.PSObject.Methods[$target]` snytax to make it backward compatible. PS v4.0 and later allow `$obj.$target`. - -Thank you to [xelsirko](https://github.com/xelsirko) for fixing - *Import-module importexcel gives version warning if started inside background job* - -#### 8/12/2016 -[Fixed](https://github.com/dfinke/ImportExcel/issues/115) reading the headers from cells, moved from using `Text` property to `Value` property. - -#### 7/30/2016 -* Added `Copy-ExcelWorksheet`. Let's you copy a work sheet from one Excel workbook to another. - -#### 7/21/2016 -* Fixes `Import-Excel` #68 - -#### 7/7/2016 -[Attila Mihalicz](https://github.com/attilamihalicz) fixed two issues - -* Removing extra spaces after the backtick -* Uninitialized variable $idx leaks into the pipeline when `-TableName` parameter is used - -Thanks Attila. - - -#### 7/1/2016 -* Pushed 2.2.7 fixed resolve path in Get-ExcelSheetInfo -* Fixed [Casting Error in Export-Excel](https://github.com/dfinke/ImportExcel/issues/108) -* For `Import-Excel` change Resolve-Path to return ProviderPath for use with UNC - -#### 6/01/2016 -* Added -UseDefaultCredentials to both `Import-Html` and `Get-HtmlTable` -* New functions, `Import-UPS` and `Import-USPS`. Pass in a valid tracking # and it scrapes the page for the delivery details - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/Tracking.gif) - -#### 4/30/2016 -Huge thank you to [Willie Möller](https://github.com/W1M0R) - -* He added a version check so the PowerShell Classes don't cause issues for downlevel version of PowerShell -* He also contributed the first Pester tests for the module. Super! Check them out, they'll be the way tests will be implemented going forward - -#### 4/18/2016 -Thanks to [Paul Williams](https://github.com/pauldalewilliams) for this feature. Now data can be transposed to columns for better charting. - -```PowerShell -$file = "C:\Temp\ps.xlsx" -rm $file -ErrorAction Ignore - -ps | - where company | - select Company,PagedMemorySize,PeakPagedMemorySize | - Export-Excel $file -Show -AutoSize ` - -IncludePivotTable ` - -IncludePivotChart ` - -ChartType ColumnClustered ` - -PivotRows Company ` - -PivotData @{PagedMemorySize='sum';PeakPagedMemorySize='sum'} -``` -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PivotAsRows.png) - - -Add `-PivotDataToColumn` - -```PowerShell -$file = "C:\Temp\ps.xlsx" -rm $file -ErrorAction Ignore - -ps | - where company | - select Company,PagedMemorySize,PeakPagedMemorySize | - Export-Excel $file -Show -AutoSize ` - -IncludePivotTable ` - -IncludePivotChart ` - -ChartType ColumnClustered ` - -PivotRows Company ` - -PivotData @{PagedMemorySize='sum';PeakPagedMemorySize='sum'} ` - -PivotDataToColumn -``` -And here is the new chart view -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PivotAsColumns.png) -#### 4/7/2016 -Made more methods fluent -``` -$t=Get-Range 0 5 .2 - -$t2=$t|%{$_*$_} -$t3=$t|%{$_*$_*$_} - -(New-Plot). - Plot($t,$t, $t,$t2, $t,$t3). - SetChartPosition("i"). - SetChartSize(500,500). - Title("Hello World"). - Show() -``` -#### 3/31/2016 -* Thanks to [redoz](https://github.com/redoz) Multi Series Charts are now working - -Also check out how you can create a table and then with Excel notation, index into the data for charting `"Impressions[A]"` - -``` -$data = @" -A,B,C,Date -2,1,1,2016-03-29 -5,10,1,2016-03-29 -"@ | ConvertFrom-Csv - -$c = New-ExcelChart -Title Impressions ` - -ChartType Line -Header "Something" ` - -XRange "Impressions[Date]" ` - -YRange @("Impressions[B]","Impressions[A]") - -$data | - Export-Excel temp.xlsx -AutoSize -TableName Impressions -Show -ExcelChartDefinition $c -``` -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/MultiSeries.gif) - -#### 3/26/2016 -* Added `NumberFormat` parameter - -``` -$data | - Export-Excel -Path $file -Show -NumberFormat '[Blue]$#,##0.00;[Red]-$#,##0.00' -``` -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/Formatting.png) - - -#### 3/18/2016 -* Added `Get-Range`, `New-Plot` and Plot Cos example -* Updated EPPlus DLL. Allows markers to be changed and colored -* Handles and warns if auto name range names are also valid Excel ranges - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PSPlot.gif) - -#### 3/7/2016 -* Added `Header` and `FirstDataRow` for `Import-Html` - -#### 3/2/2016 -* Added `GreaterThan`, `GreaterThanOrEqual`, `LessThan`, `LessThanOrEqual` to `New-ConditionalText` - -```PowerShell -echo 489 668 299 777 860 151 119 497 234 788 | - Export-Excel c:\temp\test.xlsx -Show ` - -ConditionalText (New-ConditionalText -ConditionalType GreaterThan 525) -``` -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/GTConditional.png) - -#### 2/22/2016 -* `Import-Html` using Lee Holmes [Extracting Tables from PowerShell’s Invoke-WebRequest](http://www.leeholmes.com/blog/2015/01/05/extracting-tables-from-PowerShells-invoke-webrequest/) - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/ImportHtml.gif) - -#### 2/17/2016 -* Added Conditional Text types of `Equal` and `NotEqual` -* Phone #'s like '+33 011 234 34' will be now be handled correctly - -## Try *PassThru* - -```PowerShell -$file = "C:\Temp\passthru.xlsx" -rm $file -ErrorAction Ignore - -$xlPkg = $( - New-PSItem north 10 - New-PSItem east 20 - New-PSItem west 30 - New-PSItem south 40 -) | Export-Excel $file -PassThru - -$ws=$xlPkg.Workbook.Worksheets[1] - -$ws.Cells["A3"].Value = "Hello World" -$ws.Cells["B3"].Value = "Updating cells" -$ws.Cells["D1:D5"].Value = "Data" - -$ws.Cells.AutoFitColumns() - -$xlPkg.Save() -$xlPkg.Dispose() - -Invoke-Item $file -``` - -## Result -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PassThru.png) - -#### 1/18/2016 - -* Added `Conditional Text Formatting`. [Boe Prox](https://twitter.com/proxb) posted about [HTML Reporting, Part 2: Take Your Reporting a Step Further](https://mcpmag.com/articles/2016/01/14/html-reporting-part-2.aspx) and colorized cells. Great idea, now part of the PowerShell Excel module. - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/ConditionalText2.gif) - -#### 1/7/2016 -* Added `Get-ExcelSheetInfo` - Great contribution from *Johan Åkerström* check him out on [GitHub](https://github.com/CosmosKey) and [Twitter](https://twitter.com/neptune443) - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/GetExcelSheetInfo.png) - -#### 12/26/2015 - -* Added `NoLegend`, `Show-Category`, `ShowPercent` for all charts including Pivot Charts -* Updated PieChart, BarChart, ColumnChart and Line chart to work with the pipeline and added `NoLegend`, `Show-Category`, `ShowPercent` - -#### 12/17/2015 - -These new features open the door for really sophisticated work sheet creation. - -Stay tuned for a [blog post](http://www.dougfinke.com/blog/) and examples. - -***Quick List*** -* StartRow, StartColumn for placing data anywhere in a sheet -* New-ExcelChart - Add charts to a sheet, multiple series for a chart, locate the chart anywhere on the sheet -* AutoNameRange, Use functions and/or calculations in a cell -* Quick charting using PieChart, BarChart, ColumnChart and more - -![](https://raw.githubusercontent.com/dfinke/GifCam/master/JustCharts.gif) - -#### 10/20/2015 - -Big bug fix for version 3.0 PowerShell folks! - -This technique fails in 3.0 and works in 4.0 and later. -```PowerShell -$m="substring" -"hello".$m(2,1) -``` - -Adding `.invoke` works in 3.0 and later. - -```PowerShell -$m="substring" -"hello".$m.invoke(2,1) -``` - -A ***big thank you*** to [DarkLite1](https://github.com/DarkLite1) for adding the help to Export-Excel. - -Added `-HeaderRow` parameter. Sometimes the heading does not start in Row 1. - - -#### 10/16/2015 - -Fixes [Export-Excel generates corrupt Excel file](https://github.com/dfinke/ImportExcel/issues/46) - -#### 10/15/2015 - -`Import-Excel` has a new parameter `NoHeader`. If data in the sheet does not have headers and you don't want to supply your own, `Import-Excel` will generate the property name. - -`Import-Excel` now returns `.Value` rather than `.Text` - - -#### 10/1/2015 - -Merged ValidateSet for Encoding and Extension. Thank you [Irwin Strachan](https://github.com/irwins). - -#### 9/30/2015 - -Export-Excel can now handle data that is **not** an object - - echo a b c 1 $true 2.1 1/1/2015 | Export-Excel c:\temp\test.xlsx -Show -Or - - dir -Name | Export-Excel c:\temp\test.xlsx -Show - -#### 9/25/2015 - -**Hide worksheets** -Got a great request from [forensicsguy20012004](https://github.com/forensicsguy20012004) to hide worksheets. You create a few pivotables, generate charts and then pivotable worksheets don't need to be visible. - -`Export-Excel` now has a `-HideSheet` parameter that takes and array of worksheet names and hides them. - -##### Example -Here, you create four worksheets named `PM`,`Handles`,`Services` and `Files`. - -The last line creates the `Files` sheet and then hides the `Handles`,`Services` sheets. - - $p = Get-Process - - $p|select company, pm | Export-Excel $xlFile -WorkSheetname PM - $p|select company, handles| Export-Excel $xlFile -WorkSheetname Handles - Get-Service| Export-Excel $xlFile -WorkSheetname Services - - dir -File | Export-Excel $xlFile -WorkSheetname Files -Show -HideSheet Handles, Services - - -**Note** There is a bug in EPPlus that does not let you hide the first worksheet created. Hopefully it'll resolved soon. - -#### 9/11/2015 - -Added Conditional formatting. See [TryConditional.ps1](https://github.com/dfinke/ImportExcel/blob/master/TryConditional.ps1) as an example. - -Or, check out the short ***"How To"*** video. - -[![image](http://www.dougfinke.com/videos/excelpsmodule/ExcelPSModule_First_Frame.png)](http://www.dougfinke.com/videos/excelpsmodule/excelpsmodule.mp4) - - -#### 8/21/2015 -* Now import Excel sheets even if the file is open in Excel. Thank you [Francois Lachance-Guillemette](https://github.com/francoislg) - -#### 7/09/2015 -* For -PivotRows you can pass a `hashtable` with the name of the property and the type of calculation. `Sum`, `Average`, `Max`, `Min`, `Product`, `StdDev`, `StdDevp`, `Var`, `Varp` - -```PowerShell -Get-Service | - Export-Excel "c:\temp\test.xlsx" ` - -Show ` - -IncludePivotTable ` - -PivotRows status ` - -PivotData @{status='count'} -``` - -#### 6/16/2015 (Thanks [Justin](https://github.com/zippy1981)) -* Improvements to PivotTable overwriting -* Added two parameters to Export-Excel - * RangeName - Turns the data piped to Export-Excel into a named range. - * TableName - Turns the data piped to Export-Excel into an excel table. - -Examples - - Get-Process|Export-Excel foo.xlsx -Verbose -IncludePivotTable -TableName "Processes" -Show - Get-Process|Export-Excel foo.xlsx -Verbose -IncludePivotTable -RangeName "Processes" -Show - - -#### 5/25/2015 -* Fixed null header problem - -#### 5/17/2015 -* Added three parameters: - * FreezeTopRow - Freezes the first row of the data - * AutoFilter - Enables filtering for the data in the sheet - * BoldTopRow - Bolds the top row of data, the column headers - -Example - - Get-CimInstance win32_service | - select state, accept*, start*, caption | - Export-Excel test.xlsx -Show -BoldTopRow -AutoFilter -FreezeTopRow -AutoSize - -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/FilterFreezeBold.gif) - - -#### 5/4/2015 -* Published to PowerShell Gallery. In PowerShell v5 use `Find-Module importexcel` then `Find-Module importexcel | Install-Module` - - -#### 4/27/2015 -* datetime properties were displaying as ints, now are formatted - -#### 4/25/2015 -* Now you can create multiple Pivot tables in one pass - * Thanks to [pscookiemonster](https://twitter.com/pscookiemonster), he submitted a repro case to the EPPlus CodePlex project and got it fixed - -#### Example - - $ps = ps - - $ps | - Export-Excel .\testExport.xlsx -WorkSheetname memory ` - -IncludePivotTable -PivotRows Company -PivotData PM ` - -IncludePivotChart -ChartType PieExploded3D - $ps | - Export-Excel .\testExport.xlsx -WorkSheetname handles ` - -IncludePivotTable -PivotRows Company -PivotData Handles ` - -IncludePivotChart -ChartType PieExploded3D -Show - -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/MultiplePivotTables.png) - -#### 4/20/2015 -* Included and embellished [Claus Nielsen](https://github.com/Claustn) function to take all sheets in an Excel file workbook and create a text file for each `ConvertFrom-ExcelSheet` -* Renamed `Export-MultipleExcelSheets` to `ConvertFrom-ExcelSheet` - -#### 4/13/2015 -* You can add a title to the Excel "Report" `Title`, `TitleFillPattern`, `TitleBold`, `TitleSize`, `TitleBackgroundColor` - * Thanks to [Irwin Strachan](http://pshirwin.wordpress.com) for this and other great suggestions, testing and more - - -#### 4/10/2015 -* Renamed `AutoFitColumns` to `AutoSize` -* Implemented `Export-MultipleExcelSheets` -* Implemented `-Password` for a worksheet -* Replaced `-Force` switch with `-NoClobber` switch -* Added examples for `Get-Help` -* If Pivot table is requested, that sheet becomes the tab selected - -#### 4/8/2015 -* Implemented exporting data to **named sheets** via the -WorkSheetname parameter. - -Examples -- -`gsv | Export-Excel .\test.xlsx -WorkSheetname Services` - -`dir -file | Export-Excel .\test.xlsx -WorkSheetname Files` - -`ps | Export-Excel .\test.xlsx -WorkSheetname Processes -IncludePivotTable -Show -PivotRows Company -PivotData PM` - -#### Convert (All or Some) Excel Sheets to Text files - -Reads each sheet in TestSheets.xlsx and outputs it to the data directory as the sheet name with the extension .txt - - ConvertFrom-ExcelSheet .\TestSheets.xlsx .\data - -Reads and outputs sheets like Sheet10 and Sheet20 form TestSheets.xlsx and outputs it to the data directory as the sheet name with the extension .txt - - ConvertFrom-ExcelSheet .\TestSheets.xlsx .\data sheet?0 - -#### Example Adding a Title -You can set the pattern, size and of if the title is bold. - - $p=@{ - Title = "Process Report as of $(Get-Date)" - TitleFillPattern = "LightTrellis" - TitleSize = 18 - TitleBold = $true - - Path = "$pwd\testExport.xlsx" - Show = $true - AutoSize = $true - } - - Get-Process | - Where Company | Select Company, PM | - Export-Excel @p - -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/Title.png) - -#### Example Export-MultipleExcelSheets -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/ExportMultiple.gif) - - $p = Get-Process - - $DataToGather = @{ - PM = {$p|select company, pm} - Handles = {$p|select company, handles} - Services = {gsv} - Files = {dir -File} - Albums = {(Invoke-RestMethod http://www.dougfinke.com/PowerShellfordevelopers/albums.js)} - } - - Export-MultipleExcelSheets -Show -AutoSize .\testExport.xlsx $DataToGather - - - -***NOTE*** If the sheet exists when using *-WorkSheetname* parameter, it will be deleted and then added with the new data. - -## Get-Process Exported to Excel - -### Total Physical Memory Grouped By Company -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PivotTablesAndCharts.png) - -## Importing data from an Excel spreadsheet - -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/TryImportExcel.gif) - -You can also find EPPLus on [Nuget](https://www.nuget.org/packages/EPPlus/). - -## Known Issues - -* Using `-IncludePivotTable`, if that pivot table name exists, you'll get an error. - * Investigating a solution - * *Workaround* delete the Excel file first, then do the export ->>>>>>> 9f7884f991c80448091ef56853027f64d98b6cc7 diff --git a/ToDo.md b/ToDo.md index 82fad23..304b468 100644 --- a/ToDo.md +++ b/ToDo.md @@ -1 +1,4 @@ - [ ] Create an autocomplete for WorkSheetName param on ImportExcel +- [ ] Add help text for parmaters which don't have it in Export Excel +- [ ] Add checks for valid worksheet names (also check pivot names, range names and table names are valid) +- [ ] Investigate regional support for number conversion \ No newline at end of file diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 index 14d3765..326a774 100644 --- a/__tests__/Export-Excel.Tests.ps1 +++ b/__tests__/Export-Excel.Tests.ps1 @@ -5,7 +5,7 @@ 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 { +#53Describe ExportExcel { Context "#Example 1 # Creates and opens a file with the right number of rows and columns" { $path = "$env:TEMP\Test.xlsx" @@ -685,6 +685,43 @@ Describe ExportExcel { } Close-ExcelPackage -ExcelPackage $excel -nosave } +describe "foo" { + Context " # Awkward multiple tables" { + $path = "$Env:TEMP\test.xlsx" + remove-item -Path $path -ErrorAction SilentlyContinue + $r = Get-ChildItem -path C:\WINDOWS\system32 -File + + "Biggest files" | Export-Excel -Path $path -StartRow 1 -StartColumn 7 + $r | Sort-Object length -Descending | Select -First 14 Name, @{n="Size";e={$_.Length}} | + Export-Excel -Path $path -TableName FileSize -StartRow 2 -StartColumn 7 -TableStyle Medium2 + + $r.extension | Group-Object | Sort-Object -Property count -Descending | Select-Object -First 12 Name, Count | + Export-Excel -Path $path -TableName ExtSize -Title "Frequent Extensions" -TitleSize 11 + + $r | Group-Object -Property extension | Select-Object Name, @{n="Size"; e={($_.group | measure -property length -sum).sum}} | + Sort-Object -Property size -Descending | Select-Object -First 10 | + Export-Excel -Path $path -TableName ExtCount -Title "Biggest extensions" -TitleSize 11 -StartColumn 4 -AutoSize + + $excel = Open-ExcelPackage -Path $path + $ws = $excel.Workbook.Worksheets[1] + it "Created 3 tables " { + $ws.tables.count | should be 3 + } + it "Created the FileSize table in the right places with the right size " { + $ws.Tables["FileSize"].Address.Address | should be "G2:H16" #Insert at row 2, Column 7, 14 rows x 2 columns of data + $ws.Tables["FileSize"].StyleName | should be "TableStyleMedium2" + } + it "Created the ExtSize table in the right places with the right size " { + $ws.Tables["ExtSize"].Address.Address | should be "A2:B14" #tile, then 12 rows x 2 columns of data + $ws.Tables["ExtSize"].TableStyle.tostring() | should be "medium6" + } + it "Created the ExtSize table in the right places with the right size " { + $ws.Tables["ExtSize"].Address.Address | should be "A2:B14" #tile, then 12 rows x 2 columns of data + $ws.Tables["ExtSize"].TableStyle.tostring() | should be "medium6" + } + } + + ## To do ## More Charts , pivot options & other FreezePanes settings ? From 73af0b5dc930643a277d9e03d81350b66eb5496d Mon Sep 17 00:00:00 2001 From: jhoneill Date: Fri, 6 Jul 2018 19:57:17 +0100 Subject: [PATCH 02/21] Updated tests for export-excel --- __tests__/Export-Excel.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 index 326a774..541c7ae 100644 --- a/__tests__/Export-Excel.Tests.ps1 +++ b/__tests__/Export-Excel.Tests.ps1 @@ -5,7 +5,7 @@ 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} -#53Describe ExportExcel { +Describe ExportExcel { Context "#Example 1 # Creates and opens a file with the right number of rows and columns" { $path = "$env:TEMP\Test.xlsx" @@ -685,7 +685,7 @@ if (Get-process -Name Excel,xlim -ErrorAction SilentlyContinue) { Write-Warni } Close-ExcelPackage -ExcelPackage $excel -nosave } -describe "foo" { + Context " # Awkward multiple tables" { $path = "$Env:TEMP\test.xlsx" remove-item -Path $path -ErrorAction SilentlyContinue From 82458edd7c1e914e3fbf04af461981df9cc76452 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Fri, 6 Jul 2018 20:41:41 +0100 Subject: [PATCH 03/21] Updates tests, extra parameters on join worksheet, Also better distinction of "whole sheet" from "added data", optimization of add-cellvalue (see readme.md) --- .../Join-Worksheet/Join-Worksheet.sample.ps1 | 43 + Export-Excel.Tests.ps1 | 620 ------------- Export-Excel.ps1 | 60 +- Join-Worksheet.ps1 | 40 +- README.md | 846 +----------------- ToDo.md | 3 + __tests__/Export-Excel.Tests.ps1 | 37 + 7 files changed, 154 insertions(+), 1495 deletions(-) create mode 100644 Examples/Join-Worksheet/Join-Worksheet.sample.ps1 delete mode 100644 Export-Excel.Tests.ps1 diff --git a/Examples/Join-Worksheet/Join-Worksheet.sample.ps1 b/Examples/Join-Worksheet/Join-Worksheet.sample.ps1 new file mode 100644 index 0000000..ca81895 --- /dev/null +++ b/Examples/Join-Worksheet/Join-Worksheet.sample.ps1 @@ -0,0 +1,43 @@ +#Get rid of pre-exisiting sheet +$path = "$Env:TEMP\test.xlsx" +remove-item -Path $path -ErrorAction SilentlyContinue + +#Create simple pages for 3 stores with product ID, Product Name, quanity price and total + +@" +ID,Product,Quantity,Price,Total +12001,Nails,37,3.99,147.63 +12002,Hammer,5,12.10,60.5 +12003,Saw,12,15.37,184.44 +12010,Drill,20,8,160 +12011,Crowbar,7,23.48,164.36 +"@ | ConvertFrom-Csv| Export-Excel -Path $path -WorkSheetname Oxford + +@" +ID,Product,Quantity,Price,Total +12001,Nails,53,3.99,211.47 +12002,Hammer,6,12.10,72.60 +12003,Saw,10,15.37,153.70 +12010,Drill,10,8,80 +12012,Pliers,2,14.99,29.98 +"@ | ConvertFrom-Csv| Export-Excel -Path $path -WorkSheetname Abingdon + + +@" +ID,Product,Quantity,Price,Total +12001,Nails,20,3.99,79.80 +12002,Hammer,2,12.10,24.20 +12010,Drill,11,8,88 +12012,Pliers,3,14.99,44.97 +"@ | ConvertFrom-Csv| Export-Excel -Path $path -WorkSheetname Banbury + +#define a pivot table with a chart to show a sales by store, broken down by product +$ptdef = New-PivotTableDefinition -PivotTableName "Summary" -PivotRows "Store" -PivotColumns "Product" -PivotData @{"Total"="SUM"} -IncludePivotChart -ChartTitle "Sales Breakdown" -ChartType ColumnStacked -ChartColumn 10 + +#Join the 3 worksheets. +#Name the combined page "Total" and Name the column with the sheet names "store" (as the sheets 'Oxford','Abingdon' and 'Banbury' are the names of the stores +#Format the data as a table named "Summary", using the style "Light1", put the column headers in bold +#Put in a title and freeze to top of the sheet including title and colmun headings +#Add the Pivot table. +#Show the result +Join-Worksheet -Path $path -WorkSheetName "Total" -Clearsheet -FromLabel "Store" -TableName "Summary" -TableStyle Light1 -AutoSize -BoldTopRow -FreezePane 2,1 -Title "Store Sales Summary" -TitleBold -TitleSize 14 -PivotTableDefinition $ptdef -show diff --git a/Export-Excel.Tests.ps1 b/Export-Excel.Tests.ps1 deleted file mode 100644 index 1d63afc..0000000 --- a/Export-Excel.Tests.ps1 +++ /dev/null @@ -1,620 +0,0 @@ -#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 -Skip "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 - } - } - - ## To do - ## More pivot options & other FreezePanes settings ? - ## Charts - ## Style script block - ## Rezip ? - -} diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 495a41f..a93367a 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -465,7 +465,7 @@ #Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as formula" break } - { $_ -is [Uri] } { + { [System.Uri]::IsWellFormedUriString($_ , [System.UriKind]::Absolute) } { # Save a hyperlink $TargetCell.Value = $_.AbsoluteUri $TargetCell.HyperLink = $_ @@ -474,8 +474,8 @@ #Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($_.AbsoluteUri)' as Hyperlink" break } - {(($NoNumberConversion) -and ($NoNumberConversion -contains $Name)) -or - ($NoNumberConversion -eq '*')} { + {( $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" @@ -484,7 +484,7 @@ Default { #Save a value as a number if possible $number = $null - if ( [Double]::TryParse([String]$_, [System.Globalization.NumberStyles]::Any, [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number)) { + if ([Double]::TryParse($_, [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 } @@ -583,15 +583,14 @@ if ($firstTimeThru) { $firstTimeThru = $false $isDataTypeValueType = $TargetData.GetType().name -match 'string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort' + if ($isDataTypeValueType) {$row -= 1} #row incremented before adding values, so it is set to the number of rows inserted at the end Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" } if ($isDataTypeValueType) { $ColumnIndex = $StartColumn - + $Row += 1 Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData - - $Row += 1 } else { #region Add headers @@ -708,7 +707,7 @@ #if the table exists, update it. if ($ws.Tables[$TableName]) { $ws.Tables[$TableName].TableXml.table.ref = $dataRange - $ws.Tables[$TableName].TableStyle = $TableStyle + $ws.Tables[$TableName].TableStyle = $TableStyle } else { $tbl = $ws.Tables.Add($ws.Cells[$dataRange], $TableName) @@ -743,36 +742,35 @@ $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 ($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} + $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) - Write-Verbose -Message "Froze top row" - } - - if ($FreezeTopRowFirstColumn) { + #Allow single switch or two seperate ones. + if ($FreezeTopRowFirstColumn -or ($FreezeTopRow -and $FreezeFirstColumn)) { $ws.View.FreezePanes(2, 2) Write-Verbose -Message "Froze top row and first column" } - - if ($FreezeFirstColumn) { + elseif ($FreezeTopRow) { + $ws.View.FreezePanes(2, 1) + Write-Verbose -Message "Froze top row" + } + elseif ($FreezeFirstColumn) { $ws.View.FreezePanes(1, 2) Write-Verbose -Message "Froze first column" } @@ -791,8 +789,8 @@ } catch {Write-Warning -Message "Failed adding Freezing the panes in worksheet '$WorkSheetname': $_"} - if ($BoldTopRow) { - try { + if ($BoldTopRow) { #it sets bold as far as there are populated cells: for whole row could do $ws.row($x).style.font.bold = $true + try { if ($Title) { $range = $ws.Dimension.Address -replace '\d+', ($StartRow + 1) } @@ -859,7 +857,7 @@ if ($PassThru) { $pkg } else { - if ($ReturnRange) {$ws.Dimension.Address } + if ($ReturnRange) {$dataRange } $pkg.Save() Write-Verbose -Message "Saved workbook $($pkg.File)" diff --git a/Join-Worksheet.ps1 b/Join-Worksheet.ps1 index c0ba310..fe4aa02 100644 --- a/Join-Worksheet.ps1 +++ b/Join-Worksheet.ps1 @@ -37,9 +37,11 @@ param ( # Path to a new or existing .XLSX file. [Parameter(ParameterSetName = "Default", Position = 0)] + [Parameter(ParameterSetName = "Table" , Position = 0)] [String]$Path , # An object representing an Excel Package - usually this is returned by specifying -Passthru allowing multiple commands to work on the same Workbook without saving and reloading each time. - [Parameter(Mandatory = $true, ParameterSetName = "Package")] + [Parameter(Mandatory = $true, ParameterSetName = "PackageDefault")] + [Parameter(Mandatory = $true, ParameterSetName = "PackageTable")] [OfficeOpenXml.ExcelPackage]$ExcelPackage, # The name of a sheet within the workbook where the other sheets will be joined together - "Combined" by default. $WorkSheetName = 'Combined', @@ -62,6 +64,8 @@ # Freezes panes at specified coordinates (in the form RowNumber , ColumnNumber). [Int[]]$FreezePane, #Enables the 'Filter' in Excel on the complete header row. So users can easily sort, filter and/or search the data in the select column from within Excel. + [Parameter(ParameterSetName = 'Default')] + [Parameter(ParameterSetName = 'PackageDefault')] [Switch]$AutoFilter, #Makes the top Row boldface. [Switch]$BoldTopRow, @@ -77,10 +81,36 @@ [Switch]$TitleBold, #Sets the point size for the title. [Int]$TitleSize = 22, - # Hashtable(s) with Sheet PivotRows, PivotColumns, PivotData, IncludePivotChart and ChartType values to specify a definition for one or more pivot table(s). + #Hashtable(s) with Sheet PivotRows, PivotColumns, PivotData, IncludePivotChart and ChartType values to specify a definition for one or more pivot table(s). [Hashtable]$PivotTableDefinition, - # A hashtable containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. + #A hashtable containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. [Object[]]$ExcelChartDefinition, + [Object[]]$ConditionalFormat, + #Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered. + [Object[]]$ConditionalText, + #Makes each column a named range. + [switch]$AutoNameRange, + #Makes the data in the worksheet a named range. + [ValidateScript( { + if (-not $_) { throw 'RangeName is null or empty.' } + elseif ($_[0] -notmatch '[a-z]') { throw 'RangeName starts with an invalid character.' } + else { $true } + })] + [String]$RangeName, + [ValidateScript( { + if (-not $_) { throw 'Tablename is null or empty.' } + elseif ($_[0] -notmatch '[a-z]') { throw 'Tablename starts with an invalid character.' } + else { $true } + })] + [Parameter(ParameterSetName = 'Table' , Mandatory = $true)] + [Parameter(ParameterSetName = 'PackageTable' , Mandatory = $true)] + # Makes the data in the worksheet a table with a name applies a style to it. Name must not contain spaces. + [String]$TableName, + [Parameter(ParameterSetName = 'Table')] + [Parameter(ParameterSetName = 'PackageTable')] + [OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6', + #Selects the style for the named table - defaults to 'Medium6'. + [switch]$ReturnRange, #Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. [switch]$Show, #If specified, an object representing the unsaved Excel package will be returned, it then needs to be saved. @@ -89,6 +119,7 @@ #region get target worksheet, select it and move it to the end. if ($Path -and -not $ExcelPackage) {$ExcelPackage = Open-ExcelPackage -path $Path } $destinationSheet = Add-WorkSheet -ExcelPackage $ExcelPackage -WorkSheetname $WorkSheetName -ClearSheet:$Clearsheet + foreach ($w in $ExcelPackage.Workbook.Worksheets) {$w.view.TabSelected = $false} $destinationSheet.View.TabSelected = $true $ExcelPackage.Workbook.Worksheets.MoveToEnd($WorkSheetName) #row to insert at will be 1 on a blank sheet and lastrow + 1 on populated one @@ -152,9 +183,10 @@ 'Path', 'Clearsheet', 'NoHeader', 'FromLabel', 'LabelBlocks', 'HideSource', 'Title', 'TitleFillPattern', 'TitleBackgroundColor', 'TitleBold', 'TitleSize' | ForEach-Object {[void]$params.Remove($_)} if ($params.Keys.Count) { + if ($Title) { $params.StartRow = 2} $params.WorkSheetName = $WorkSheetName $params.ExcelPackage = $ExcelPackage - Export-Excel @Params + Export-Excel @Params } else { Close-ExcelPackage -ExcelPackage $ExcelPackage diff --git a/README.md b/README.md index 45ecff4..6423db5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -<<<<<<< HEAD PowerShell Import-Excel - @@ -32,16 +31,17 @@ 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 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. -- Added paramters for managing Axes and legend +# What's new to 6th 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 that -Header does nothing, so it isn't Add-Excel chart and there is a message that does nothing in New-ExcelChartDefinition . +- Added paramters for managing Axes and legend (these are currently in Add-ExcelChart but not new-ExcelChartDefinition) - 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) +- Reverted the [double]::tryParse in export excel to the previous way, as the shorter way 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) This means that if there are multiple inserts only inserted data is touched, rather than going as far down and/or right as the furthest used cell. Added a test for this. +- Added more of the Parameters from Export-Excel to Join-worksheet, join just calls export with these parameters so there is no code behind them (#383) # New in June 18 - New commands - Diff , Merge and Join @@ -841,837 +841,3 @@ You can also find EPPLus on [Nuget](https://www.nuget.org/packages/EPPlus/). * Using `-IncludePivotTable`, if that pivot table name exists, you'll get an error. * Investigating a solution * *Workaround* delete the Excel file first, then do the export -======= -PowerShell Import-Excel -- - -Install from the [PowerShell Gallery](https://www.powershellgallery.com/packages/ImportExcel/). - -This PowerShell Module allows you to read and write Excel files without installing Microsoft Excel on your system. No need to bother with the cumbersome Excel COM-object. Creating Tables, Pivot Tables, Charts and much more has just become a lot easier. - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/testimonial.png) - -# How to Vidoes -* [PowerShell Excel Module - ImportExcel](https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5uoqS92stXioZw-u-ze_NtvSo0k0K0kq) - -Installation -- -#### [PowerShell V5](https://www.microsoft.com/en-us/download/details.aspx?id=50395) and Later -You can install the `ImportExcel` module directly from the PowerShell Gallery - -* [Recommended] Install to your personal PowerShell Modules folder -```PowerShell -Install-Module ImportExcel -scope CurrentUser -``` -* [Requires Elevation] Install for Everyone (computer PowerShell Modules folder) -```PowerShell -Install-Module ImportExcel -``` - -#### PowerShell V4 and Earlier -To install to your personal modules folder (e.g. ~\Documents\WindowsPowerShell\Modules), run: - -```PowerShell -iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfinke/ImportExcel/master/Install.ps1') -``` - -# What's new - -- 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!) - 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 - - I have moved this from ImportExcel.psm1 to ExportExcel.ps1 and it now can move a new worksheet to the right place, and can copy an existing worksheet (from the same or a different workbook) to a new one, and I set the Set return-type to aid intellisense -- New-PivotTableDefinition - - Now Supports `-PivotFilter` and `-PivotDataToColumn`, `-ChartHeight/width` `-ChartRow/Column`, `-ChartRow/ColumnPixelOffset` parameters -- Set-Format - - Fixed a bug where the `-address` parameter had to be named, although the examples in `export-excel` help showed it working by position (which works now. ) -- Export-Excel - - I've done some re-factoring - 1. I "flattened out" small "called-once" functions , add-title, convert-toNumber and Stop-ExcelProcess. - 2. It now uses Add-Worksheet, Open-ExcelPackage and Add-ConditionalFormat instead of duplicating their functionality. - 3. I've moved the PivotTable functionality (which was doubled up) out to a new function "Add-PivotTable" which supports some extra parameters PivotFilter and PivotDataToColumn, ChartHeight/width ChartRow/Column, ChartRow/ColumnPixelOffsets. - 4. I've made the try{} catch{} blocks cover smaller blocks of code to give a better idea where a failure happend, some of these now Warn instead of throwing - I'd rather save the data with warnings than throw it away because we can't add a chart. Along with this I've added some extra write-verbose messages - - Bad column-names specified for Pivots now generate warnings instead of throwing. - - Fixed issues when pivottables / charts already exist and an export tries to create them again. - - Fixed issue where AutoNamedRange, NamedRange, and TableName do not work when appending to a sheet which already contains the range(s) / table - - Fixed issue where AutoNamedRange may try to create ranges with an illegal name. - - Added check for illegal characters in RangeName or Table Name (replace them with "_"), changed tablename validation to allow spaces and applied same validation to RangeName - - Fixed a bug where BoldTopRow is always bolds row 1 even if the export is told to start at a lower row. - - Fixed a bug where titles throw pivot table creation out of alignment. - - Fixed a bug where Append can overwrite the last rows of data if the initial export had blank rows at the top of the sheet. - - Removed the need to specify a fill type when specifying a title background color - - Added MoveToStart, MoveToEnd, MoveBefore and MoveAfter Parameters - these go straight through to Add worksheet - - Added "NoScriptOrAliasProperties" "DisplayPropertySet" switches (names subject to change) - combined with ExcludeProperty these are a quick way to reduce the data exported (and speed things up) - - Added PivotTableName Switch (in line with 5.0.1 release) - - Add-CellValue now understands URI item properties. If a property is of type URI it is created as a hyperlink to speed up Add-CellValue - - 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 - ---- - - -- [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 - - Removed `[PSPlot]` as OutputType. Fixes it throwing an error -- [Nasir Zubair](https://github.com/nzubair) added `ConvertEmptyStringsToNull` to the function `ConvertFrom-ExcelToSQLInsert` - - If specified, cells without any data are replaced with NULL, instead of an empty string. This is to address behviors in certain DBMS where an empty string is insert as 0 for INT column, instead of a NULL value. - - -#### 4/10/2018 --New parameter `-ReZip`. It ReZips the xlsx so it can be imported to PowerBI - -Thanks to [Justin Grote](https://github.com/JustinGrote) for finding and fixing the error that Excel files created do not import to PowerBI online. Plus, thank you to [CrashM](https://github.com/CrashM) for confirming the fix. - -Super helpful! - -#### 3/31/2018 -- Updated `Set-Format` - * Added parameters to set borders for cells, including top, bottm, left and right - * Added parameters to set `value` and `formula` - -```powershell -$data = @" -From,To,RDollars,RPercent,MDollars,MPercent,Revenue,Margin -Atlanta,New York,3602000,.0809,955000,.09,245,65 -New York,Washington,4674000,.105,336000,.03,222,16 -Chicago,New York,4674000,.0804,1536000,.14,550,43 -New York,Philadelphia,12180000,.1427,-716000,-.07,321,-25 -New York,San Francisco,3221000,.0629,1088000,.04,436,21 -New York,Phoneix,2782000,.0723,467000,.10,674,33 -"@ -``` - -![](https://github.com/dfinke/ImportExcel/blob/master/images/CustomReport.png?raw=true) - - -- Added `-PivotFilter` parameter, allows you to set up a filter so you can drill down into a subset of the overall dataset. - -```powershell -$data =@" -Region,Area,Product,Units,Cost -North,A1,Apple,100,.5 -South,A2,Pear,120,1.5 -East,A3,Grape,140,2.5 -West,A4,Banana,160,3.5 -North,A1,Pear,120,1.5 -North,A1,Grape,140,2.5 -"@ -``` - -![](https://github.com/dfinke/ImportExcel/blob/master/images/PivotTableFilter.png?raw=true) - - -#### 3/14/2018 -- Thank you to [James O'Neill](https://twitter.com/jamesoneill), fixed bugs with ChangeDatabase parameter which would prevent it working - -#### -* Added -Force to New-Alias -* Add example to set the background color of a column -* Supports excluding Row Grand Totals for PivotTables -* Allow xlsm files to be read -* Fix `Set-Column.ps1`, `Set-Row.ps1`, `SetFormat.ps1`, `formatting.ps1` **$falsee** and **$BorderRound** -#### 1/1/2018 -* Added switch `[Switch]$NoTotalsInPivot`. Allows hiding of the row totals in the pivot table. -Thanks you to [jameseholt](https://github.com/jameseholt) for the request. - -```powershell - get-process | where Company | select Company, Handles, WorkingSet | - export-excel C:\temp\testColumnGrand.xlsx ` - -Show -ClearSheet -KillExcel ` - -IncludePivotTable -PivotRows Company -PivotData @{"Handles"="average"} -NoTotalsInPivot -``` - -* Fixed when using certain a `ChartType` for the Pivot Table Chart, would throw an error -* Fixed - when you specify a file, and the directory does not exit, it now creates it - -#### 11/23/2017 -More great additions and thanks to [James O'Neill](https://twitter.com/jamesoneill) - -* Added `Convert-XlRangeToImage` Gets the specified part of an Excel file and exports it as an image -* Fixed a typo in the message at line 373. -* Now catch an attempt to both clear the sheet and append to it. -* Fixed some issues when appending to sheets where the header isn't in row 1 or the data doesn't start in column 1. -* Added support for more settings when creating a pivot chart. -* Corrected a typo PivotTableName was PivtoTableName in definition of New-PivotTableDefinition -* Add-ConditionalFormat and Set-Format added to the parameters so each has the choice of working more like the other. -* Added Set-Row and Set-Column - fill a formula down or across. -* Added Send-SQLDataToExcel. Insert a rowset and then call Export-Excel for ranges, charts, pivots etc - -#### 10/30/2017 -Huge thanks to [James O'Neill](https://twitter.com/jamesoneill). PowerShell aficionado. He always brings a flare when working with PowerShell. This is no exception. - -(Check out the examples `help Export-Excel -Examples`) - -* New parameter `Package` allows an ExcelPackage object returned by `-passThru` to be passed in -* New parameter `ExcludeProperty` to remove unwanted properties without needing to go through `select-object` -* New parameter `Append` code to read the existing headers and move the insertion point below the current data -* New parameter `ClearSheet` which removes the worksheet and any past data - -* Remove any existing Pivot table before trying to [re]create it -* Check for inserting a pivot table so if `-InsertPivotChart` is specified it implies `-InsertPivotTable` - -(Check out the examples `help Export-Excel -Examples`) - -* New function `Export-Charts` (requires Excel to be installed) - Export Excel charts out as JPG files -* New function `Add-ConditionalFormatting` Adds contitional formatting to worksheet -* New function `Set-Format` Applies Number, font, alignment and colour formatting to a range of Excel Cells -* `ColorCompletion` an argument completer for `Colors` for params across functions - -I also worked out the parameters so you can do this, which is the same as passing `-Now`. It creates an Excel file name for you, does an auto fit and sets up filters. - -`ps | select Company, Handles | Export-Excel` - -#### 10/13/2017 -Added `New-PivotTableDefinition`. You can create and wire up a PivotTable to a WorkSheet. You can also create as many PivotTable Worksheets to point a one Worksheet. Or, you create many Worksheets and many corresponding PivotTable Worksheets. - -Here you can create a WorkSheet with the data from `Get-Service`. Then create four PivotTables, pointing to the data each pivoting on a differnt dimension and showing a differnet chart - -```powershell -$base = @{ - SourceWorkSheet = 'gsv' - PivotData = @{'Status' = 'count'} - IncludePivotChart = $true -} - -$ptd = [ordered]@{} - -$ptd += New-PivotTableDefinition @base servicetype -PivotRows servicetype -ChartType Area3D -$ptd += New-PivotTableDefinition @base status -PivotRows status -ChartType PieExploded3D -$ptd += New-PivotTableDefinition @base starttype -PivotRows starttype -ChartType BarClustered3D -$ptd += New-PivotTableDefinition @base canstop -PivotRows canstop -ChartType ConeColStacked - -Get-Service | Export-Excel -path $file -WorkSheetname gsv -Show -PivotTableDefinition $ptd -``` - -#### 10/4/2017 -Thanks to https://github.com/ili101 : -- Fix Bug, Unable to find type [PSPlot] -- Fix Bug, AutoFilter with TableName create corrupted Excel file. - -#### 10/2/2017 -Thanks to [Jeremy Brun](https://github.com/jeremytbrun) -Fixed issues related to use of -Title parameter combined with column formatting parameters. -- [Issue #182](https://github.com/dfinke/ImportExcel/issues/182) -- [Issue #89](https://github.com/dfinke/ImportExcel/issues/89) - -#### 9/28/2017 (Version 4.0.1) -- Added a new parameter called `Password` to import password protected files -- Added even more `Pester` tests for a more robust and bug free module -- Renamed parameter 'TopRow' to 'StartRow' - This allows us to be more concise when new parameters ('StartColumn', ..) will be added in the future Your code will not break after the update, because we added an alias for backward compatibility - -Special thanks to [robinmalik](https://github.com/robinmalik) for providing us with [the code](https://github.com/dfinke/ImportExcel/issues/174) to implement this new feature. A high five to [DarkLite1](https://github.com/DarkLite1) for the implementation. - -#### 9/12/2017 (Version 4.0.0) - -Super thanks and hat tip to [DarkLite1](https://github.com/DarkLite1). There is now a new and improved `Import-Excel`, not only in functionality, but also improved readability, examples and more. Not only that, he's been running it in production in his company for a number of weeks! - -*Added* `Update-FirstObjectProperties` Updates the first object to contain all the properties of the object with the most properties in the array. Check out the help. - - -***Breaking Changes***: Due to a big portion of the code that is rewritten some slightly different behavior can be expected from the `Import-Excel` function. This is especially true for importing empty Excel files with or without using the `TopRow` parameter. To make sure that your code is still valid, please check the examples in the help or the accompanying `Pester` test file. - - -Moving forward, we are planning to include automatic testing with the help of `Pester`, `Appveyor` and `Travis`. From now on any changes in the module will have to be accompanied by the corresponding `Pester` tests to avoid breakages of code and functionality. This is in preparation for new features coming down the road. - -#### 7/3/2017 -Thanks to [Mikkel Nordberg](https://www.linkedin.com/in/mikkelnordberg). He contributed a `ConvertTo-ExcelXlsx`. To use it, Excel needs to be installed. The function converts the older Excel file format ending in `.xls` to the new format ending in `.xlsx`. - -#### 6/15/2017 -Huge thank you to [DarkLite1](https://github.com/DarkLite1)! Refactoring of code, adding help, adding features, fixing bugs. Specifically this long outstanding one: - -[Export-Excel: Numeric values not correct](https://github.com/dfinke/ImportExcel/issues/168) - -It is fantastic to work with people like `DarkLite1` in the community, to help make the module so much better. A hat to you. - -Another shout out to [Damian Reeves](https://twitter.com/DamReev)! His questions turn into great features. He asked if it was possible to import an Excel worksheet and transform the data into SQL `INSERT` statements. We can now answer that question with a big YES! - -```PowerShell -ConvertFrom-ExcelToSQLInsert People .\testSQLGen.xlsx -``` - -``` -INSERT INTO People ('First', 'Last', 'The Zip') Values('John', 'Doe', '12345'); -INSERT INTO People ('First', 'Last', 'The Zip') Values('Jim', 'Doe', '12345'); -INSERT INTO People ('First', 'Last', 'The Zip') Values('Tom', 'Doe', '12345'); -INSERT INTO People ('First', 'Last', 'The Zip') Values('Harry', 'Doe', '12345'); -INSERT INTO People ('First', 'Last', 'The Zip') Values('Jane', 'Doe', '12345'); -``` -## Bonus Points -Use the underlying `ConvertFrom-ExcelData` function and you can use a scriptblock to format the data however you want. - -```PowerShell -ConvertFrom-ExcelData .\testSQLGen.xlsx { - param($propertyNames, $record) - - $reportRecord = @() - foreach ($pn in $propertyNames) { - $reportRecord += "{0}: {1}" -f $pn, $record.$pn - } - $reportRecord +="" - $reportRecord -join "`r`n" -} -``` -Generates - -``` -First: John -Last: Doe -The Zip: 12345 - -First: Jim -Last: Doe -The Zip: 12345 - -First: Tom -Last: Doe -The Zip: 12345 - -First: Harry -Last: Doe -The Zip: 12345 - -First: Jane -Last: Doe -The Zip: 12345 -``` - -#### 2/2/2017 -Thank you to [DarkLite1](https://github.com/DarkLite1) for more updates -* TableName with parameter validation, throws an error when the TableName: - - Starts with something else then a letter - - Is NULL or empty - - Contains spaces -- Numeric parsing now uses `CurrentInfo` to use the system settings - -#### 2/14/2017 -Big thanks to [DarkLite1](https://github.com/DarkLite1) for some great updates -* `-DataOnly` switch added to `Import-Excel`. When used it will only generate objects for rows that contain text values, not for empty rows or columns. - -* `Get-ExcelWorkBookInfo` - retrieves information of an Excel workbook. -``` - Get-ExcelWorkbookInfo .\Test.xlsx - - CorePropertiesXml : #document - Title : - Subject : - Author : Konica Minolta User - Comments : - Keywords : - LastModifiedBy : Bond, James (London) GBR - LastPrinted : 2017-01-21T12:36:11Z - Created : 17/01/2017 13:51:32 - Category : - Status : - ExtendedPropertiesXml : #document - Application : Microsoft Excel - HyperlinkBase : - AppVersion : 14.0300 - Company : Secret Service - Manager : - Modified : 10/02/2017 12:45:37 - CustomPropertiesXml : #document -``` - -#### 12/22/2016 -- Added `-Now` switch. This short cuts the process, automatically creating a temp file and enables the `-Show`, `-AutoFilter`, `-AutoSize` switches. - -```PowerShell -Get-Process | Select Company, Handles | Export-Excel -Now -``` - -- Added ScriptBlocks for coloring cells. Check out [Examples](https://github.com/dfinke/ImportExcel/tree/master/Examples/FormatCellStyles) - -```PowerShell -Get-Process | - Select-Object Company,Handles,PM, NPM| - Export-Excel $xlfile -Show -AutoSize -CellStyleSB { - param( - $workSheet, - $totalRows, - $lastColumn - ) - - Set-CellStyle $workSheet 1 $LastColumn Solid Cyan - - foreach($row in (2..$totalRows | Where-Object {$_ % 2 -eq 0})) { - Set-CellStyle $workSheet $row $LastColumn Solid Gray - } - - foreach($row in (2..$totalRows | Where-Object {$_ % 2 -eq 1})) { - Set-CellStyle $workSheet $row $LastColumn Solid LightGray - } - } -``` -![](https://github.com/dfinke/ImportExcel/blob/master/images/CellFormatting.png?raw=true) - -#### 9/28/2016 -[Fixed](https://github.com/dfinke/ImportExcel/pull/126) PowerShell 3.0 compatibility. Thanks to [headsphere](https://github.com/headsphere). He used `$obj.PSObject.Methods[$target]` snytax to make it backward compatible. PS v4.0 and later allow `$obj.$target`. - -Thank you to [xelsirko](https://github.com/xelsirko) for fixing - *Import-module importexcel gives version warning if started inside background job* - -#### 8/12/2016 -[Fixed](https://github.com/dfinke/ImportExcel/issues/115) reading the headers from cells, moved from using `Text` property to `Value` property. - -#### 7/30/2016 -* Added `Copy-ExcelWorksheet`. Let's you copy a work sheet from one Excel workbook to another. - -#### 7/21/2016 -* Fixes `Import-Excel` #68 - -#### 7/7/2016 -[Attila Mihalicz](https://github.com/attilamihalicz) fixed two issues - -* Removing extra spaces after the backtick -* Uninitialized variable $idx leaks into the pipeline when `-TableName` parameter is used - -Thanks Attila. - - -#### 7/1/2016 -* Pushed 2.2.7 fixed resolve path in Get-ExcelSheetInfo -* Fixed [Casting Error in Export-Excel](https://github.com/dfinke/ImportExcel/issues/108) -* For `Import-Excel` change Resolve-Path to return ProviderPath for use with UNC - -#### 6/01/2016 -* Added -UseDefaultCredentials to both `Import-Html` and `Get-HtmlTable` -* New functions, `Import-UPS` and `Import-USPS`. Pass in a valid tracking # and it scrapes the page for the delivery details - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/Tracking.gif) - -#### 4/30/2016 -Huge thank you to [Willie Möller](https://github.com/W1M0R) - -* He added a version check so the PowerShell Classes don't cause issues for downlevel version of PowerShell -* He also contributed the first Pester tests for the module. Super! Check them out, they'll be the way tests will be implemented going forward - -#### 4/18/2016 -Thanks to [Paul Williams](https://github.com/pauldalewilliams) for this feature. Now data can be transposed to columns for better charting. - -```PowerShell -$file = "C:\Temp\ps.xlsx" -rm $file -ErrorAction Ignore - -ps | - where company | - select Company,PagedMemorySize,PeakPagedMemorySize | - Export-Excel $file -Show -AutoSize ` - -IncludePivotTable ` - -IncludePivotChart ` - -ChartType ColumnClustered ` - -PivotRows Company ` - -PivotData @{PagedMemorySize='sum';PeakPagedMemorySize='sum'} -``` -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PivotAsRows.png) - - -Add `-PivotDataToColumn` - -```PowerShell -$file = "C:\Temp\ps.xlsx" -rm $file -ErrorAction Ignore - -ps | - where company | - select Company,PagedMemorySize,PeakPagedMemorySize | - Export-Excel $file -Show -AutoSize ` - -IncludePivotTable ` - -IncludePivotChart ` - -ChartType ColumnClustered ` - -PivotRows Company ` - -PivotData @{PagedMemorySize='sum';PeakPagedMemorySize='sum'} ` - -PivotDataToColumn -``` -And here is the new chart view -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PivotAsColumns.png) -#### 4/7/2016 -Made more methods fluent -``` -$t=Get-Range 0 5 .2 - -$t2=$t|%{$_*$_} -$t3=$t|%{$_*$_*$_} - -(New-Plot). - Plot($t,$t, $t,$t2, $t,$t3). - SetChartPosition("i"). - SetChartSize(500,500). - Title("Hello World"). - Show() -``` -#### 3/31/2016 -* Thanks to [redoz](https://github.com/redoz) Multi Series Charts are now working - -Also check out how you can create a table and then with Excel notation, index into the data for charting `"Impressions[A]"` - -``` -$data = @" -A,B,C,Date -2,1,1,2016-03-29 -5,10,1,2016-03-29 -"@ | ConvertFrom-Csv - -$c = New-ExcelChart -Title Impressions ` - -ChartType Line -Header "Something" ` - -XRange "Impressions[Date]" ` - -YRange @("Impressions[B]","Impressions[A]") - -$data | - Export-Excel temp.xlsx -AutoSize -TableName Impressions -Show -ExcelChartDefinition $c -``` -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/MultiSeries.gif) - -#### 3/26/2016 -* Added `NumberFormat` parameter - -``` -$data | - Export-Excel -Path $file -Show -NumberFormat '[Blue]$#,##0.00;[Red]-$#,##0.00' -``` -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/Formatting.png) - - -#### 3/18/2016 -* Added `Get-Range`, `New-Plot` and Plot Cos example -* Updated EPPlus DLL. Allows markers to be changed and colored -* Handles and warns if auto name range names are also valid Excel ranges - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PSPlot.gif) - -#### 3/7/2016 -* Added `Header` and `FirstDataRow` for `Import-Html` - -#### 3/2/2016 -* Added `GreaterThan`, `GreaterThanOrEqual`, `LessThan`, `LessThanOrEqual` to `New-ConditionalText` - -```PowerShell -echo 489 668 299 777 860 151 119 497 234 788 | - Export-Excel c:\temp\test.xlsx -Show ` - -ConditionalText (New-ConditionalText -ConditionalType GreaterThan 525) -``` -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/GTConditional.png) - -#### 2/22/2016 -* `Import-Html` using Lee Holmes [Extracting Tables from PowerShell’s Invoke-WebRequest](http://www.leeholmes.com/blog/2015/01/05/extracting-tables-from-PowerShells-invoke-webrequest/) - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/ImportHtml.gif) - -#### 2/17/2016 -* Added Conditional Text types of `Equal` and `NotEqual` -* Phone #'s like '+33 011 234 34' will be now be handled correctly - -## Try *PassThru* - -```PowerShell -$file = "C:\Temp\passthru.xlsx" -rm $file -ErrorAction Ignore - -$xlPkg = $( - New-PSItem north 10 - New-PSItem east 20 - New-PSItem west 30 - New-PSItem south 40 -) | Export-Excel $file -PassThru - -$ws=$xlPkg.Workbook.Worksheets[1] - -$ws.Cells["A3"].Value = "Hello World" -$ws.Cells["B3"].Value = "Updating cells" -$ws.Cells["D1:D5"].Value = "Data" - -$ws.Cells.AutoFitColumns() - -$xlPkg.Save() -$xlPkg.Dispose() - -Invoke-Item $file -``` - -## Result -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PassThru.png) - -#### 1/18/2016 - -* Added `Conditional Text Formatting`. [Boe Prox](https://twitter.com/proxb) posted about [HTML Reporting, Part 2: Take Your Reporting a Step Further](https://mcpmag.com/articles/2016/01/14/html-reporting-part-2.aspx) and colorized cells. Great idea, now part of the PowerShell Excel module. - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/ConditionalText2.gif) - -#### 1/7/2016 -* Added `Get-ExcelSheetInfo` - Great contribution from *Johan Åkerström* check him out on [GitHub](https://github.com/CosmosKey) and [Twitter](https://twitter.com/neptune443) - -![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/GetExcelSheetInfo.png) - -#### 12/26/2015 - -* Added `NoLegend`, `Show-Category`, `ShowPercent` for all charts including Pivot Charts -* Updated PieChart, BarChart, ColumnChart and Line chart to work with the pipeline and added `NoLegend`, `Show-Category`, `ShowPercent` - -#### 12/17/2015 - -These new features open the door for really sophisticated work sheet creation. - -Stay tuned for a [blog post](http://www.dougfinke.com/blog/) and examples. - -***Quick List*** -* StartRow, StartColumn for placing data anywhere in a sheet -* New-ExcelChart - Add charts to a sheet, multiple series for a chart, locate the chart anywhere on the sheet -* AutoNameRange, Use functions and/or calculations in a cell -* Quick charting using PieChart, BarChart, ColumnChart and more - -![](https://raw.githubusercontent.com/dfinke/GifCam/master/JustCharts.gif) - -#### 10/20/2015 - -Big bug fix for version 3.0 PowerShell folks! - -This technique fails in 3.0 and works in 4.0 and later. -```PowerShell -$m="substring" -"hello".$m(2,1) -``` - -Adding `.invoke` works in 3.0 and later. - -```PowerShell -$m="substring" -"hello".$m.invoke(2,1) -``` - -A ***big thank you*** to [DarkLite1](https://github.com/DarkLite1) for adding the help to Export-Excel. - -Added `-HeaderRow` parameter. Sometimes the heading does not start in Row 1. - - -#### 10/16/2015 - -Fixes [Export-Excel generates corrupt Excel file](https://github.com/dfinke/ImportExcel/issues/46) - -#### 10/15/2015 - -`Import-Excel` has a new parameter `NoHeader`. If data in the sheet does not have headers and you don't want to supply your own, `Import-Excel` will generate the property name. - -`Import-Excel` now returns `.Value` rather than `.Text` - - -#### 10/1/2015 - -Merged ValidateSet for Encoding and Extension. Thank you [Irwin Strachan](https://github.com/irwins). - -#### 9/30/2015 - -Export-Excel can now handle data that is **not** an object - - echo a b c 1 $true 2.1 1/1/2015 | Export-Excel c:\temp\test.xlsx -Show -Or - - dir -Name | Export-Excel c:\temp\test.xlsx -Show - -#### 9/25/2015 - -**Hide worksheets** -Got a great request from [forensicsguy20012004](https://github.com/forensicsguy20012004) to hide worksheets. You create a few pivotables, generate charts and then pivotable worksheets don't need to be visible. - -`Export-Excel` now has a `-HideSheet` parameter that takes and array of worksheet names and hides them. - -##### Example -Here, you create four worksheets named `PM`,`Handles`,`Services` and `Files`. - -The last line creates the `Files` sheet and then hides the `Handles`,`Services` sheets. - - $p = Get-Process - - $p|select company, pm | Export-Excel $xlFile -WorkSheetname PM - $p|select company, handles| Export-Excel $xlFile -WorkSheetname Handles - Get-Service| Export-Excel $xlFile -WorkSheetname Services - - dir -File | Export-Excel $xlFile -WorkSheetname Files -Show -HideSheet Handles, Services - - -**Note** There is a bug in EPPlus that does not let you hide the first worksheet created. Hopefully it'll resolved soon. - -#### 9/11/2015 - -Added Conditional formatting. See [TryConditional.ps1](https://github.com/dfinke/ImportExcel/blob/master/TryConditional.ps1) as an example. - -Or, check out the short ***"How To"*** video. - -[![image](http://www.dougfinke.com/videos/excelpsmodule/ExcelPSModule_First_Frame.png)](http://www.dougfinke.com/videos/excelpsmodule/excelpsmodule.mp4) - - -#### 8/21/2015 -* Now import Excel sheets even if the file is open in Excel. Thank you [Francois Lachance-Guillemette](https://github.com/francoislg) - -#### 7/09/2015 -* For -PivotRows you can pass a `hashtable` with the name of the property and the type of calculation. `Sum`, `Average`, `Max`, `Min`, `Product`, `StdDev`, `StdDevp`, `Var`, `Varp` - -```PowerShell -Get-Service | - Export-Excel "c:\temp\test.xlsx" ` - -Show ` - -IncludePivotTable ` - -PivotRows status ` - -PivotData @{status='count'} -``` - -#### 6/16/2015 (Thanks [Justin](https://github.com/zippy1981)) -* Improvements to PivotTable overwriting -* Added two parameters to Export-Excel - * RangeName - Turns the data piped to Export-Excel into a named range. - * TableName - Turns the data piped to Export-Excel into an excel table. - -Examples - - Get-Process|Export-Excel foo.xlsx -Verbose -IncludePivotTable -TableName "Processes" -Show - Get-Process|Export-Excel foo.xlsx -Verbose -IncludePivotTable -RangeName "Processes" -Show - - -#### 5/25/2015 -* Fixed null header problem - -#### 5/17/2015 -* Added three parameters: - * FreezeTopRow - Freezes the first row of the data - * AutoFilter - Enables filtering for the data in the sheet - * BoldTopRow - Bolds the top row of data, the column headers - -Example - - Get-CimInstance win32_service | - select state, accept*, start*, caption | - Export-Excel test.xlsx -Show -BoldTopRow -AutoFilter -FreezeTopRow -AutoSize - -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/FilterFreezeBold.gif) - - -#### 5/4/2015 -* Published to PowerShell Gallery. In PowerShell v5 use `Find-Module importexcel` then `Find-Module importexcel | Install-Module` - - -#### 4/27/2015 -* datetime properties were displaying as ints, now are formatted - -#### 4/25/2015 -* Now you can create multiple Pivot tables in one pass - * Thanks to [pscookiemonster](https://twitter.com/pscookiemonster), he submitted a repro case to the EPPlus CodePlex project and got it fixed - -#### Example - - $ps = ps - - $ps | - Export-Excel .\testExport.xlsx -WorkSheetname memory ` - -IncludePivotTable -PivotRows Company -PivotData PM ` - -IncludePivotChart -ChartType PieExploded3D - $ps | - Export-Excel .\testExport.xlsx -WorkSheetname handles ` - -IncludePivotTable -PivotRows Company -PivotData Handles ` - -IncludePivotChart -ChartType PieExploded3D -Show - -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/MultiplePivotTables.png) - -#### 4/20/2015 -* Included and embellished [Claus Nielsen](https://github.com/Claustn) function to take all sheets in an Excel file workbook and create a text file for each `ConvertFrom-ExcelSheet` -* Renamed `Export-MultipleExcelSheets` to `ConvertFrom-ExcelSheet` - -#### 4/13/2015 -* You can add a title to the Excel "Report" `Title`, `TitleFillPattern`, `TitleBold`, `TitleSize`, `TitleBackgroundColor` - * Thanks to [Irwin Strachan](http://pshirwin.wordpress.com) for this and other great suggestions, testing and more - - -#### 4/10/2015 -* Renamed `AutoFitColumns` to `AutoSize` -* Implemented `Export-MultipleExcelSheets` -* Implemented `-Password` for a worksheet -* Replaced `-Force` switch with `-NoClobber` switch -* Added examples for `Get-Help` -* If Pivot table is requested, that sheet becomes the tab selected - -#### 4/8/2015 -* Implemented exporting data to **named sheets** via the -WorkSheetname parameter. - -Examples -- -`gsv | Export-Excel .\test.xlsx -WorkSheetname Services` - -`dir -file | Export-Excel .\test.xlsx -WorkSheetname Files` - -`ps | Export-Excel .\test.xlsx -WorkSheetname Processes -IncludePivotTable -Show -PivotRows Company -PivotData PM` - -#### Convert (All or Some) Excel Sheets to Text files - -Reads each sheet in TestSheets.xlsx and outputs it to the data directory as the sheet name with the extension .txt - - ConvertFrom-ExcelSheet .\TestSheets.xlsx .\data - -Reads and outputs sheets like Sheet10 and Sheet20 form TestSheets.xlsx and outputs it to the data directory as the sheet name with the extension .txt - - ConvertFrom-ExcelSheet .\TestSheets.xlsx .\data sheet?0 - -#### Example Adding a Title -You can set the pattern, size and of if the title is bold. - - $p=@{ - Title = "Process Report as of $(Get-Date)" - TitleFillPattern = "LightTrellis" - TitleSize = 18 - TitleBold = $true - - Path = "$pwd\testExport.xlsx" - Show = $true - AutoSize = $true - } - - Get-Process | - Where Company | Select Company, PM | - Export-Excel @p - -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/Title.png) - -#### Example Export-MultipleExcelSheets -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/ExportMultiple.gif) - - $p = Get-Process - - $DataToGather = @{ - PM = {$p|select company, pm} - Handles = {$p|select company, handles} - Services = {gsv} - Files = {dir -File} - Albums = {(Invoke-RestMethod http://www.dougfinke.com/PowerShellfordevelopers/albums.js)} - } - - Export-MultipleExcelSheets -Show -AutoSize .\testExport.xlsx $DataToGather - - - -***NOTE*** If the sheet exists when using *-WorkSheetname* parameter, it will be deleted and then added with the new data. - -## Get-Process Exported to Excel - -### Total Physical Memory Grouped By Company -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/PivotTablesAndCharts.png) - -## Importing data from an Excel spreadsheet - -![image](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/TryImportExcel.gif) - -You can also find EPPLus on [Nuget](https://www.nuget.org/packages/EPPlus/). - -## Known Issues - -* Using `-IncludePivotTable`, if that pivot table name exists, you'll get an error. - * Investigating a solution - * *Workaround* delete the Excel file first, then do the export ->>>>>>> 9f7884f991c80448091ef56853027f64d98b6cc7 diff --git a/ToDo.md b/ToDo.md index 82fad23..304b468 100644 --- a/ToDo.md +++ b/ToDo.md @@ -1 +1,4 @@ - [ ] Create an autocomplete for WorkSheetName param on ImportExcel +- [ ] Add help text for parmaters which don't have it in Export Excel +- [ ] Add checks for valid worksheet names (also check pivot names, range names and table names are valid) +- [ ] Investigate regional support for number conversion \ No newline at end of file diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 index 14d3765..541c7ae 100644 --- a/__tests__/Export-Excel.Tests.ps1 +++ b/__tests__/Export-Excel.Tests.ps1 @@ -686,6 +686,43 @@ Describe ExportExcel { Close-ExcelPackage -ExcelPackage $excel -nosave } + Context " # Awkward multiple tables" { + $path = "$Env:TEMP\test.xlsx" + remove-item -Path $path -ErrorAction SilentlyContinue + $r = Get-ChildItem -path C:\WINDOWS\system32 -File + + "Biggest files" | Export-Excel -Path $path -StartRow 1 -StartColumn 7 + $r | Sort-Object length -Descending | Select -First 14 Name, @{n="Size";e={$_.Length}} | + Export-Excel -Path $path -TableName FileSize -StartRow 2 -StartColumn 7 -TableStyle Medium2 + + $r.extension | Group-Object | Sort-Object -Property count -Descending | Select-Object -First 12 Name, Count | + Export-Excel -Path $path -TableName ExtSize -Title "Frequent Extensions" -TitleSize 11 + + $r | Group-Object -Property extension | Select-Object Name, @{n="Size"; e={($_.group | measure -property length -sum).sum}} | + Sort-Object -Property size -Descending | Select-Object -First 10 | + Export-Excel -Path $path -TableName ExtCount -Title "Biggest extensions" -TitleSize 11 -StartColumn 4 -AutoSize + + $excel = Open-ExcelPackage -Path $path + $ws = $excel.Workbook.Worksheets[1] + it "Created 3 tables " { + $ws.tables.count | should be 3 + } + it "Created the FileSize table in the right places with the right size " { + $ws.Tables["FileSize"].Address.Address | should be "G2:H16" #Insert at row 2, Column 7, 14 rows x 2 columns of data + $ws.Tables["FileSize"].StyleName | should be "TableStyleMedium2" + } + it "Created the ExtSize table in the right places with the right size " { + $ws.Tables["ExtSize"].Address.Address | should be "A2:B14" #tile, then 12 rows x 2 columns of data + $ws.Tables["ExtSize"].TableStyle.tostring() | should be "medium6" + } + it "Created the ExtSize table in the right places with the right size " { + $ws.Tables["ExtSize"].Address.Address | should be "A2:B14" #tile, then 12 rows x 2 columns of data + $ws.Tables["ExtSize"].TableStyle.tostring() | should be "medium6" + } + } + + + ## To do ## More Charts , pivot options & other FreezePanes settings ? ## Style script block From 75676a810f454d13de76da43f57a220e5d760ec3 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sat, 7 Jul 2018 12:00:14 +0100 Subject: [PATCH 04/21] Fix to parameter set detection in Export-Excel Parameter set problem was causing append to demand a table name. Tweaked eample in join worksheet, added another join worksheet sample --- .../Join-Worksheet/Join-worksheet-blocks.sample.ps1 | 11 +++++++++++ Export-Excel.ps1 | 4 ++-- Join-Worksheet.ps1 | 4 ++-- README.md | 5 +++-- 4 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 Examples/Join-Worksheet/Join-worksheet-blocks.sample.ps1 diff --git a/Examples/Join-Worksheet/Join-worksheet-blocks.sample.ps1 b/Examples/Join-Worksheet/Join-worksheet-blocks.sample.ps1 new file mode 100644 index 0000000..81976f4 --- /dev/null +++ b/Examples/Join-Worksheet/Join-worksheet-blocks.sample.ps1 @@ -0,0 +1,11 @@ + +$path = "$env:TEMP\Test.xlsx" +Remove-item -Path $path -ErrorAction SilentlyContinue + Get-WmiObject -Class win32_logicaldisk | + Select-Object -Property DeviceId,VolumeName, Size,Freespace | + Export-Excel -Path $path -WorkSheetname Volumes -NumberFormat "0,000" +Get-NetAdapter | + Select-Object -Property Name,InterfaceDescription,MacAddress,LinkSpeed | + Export-Excel -Path $path -WorkSheetname NetAdapters + +Join-Worksheet -Path $path -HideSource -WorkSheetName Summary -NoHeader -LabelBlocks -AutoSize -Title "Summary" -TitleBold -TitleSize 22 -show diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index a93367a..94964e2 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -390,8 +390,8 @@ elseif ($_[0] -notmatch '[a-z]') { throw 'Tablename starts with an invalid character.' } else { $true } })] - [Parameter(ParameterSetName = 'Table' , Mandatory = $true)] - [Parameter(ParameterSetName = 'PackageTable' , Mandatory = $true)] + [Parameter(ParameterSetName = 'Table' , Mandatory = $true, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'PackageTable' , Mandatory = $true, ValueFromPipelineByPropertyName)] [String]$TableName, [Parameter(ParameterSetName = 'Table')] [Parameter(ParameterSetName = 'PackageTable')] diff --git a/Join-Worksheet.ps1 b/Join-Worksheet.ps1 index fe4aa02..0779f29 100644 --- a/Join-Worksheet.ps1 +++ b/Join-Worksheet.ps1 @@ -24,10 +24,10 @@ .EXAMPLE Get-WmiObject -Class win32_logicaldisk | select -Property DeviceId,VolumeName, Size,Freespace | - Export-Excel -Path "$env:computerName.xlsx" -WorkSheetname Volumes + Export-Excel -Path "$env:computerName.xlsx" -WorkSheetname Volumes -NumberFormat "0,000" Get-NetAdapter | Select-Object Name,InterfaceDescription,MacAddress,LinkSpeed | Export-Excel -Path "$env:COMPUTERNAME.xlsx" -WorkSheetname NetAdapter - Join-Worksheet -Path "$env:COMPUTERNAME.xlsx" -WorkSheetName Summay -Title "Summary" -TitleBold -TitleSize 22 -NoHeader -LabelBlocks -AutoSize -HideSource + Join-Worksheet -Path "$env:COMPUTERNAME.xlsx" -WorkSheetName Summary -Title "Summary" -TitleBold -TitleSize 22 -NoHeader -LabelBlocks -AutoSize -HideSource -show The first two command get logical disk and network card information; each type is exported to its own sheet in a workbook. The Join-worksheet command copies both onto a page named "Summary". Because the data is disimilar -NoHeader is specified, ensuring the whole of each page is copied. diff --git a/README.md b/README.md index 6423db5..cda80e7 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,15 @@ 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 6th July 18 +# What's new to 7th 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 that -Header does nothing, so it isn't Add-Excel chart and there is a message that does nothing in New-ExcelChartDefinition . - Added paramters for managing Axes and legend (these are currently in Add-ExcelChart but not new-ExcelChartDefinition) - 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 +- 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 +- Fixed an issue with parameter sets in Export-Excel not being determined correctly in some case (I think this has been resolved before and might have regressed) - Reverted the [double]::tryParse in export excel to the previous way, as the shorter way 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) This means that if there are multiple inserts only inserted data is touched, rather than going as far down and/or right as the furthest used cell. Added a test for this. - Added more of the Parameters from Export-Excel to Join-worksheet, join just calls export with these parameters so there is no code behind them (#383) From 9a81ddeebd4446bf02f5b0e166e03eb14fd27e2b Mon Sep 17 00:00:00 2001 From: jhoneill Date: Tue, 10 Jul 2018 13:37:40 +0100 Subject: [PATCH 05/21] Relayout of Setformat for readablity --- SetFormat.ps1 | 53 +++++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/SetFormat.ps1 b/SetFormat.ps1 index d4a1928..12b6962 100644 --- a/SetFormat.ps1 +++ b/SetFormat.ps1 @@ -92,27 +92,31 @@ } else { if ($ResetFont) { - $Address.Style.Font.Color.SetColor("Black") - $Address.Style.Font.Bold = $false - $Address.Style.Font.Italic = $false - $Address.Style.Font.UnderLine = $false - $Address.Style.Font.Strike = $false + $Address.Style.Font.Color.SetColor("Black") + $Address.Style.Font.Bold = $false + $Address.Style.Font.Italic = $false + $Address.Style.Font.UnderLine = $false + $Address.Style.Font.Strike = $false } - if ($Underline) { - $Address.Style.Font.UnderLine = $true - $Address.Style.Font.UnderLineType = $UnderLineType + if ($Underline) { + $Address.Style.Font.UnderLine = $true + $Address.Style.Font.UnderLineType = $UnderLineType } - if ($Bold) {$Address.Style.Font.Bold = $true } - if ($Italic) {$Address.Style.Font.Italic = $true } - if ($StrikeThru) {$Address.Style.Font.Strike = $true } - if ($FontShift) {$Address.Style.Font.VerticalAlign = $FontShift } - if ($FontColor) {$Address.Style.Font.Color.SetColor( $FontColor ) } - - if ($BorderAround) { - $Address.Style.Border.BorderAround($BorderAround, $BorderColor) - } + if ($Bold) {$Address.Style.Font.Bold = $true } + if ($Italic) {$Address.Style.Font.Italic = $true } + if ($StrikeThru) {$Address.Style.Font.Strike = $true } + if ($FontShift) {$Address.Style.Font.VerticalAlign = $FontShift } + if ($FontColor) {$Address.Style.Font.Color.SetColor( $FontColor ) } + if ($NumberFormat) {$Address.Style.Numberformat.Format = $NumberFormat } + if ($TextRotation) {$Address.Style.TextRotation = $TextRotation } + if ($WrapText) {$Address.Style.WrapText = $true } + if ($HorizontalAlignment) {$Address.Style.HorizontalAlignment = $HorizontalAlignment } + if ($VerticalAlignment) {$Address.Style.VerticalAlignment = $VerticalAlignment } + if ($Value) {$Address.Value = $Value } + if ($Formula) {$Address.Formula = $Formula } + if ($BorderAround) {$Address.Style.Border.BorderAround($BorderAround, $BorderColor)} - if ($BorderBottom) { + if ($BorderBottom) { $Address.Style.Border.Bottom.Style=$BorderBottom $Address.Style.Border.Bottom.Color.SetColor($BorderColor) } @@ -132,12 +136,6 @@ $Address.Style.Border.Right.Color.SetColor($BorderColor) } - if ($NumberFormat) {$Address.Style.Numberformat.Format = $NumberFormat } - if ($TextRotation) {$Address.Style.TextRotation = $TextRotation } - if ($WrapText) {$Address.Style.WrapText = $true } - if ($HorizontalAlignment) {$Address.Style.HorizontalAlignment = $HorizontalAlignment } - if ($VerticalAlignment) {$Address.Style.VerticalAlignment = $VerticalAlignment } - if ($BackgroundColor) { $Address.Style.Fill.PatternType = $BackgroundPattern $Address.Style.Fill.BackgroundColor.SetColor($BackgroundColor) @@ -179,13 +177,6 @@ else {Write-Warning -Message ("Can hide a row or a column but not a {0} object" -f ($Address.GetType().name)) } } - if ($Value) { - $Address.Value = $Value - } - - if ($Formula) { - $Address.Formula = $Formula - } } } } \ No newline at end of file From bfbba90c4442f988a8c0544d55e97864a461fbf8 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Tue, 10 Jul 2018 13:43:15 +0100 Subject: [PATCH 06/21] Updates to Help text - mostly layout, inc making it VSCode freindly --- AddConditionalFormatting.ps1 | 6 +- ConvertExcelToImageFile.ps1 | 98 ++++++++++++++++---------------- ConvertFromExcelData.ps1 | 32 +++++------ ConvertFromExcelToSQLInsert.ps1 | 46 +++++---------- Export-charts.ps1 | 4 +- Get-ExcelSheetInfo.ps1 | 16 ++---- Get-ExcelWorkbookInfo.ps1 | 15 ++--- Set-Column.ps1 | 24 ++++---- Set-Row.ps1 | 39 +++++++------ Update-FirstObjectProperties.ps1 | 3 +- formatting.ps1 | 50 ++++++++-------- 11 files changed, 155 insertions(+), 178 deletions(-) diff --git a/AddConditionalFormatting.ps1 b/AddConditionalFormatting.ps1 index 6d35dbd..db18fc6 100644 --- a/AddConditionalFormatting.ps1 +++ b/AddConditionalFormatting.ps1 @@ -32,14 +32,14 @@ [Parameter(Mandatory = $true, ParameterSetName = "FourIconSet")] [Parameter(Mandatory = $true, ParameterSetName = "FiveIconSet")] [OfficeOpenXml.ExcelAddress]$Range , - #One or more row(s), Column(s) and/or block(s) of cells to format + #One or more row(s), column(s) and/or block(s) of cells to format [Parameter(Mandatory = $true, ParameterSetName = "NamedRuleAddress")] [Parameter(Mandatory = $true, ParameterSetName = "DataBarAddress")] [Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSetAddress")] [Parameter(Mandatory = $true, ParameterSetName = "FourIconSetAddress")] [Parameter(Mandatory = $true, ParameterSetName = "FiveIconSetAddress")] $Address , - #One of the standard named rules - Top / Bottom / Less than / Greater than / Contains etc + #One of the standard named rules - Top / Bottom / Less than / Greater than / Contains etc. [Parameter(Mandatory = $true, ParameterSetName = "NamedRule", Position = 3)] [Parameter(Mandatory = $true, ParameterSetName = "NamedRuleAddress", Position = 3)] [OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType]$RuleType , @@ -65,7 +65,7 @@ [OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting5IconsSetType]$FiveIconsSet, #A value for the condition (e.g. "2000" if the test is 'lessthan 2000') [string]$ConditionValue, - #A second value for the conditions like between x and Y + #A second value for the conditions like "between x and Y" [string]$ConditionValue2, #Background colour for matching items [System.Drawing.Color]$BackgroundColor, diff --git a/ConvertExcelToImageFile.ps1 b/ConvertExcelToImageFile.ps1 index 6381759..c89f069 100644 --- a/ConvertExcelToImageFile.ps1 +++ b/ConvertExcelToImageFile.ps1 @@ -1,53 +1,53 @@ Function Convert-XlRangeToImage { -<# - .Synopsis - Gets the specified part of an Excel file and exports it as an image - .Description - Excel allows charts to be exported directly to a file, but can't do this with the rest of a sheet. To work round this this function - * Opens a copy of Excel and loads a file - * Selects a worksheet and then a range of cells in that worksheet - * Copies the select to the clipboard - * Saves the clipboard contents as an image file (it will save as .JPG unless the file name ends .BMP or .PNG) - * Copies a single cell to the clipboard (to prevent the "you have put a lot in the clipboard" message appearing) - * Closes Excel -#> -Param ( - #Path to the Excel file - [parameter(Mandatory=$true)] - $Path, - #Worksheet name - if none is specified "Sheet1" will be assumed - $workSheetname = "Sheet1" , - #Range of cells within the sheet, e.g "A1:Z99" - [parameter(Mandatory=$true)] - $range, - #A bmp, png or jpg file where the result will be saved - $destination = "$pwd\temp.png", - #If specified opens the image in the default viewer. - [switch]$show -) - $extension = $destination -replace '^.*\.(\w+)$' ,'$1' - if ($extension -in @('JPEG','BMP','PNG')) { - $Format = [system.Drawing.Imaging.ImageFormat]$extension - } #if we don't recognise the extension OR if it is JPG with an E, use JPEG format - else { $Format = [system.Drawing.Imaging.ImageFormat]::Jpeg} - Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Starting Excel" - $xlApp = New-Object -ComObject "Excel.Application" - Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Opening Workbook and copying data" - $xlWbk = $xlApp.Workbooks.Open($Path) - $xlWbk.Worksheets($workSheetname).Select() - $xlWbk.ActiveSheet.Range($range).Select() | Out-Null - $xlApp.Selection.Copy() | Out-Null - Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Saving copied data" - # Get-Clipboard came in with PS5. Older versions can use [System.Windows.Clipboard] but it is ugly. - $image = Get-Clipboard -Format Image - $image.Save($destination, $Format) - Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Closing Excel" - $xlWbk.ActiveSheet.Range("a1").Select() | Out-Null - $xlApp.Selection.Copy() | Out-Null - $xlApp.Quit() - Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Completed - if ($show) {Start-Process -FilePath $destination} - else {Get-Item -Path $destination} + <# + .Synopsis + Gets the specified part of an Excel file and exports it as an image + .Description + Excel allows charts to be exported directly to a file, but can't do this with the rest of a sheet. To work round this this function + * Opens a copy of Excel and loads a file + * Selects a worksheet and then a range of cells in that worksheet + * Copies the select to the clipboard + * Saves the clipboard contents as an image file (it will save as .JPG unless the file name ends .BMP or .PNG) + * Copies a single cell to the clipboard (to prevent the "you have put a lot in the clipboard" message appearing) + * Closes Excel + #> + Param ( + #Path to the Excel file + [parameter(Mandatory=$true)] + $Path, + #Worksheet name - if none is specified "Sheet1" will be assumed + $workSheetname = "Sheet1" , + #Range of cells within the sheet, e.g "A1:Z99" + [parameter(Mandatory=$true)] + $range, + #A bmp, png or jpg file where the result will be saved + $destination = "$pwd\temp.png", + #If specified opens the image in the default viewer. + [switch]$show + ) + $extension = $destination -replace '^.*\.(\w+)$' ,'$1' + if ($extension -in @('JPEG','BMP','PNG')) { + $Format = [system.Drawing.Imaging.ImageFormat]$extension + } #if we don't recognise the extension OR if it is JPG with an E, use JPEG format + else { $Format = [system.Drawing.Imaging.ImageFormat]::Jpeg} + Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Starting Excel" + $xlApp = New-Object -ComObject "Excel.Application" + Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Opening Workbook and copying data" + $xlWbk = $xlApp.Workbooks.Open($Path) + $xlWbk.Worksheets($workSheetname).Select() + $xlWbk.ActiveSheet.Range($range).Select() | Out-Null + $xlApp.Selection.Copy() | Out-Null + Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Saving copied data" + # Get-Clipboard came in with PS5. Older versions can use [System.Windows.Clipboard] but it is ugly. + $image = Get-Clipboard -Format Image + $image.Save($destination, $Format) + Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Closing Excel" + $xlWbk.ActiveSheet.Range("a1").Select() | Out-Null + $xlApp.Selection.Copy() | Out-Null + $xlApp.Quit() + Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Completed + if ($show) {Start-Process -FilePath $destination} + else {Get-Item -Path $destination} } <# del demo*.xlsx diff --git a/ConvertFromExcelData.ps1 b/ConvertFromExcelData.ps1 index bd75895..3421bc1 100644 --- a/ConvertFromExcelData.ps1 +++ b/ConvertFromExcelData.ps1 @@ -1,25 +1,25 @@ function ConvertFrom-ExcelData { <# - .SYNOPSIS - Reads data from a sheet, and for each row, calls a custom scriptblock with a list of property names and the row of data. + .SYNOPSIS + Reads data from a sheet, and for each row, calls a custom scriptblock with a list of property names and the row of data. - - .EXAMPLE - ConvertFrom-ExcelData .\testSQLGen.xlsx { - param($propertyNames, $record) + + .EXAMPLE + ConvertFrom-ExcelData .\testSQLGen.xlsx { + param($propertyNames, $record) - $reportRecord = @() - foreach ($pn in $propertyNames) { - $reportRecord += "{0}: {1}" -f $pn, $record.$pn + $reportRecord = @() + foreach ($pn in $propertyNames) { + $reportRecord += "{0}: {1}" -f $pn, $record.$pn + } + $reportRecord +="" + $reportRecord -join "`r`n" } - $reportRecord +="" - $reportRecord -join "`r`n" -} -First: John -Last: Doe -The Zip: 12345 -.... + First: John + Last: Doe + The Zip: 12345 + .... #> param( [Alias("FullName")] diff --git a/ConvertFromExcelToSQLInsert.ps1 b/ConvertFromExcelToSQLInsert.ps1 index 0f4708a..7a3f00c 100644 --- a/ConvertFromExcelToSQLInsert.ps1 +++ b/ConvertFromExcelToSQLInsert.ps1 @@ -1,51 +1,34 @@ function ConvertFrom-ExcelToSQLInsert { <# - .SYNOPSIS + .SYNOPSIS Generate SQL insert statements from Excel spreadsheet. - - .DESCRIPTION + .DESCRIPTION Generate SQL insert statements from Excel spreadsheet. - - .PARAMETER TableName + .PARAMETER TableName Name of the target database table. - - .PARAMETER Path + .PARAMETER Path Path to an existing .XLSX file - This parameter is passed to Import-Excel as is. - - .PARAMETER WorkSheetname + .PARAMETER WorkSheetname Specifies the name of the worksheet in the Excel workbook to import. By default, if no name is provided, the first worksheet will be imported. - This parameter is passed to Import-Excel as is. - - .PARAMETER StartRow + .PARAMETER StartRow The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row. - When the parameters ‘-NoHeader’ and ‘-HeaderName’ are not provided, this row will contain the column headers that will be used as property names. When one of both parameters are provided, the property names are automatically created and this row will be treated as a regular row containing data. - - .PARAMETER Header + .PARAMETER Header Specifies custom property names to use, instead of the values defined in the column headers of the TopRow. - - In case you provide less header names than there is data in the worksheet, then only the data with a corresponding header name will be imported and the data without header name will be disregarded. - - In case you provide more header names than there is data in the worksheet, then all data will be imported and all objects will have all the property names you defined in the header names. As such, the last properties will be blanc as there is no data for them. - - .PARAMETER NoHeader + If you provide fewr header names than there is data in the worksheet, then only the data with a corresponding header name will be imported and the data without header name will be disregarded. + If you provide more header names than there is data in the worksheet, then all data will be imported and all objects will have all the property names you defined in the header names. As such, the last properties will be blank as there is no data for them. + .PARAMETER NoHeader Automatically generate property names (P1, P2, P3, ..) instead of the ones defined in the column headers of the TopRow. - This switch is best used when you want to import the complete worksheet ‘as is’ and are not concerned with the property names. - - .PARAMETER DataOnly + .PARAMETER DataOnly Import only rows and columns that contain data, empty rows and empty columns are not imported. - - - .PARAMETER ConvertEmptyStringsToNull + .PARAMETER ConvertEmptyStringsToNull If specified, cells without any data are replaced with NULL, instead of an empty string. - This is to address behviors in certain DBMS where an empty string is insert as 0 for INT column, instead of a NULL value. - .EXAMPLE + .EXAMPLE Generate SQL insert statements from Movies.xlsx file, leaving blank cells as empty strings: ---------------------------------------------------------- @@ -65,7 +48,7 @@ function ConvertFrom-ExcelToSQLInsert { INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('Skyfall', '2012', '9'); INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Avengers', '2012', ''); - .EXAMPLE + .EXAMPLE Generate SQL insert statements from Movies.xlsx file, specify NULL instead of an empty string. ---------------------------------------------------------- @@ -85,7 +68,6 @@ function ConvertFrom-ExcelToSQLInsert { INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('Skyfall', '2012', '9'); INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Avengers', '2012', NULL); - .NOTES #> [CmdletBinding()] param( diff --git a/Export-charts.ps1 b/Export-charts.ps1 index 27ec7ab..e51d894 100644 --- a/Export-charts.ps1 +++ b/Export-charts.ps1 @@ -2,7 +2,7 @@ .Synopsis Exports the charts in an Excel spreadSheet .Example - Export-Charts .\test,xlsx + Export-Charts .\test.xlsx Exports the charts in test.xlsx to JPEG files in the current directory. .Example @@ -13,7 +13,7 @@ Param ( #Path to the Excel file whose chars we will export. $Path = "C:\Users\public\Documents\stats.xlsx", - #If specified, output file objects representing the image files. + #If specified, output file objects representing the image files [switch]$Passthru, #Format to write - JPG by default [ValidateSet("JPG","PNG","GIF")] diff --git a/Get-ExcelSheetInfo.ps1 b/Get-ExcelSheetInfo.ps1 index 6ab93f5..bb00ee3 100644 --- a/Get-ExcelSheetInfo.ps1 +++ b/Get-ExcelSheetInfo.ps1 @@ -1,24 +1,20 @@ Function Get-ExcelSheetInfo { <# - .SYNOPSIS + .SYNOPSIS Get worksheet names and their indices of an Excel workbook. - - .DESCRIPTION + .DESCRIPTION The Get-ExcelSheetInfo cmdlet gets worksheet names and their indices of an Excel workbook. - - .PARAMETER Path + .PARAMETER Path Specifies the path to the Excel file. This parameter is required. - - .EXAMPLE + .EXAMPLE Get-ExcelSheetInfo .\Test.xlsx - .NOTES + .NOTES CHANGELOG 2016/01/07 Added Created by Johan Akerstrom (https://github.com/CosmosKey) - .LINK + .LINK https://github.com/dfinke/ImportExcel - #> [CmdletBinding()] diff --git a/Get-ExcelWorkbookInfo.ps1 b/Get-ExcelWorkbookInfo.ps1 index acd2e92..564e139 100644 --- a/Get-ExcelWorkbookInfo.ps1 +++ b/Get-ExcelWorkbookInfo.ps1 @@ -1,15 +1,12 @@ Function Get-ExcelWorkbookInfo { <# - .SYNOPSIS + .SYNOPSIS Retrieve information of an Excel workbook. - - .DESCRIPTION + .DESCRIPTION The Get-ExcelWorkbookInfo cmdlet retrieves information (LastModifiedBy, LastPrinted, Created, Modified, ...) fron an Excel workbook. These are the same details that are visible in Windows Explorer when right clicking the Excel file, selecting Properties and check the Details tabpage. - - .PARAMETER Path + .PARAMETER Path Specifies the path to the Excel file. This parameter is required. - - .EXAMPLE + .EXAMPLE Get-ExcelWorkbookInfo .\Test.xlsx CorePropertiesXml : #document @@ -32,11 +29,11 @@ Modified : 10/02/2017 12:45:37 CustomPropertiesXml : #document - .NOTES + .NOTES CHANGELOG 2016/01/07 Added Created by Johan Akerstrom (https://github.com/CosmosKey) - .LINK + .LINK https://github.com/dfinke/ImportExcel #> diff --git a/Set-Column.ps1 b/Set-Column.ps1 index 5246a6a..02e3081 100644 --- a/Set-Column.ps1 +++ b/Set-Column.ps1 @@ -1,31 +1,32 @@ Function Set-Column { -<# - .SYNOPSIS + <# + .SYNOPSIS Adds a column to the existing data area in an Excel sheet, fills values and sets formatting - .DESCRIPTION + .DESCRIPTION Set-Column takes a value which is either string containing a value or formula or a scriptblock which evaluates to a string, and optionally a column number and fills that value down the column. A column name can be specified and the new column can be made a named range. The column can be formatted. - .Example + .Example C:> Set-Column -Worksheet $ws -Heading "WinsToFastLaps" -Value {"=E$row/C$row"} -Column 7 -AutoSize -AutoNameRange Here $WS already contains a worksheet which contains counts of races won and fastest laps recorded by racing drivers (in columns C and E) Set-Column specifies that Column 7 should have a heading of "WinsToFastLaps" and the data cells should contain =E2/C2 , =E3/C3 - the data celss should become a named range, which will also be "WinsToFastLaps" the column width will be set automatically - -#> -[cmdletbinding()] + the data cells should become a named range, which will also be "WinsToFastLaps" the column width will be set automatically + #> + [cmdletbinding()] Param ( [Parameter(ParameterSetName="Package",Mandatory=$true)] [OfficeOpenXml.ExcelPackage]$ExcelPackage, - #Sheet to update + #The sheet to update can be a given as a name or an Excel Worksheet object - this sets it by name [Parameter(ParameterSetName="Package")] + #The sheet to update can be a given as a name or an Excel Worksheet object - $workSheet contains the object $Worksheetname = "Sheet1", [Parameter(ParameterSetName="sheet",Mandatory=$true)] [OfficeOpenXml.ExcelWorksheet] $Worksheet, #Column to fill down - first column is 1. 0 will be interpreted as first unused column $Column = 0 , + #First row to fill data in [Int]$StartRow , #value, formula or script block for to fill in. Script block can use $row, $column [number], $ColumnName [letter(s)], $startRow, $startColumn, $endRow, $endColumn [parameter(Mandatory=$true)] @@ -76,8 +77,9 @@ [Switch]$AutoSize, #Set cells to a fixed width, ignored if Autosize is specified [float]$Width, - #Set the inserted data to be a named range (ignored if header is not specified) d + #Set the inserted data to be a named range (ignored if header is not specified) [Switch]$AutoNameRange, + #If Specified, return an ExcelPackage object to allow further work to be done on the file. [switch]$PassThru ) #if we were passed a package object and a worksheet name , get the worksheet. @@ -125,7 +127,7 @@ if ($HorizontalAlignment) { $Worksheet.Column( $Column).Style.HorizontalAlignment = $HorizontalAlignment} if ($VerticalAlignment) { $Worksheet.Column( $Column).Style.VerticalAlignment = $VerticalAlignment } if ($FontColor) { $Worksheet.Column( $Column).Style.Font.Color.SetColor( $FontColor ) } - if ($BorderAround) { $Worksheet.Column( $Column).Style.Border.BorderAround( $BorderAround ) } + if ($BorderAround) { $Worksheet.Column( $Column).Style.Border.BorderAround( $BorderAround ) } if ($BackgroundColor) { $Worksheet.Column( $Column).Style.Fill.PatternType = $BackgroundPattern $Worksheet.Column( $Column).Style.Fill.BackgroundColor.SetColor($BackgroundColor ) diff --git a/Set-Row.ps1 b/Set-Row.ps1 index 9250c1a..d378763 100644 --- a/Set-Row.ps1 +++ b/Set-Row.ps1 @@ -1,24 +1,22 @@ Function Set-Row { -<# -.Synopsis - Fills values into a row in a Excel spreadsheet -.Description - Set-Row accepts either a Worksheet object or an Excel package object returned by Export-Excel and the name of a sheet, - and inserts the chosen contents into a row of the sheet. - The contents can be a constant "42" , a formula or a script block which is converted into a constant or formula. - The first cell of the row can optional be given a heading. -.Example - Set-row -Worksheet $ws -Heading Total -Value {"=sum($columnName`2:$columnName$endrow)" } + <# + .Synopsis + Fills values into a row in a Excel spreadsheet + .Description + Set-Row accepts either a Worksheet object or an Excel package object returned by Export-Excel and the name of a sheet, + and inserts the chosen contents into a row of the sheet. + The contents can be a constant "42" , a formula or a script block which is converted into a constant or formula. + The first cell of the row can optional be given a heading. + .Example + Set-row -Worksheet $ws -Heading Total -Value {"=sum($columnName`2:$columnName$endrow)" } - $Ws contains a worksheet object, and no Row number is specified so Set-Row will select the next row after the end of the data in the sheet - The first cell will contain "Total", and each other cell will contain - =Sum(xx2:xx99) - where xx is the column name, and 99 is the last row of data. - Note the use of `2 to Prevent 2 becoming part of the variable "ColumnName" - The script block can use $row, $column, $ColumnName, $startRow/Column $endRow/Column - - -#> -[cmdletbinding()] + $Ws contains a worksheet object, and no Row number is specified so Set-Row will select the next row after the end of the data in the sheet + The first cell will contain "Total", and each other cell will contain + =Sum(xx2:xx99) - where xx is the column name, and 99 is the last row of data. + Note the use of `2 to Prevent 2 becoming part of the variable "ColumnName" + The script block can use $row, $column, $ColumnName, $startRow/Column $endRow/Column + #> + [cmdletbinding()] Param ( #An Excel package object - e.g. from Export-Excel -passthru - requires a sheet name [Parameter(ParameterSetName="Package",Mandatory=$true)] @@ -75,11 +73,12 @@ [OfficeOpenXml.Style.ExcelHorizontalAlignment]$HorizontalAlignment, #Position cell contents to top bottom or centre [OfficeOpenXml.Style.ExcelVerticalAlignment]$VerticalAlignment, - #Degrees to rotate text. Up to +90 for anti-clockwise ("upwards"), or to -90 for clockwise. + #Degrees to rotate text. Up to +90 for anti-clockwise ("upwards"), or to -90 for clockwise [ValidateRange(-90, 90)] [int]$TextRotation , #Set cells to a fixed hieght [float]$Height, + #If Specified, return an ExcelPackage object to allow further work to be done on the file [switch]$PassThru ) diff --git a/Update-FirstObjectProperties.ps1 b/Update-FirstObjectProperties.ps1 index 6716a59..f19935b 100644 --- a/Update-FirstObjectProperties.ps1 +++ b/Update-FirstObjectProperties.ps1 @@ -72,7 +72,8 @@ Function Update-FirstObjectProperties { .NOTES CHANGELOG - 2017/06/08 Function born #> + 2017/06/08 Function born + #> Try { $Union = @() diff --git a/formatting.ps1 b/formatting.ps1 index 5d6d26a..f54c27c 100644 --- a/formatting.ps1 +++ b/formatting.ps1 @@ -1,22 +1,22 @@ Function Add-ConditionalFormatting { -<# -.Synopsis - Adds contitional formatting to worksheet -.Example - $excel = $avdata | Export-Excel -Path (Join-path $FilePath "\Machines.XLSX" ) -WorksheetName "Server Anti-Virus" -AutoSize -FreezeTopRow -AutoFilter -PassThru + <# + .Synopsis + Adds contitional formatting to worksheet + .Example + $excel = $avdata | Export-Excel -Path (Join-path $FilePath "\Machines.XLSX" ) -WorksheetName "Server Anti-Virus" -AutoSize -FreezeTopRow -AutoFilter -PassThru - Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "b":b1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "2003" - Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "i2:i1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "Disabled" - $excel.Workbook.Worksheets[1].Cells["D1:G1048576"].Style.Numberformat.Format = [cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern - $excel.Workbook.Worksheets[1].Row(1).style.font.bold = $true - $excel.Save() ; $excel.Dispose() + Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "b":b1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "2003" + Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "i2:i1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "Disabled" + $excel.Workbook.Worksheets[1].Cells["D1:G1048576"].Style.Numberformat.Format = [cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern + $excel.Workbook.Worksheets[1].Row(1).style.font.bold = $true + $excel.Save() ; $excel.Dispose() - Here Export-Excel is called with the -passThru parameter so the Excel Package object is stored in $Excel - The desired worksheet is selected and the then columns B and i are conditially formatted (excluding the top row) to show - Fixed formats are then applied to dates in columns D..G and the top row is formatted - Finally the workbook is saved and the Excel closed. + Here Export-Excel is called with the -passThru parameter so the Excel Package object is stored in $Excel + The desired worksheet is selected and the then columns B and i are conditially formatted (excluding the top row) to show + Fixed formats are then applied to dates in columns D..G and the top row is formatted + Finally the workbook is saved and the Excel closed. -#> + #> Param ( #The worksheet where the format is to be applied [OfficeOpenXml.ExcelWorksheet]$WorkSheet , @@ -89,17 +89,17 @@ } Function Set-Format { -<# -.SYNOPSIS - Applies Number, font, alignment and colour formatting to a range of Excel Cells -.EXAMPLE - $sheet.Column(3) | Set-Format -HorizontalAlignment Right -NumberFormat "#,###" - Selects column 3 from a sheet object (within a workbook object, which is a child of the ExcelPackage object) and passes it to Set-Format which formats as an integer with comma seperated groups -.EXAMPLE - Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NumberFormat "#,###" - Instead of piping the address in this version specifies a block of cells and applies similar formatting + <# + .SYNOPSIS + Applies Number, font, alignment and colour formatting to a range of Excel Cells + .EXAMPLE + $sheet.Column(3) | Set-Format -HorizontalAlignment Right -NumberFormat "#,###" + Selects column 3 from a sheet object (within a workbook object, which is a child of the ExcelPackage object) and passes it to Set-Format which formats as an integer with comma seperated groups + .EXAMPLE + Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NumberFormat "#,###" + Instead of piping the address in this version specifies a block of cells and applies similar formatting -#> + #> Param ( #One or more row(s), Column(s) and/or block(s) of cells to format [Parameter(ValueFromPipeline=$true)] From de72cfe8cd5a4ca8acb7e7068ce7723b0084dd06 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Tue, 10 Jul 2018 13:44:29 +0100 Subject: [PATCH 07/21] Removed GetExcelColumnName --- Get-ExcelColumnName.ps1 | 22 ---------------------- ImportExcel.psm1 | 1 - 2 files changed, 23 deletions(-) delete mode 100644 Get-ExcelColumnName.ps1 diff --git a/Get-ExcelColumnName.ps1 b/Get-ExcelColumnName.ps1 deleted file mode 100644 index 7562391..0000000 --- a/Get-ExcelColumnName.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -function Get-ExcelColumnName { - param( - [Parameter(ValueFromPipeline=$true)] - $columnNumber=1 - ) - - Process { - $dividend = $columnNumber - $columnName = @() - while($dividend -gt 0) { - $modulo = ($dividend - 1) % 26 - $columnName += [char](65 + $modulo) - $dividend = [int](($dividend -$modulo)/26) - } - - [PSCustomObject] @{ - ColumnNumber = $columnNumber - ColumnName = $columnName -join '' - } - - } -} \ No newline at end of file diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index 352056c..a81259e 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -11,7 +11,6 @@ . $PSScriptRoot\Copy-ExcelWorkSheet.ps1 . $PSScriptRoot\Export-Excel.ps1 . $PSScriptRoot\Export-ExcelSheet.ps1 - . $PSScriptRoot\Get-ExcelColumnName.ps1 . $PSScriptRoot\Get-ExcelSheetInfo.ps1 . $PSScriptRoot\Get-ExcelWorkbookInfo.ps1 . $PSScriptRoot\Get-HtmlTable.ps1 From 90942e40845b8cc2d3040dc31f5b26ff266ca868 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 12 Jul 2018 10:49:29 +0100 Subject: [PATCH 08/21] Improved tests for conversions, added quick chart support, and extended Send-SQLData --- Export-Excel.ps1 | 121 +++++++++++--- New-ExcelChart.ps1 | 51 +++++- README.md | 17 +- Send-SqlDataToExcel.ps1 | 263 +++++++++++++++++++++++-------- ToDo.md | 9 +- __tests__/Export-Excel.Tests.ps1 | 118 ++++++++------ 6 files changed, 430 insertions(+), 149 deletions(-) diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 94964e2..409ced8 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -16,14 +16,14 @@ If specified data will be added to the end of an existing sheet, using the same column headings. .PARAMETER TargetData Data to insert onto the worksheet - this is often provided from the pipeline. + .PARAMETER DisplayPropertySet + Many (but not all) objects have a hidden property named psStandardmembers with a child property DefaultDisplayPropertySet ; this parameter reduces the properties exported to those in this set. + .PARAMETER NoAliasOrScriptPropeties + Some objects duplicate existing properties by adding aliases, or have Script properties which take a long time to return a value and slow the export down, if specified this removes these properties .PARAMETER ExcludeProperty Specifies properties which may exist in the target data but should not be placed on the worksheet. - .PARAMETER NoAliasOrScriptPropeties - Some objects duplicate properties with aliases, or have Script properties which take a long time to return a value and slow the export down, if specified this removes these properties - .PARAMETER DisplayPropertySet, - Many (but not all) objects have a hidden property named psStandardmembers with a child property DefaultDisplayPropertySet ; this parameter reduces the properties exported to those in this set. .PARAMETER Title - Text of a title to be placed in Cell A1. + Text of a title to be placed in the top left cell. .PARAMETER TitleBold Sets the title in boldface type. .PARAMETER TitleSize @@ -36,22 +36,33 @@ Sets password protection on the workbook. .PARAMETER IncludePivotTable Adds a Pivot table using the data in the worksheet. + .PARAMETER PivotTableName + If a Pivot table is created from command line parameters, specificies the name of the new sheet holding the pivot. If Omitted this will be "WorksheetName-PivotTable" .PARAMETER PivotRows - Name(s) columns from the spreadhseet which will provide the row name(s) in the pivot table. + Name(s) columns from the spreadhseet which will provide the Row name(s) in a pivot table created from command line parameters. .PARAMETER PivotColumns - Name(s) columns from the spreadhseet which will provide the Column name(s) in the pivot table. + Name(s) columns from the spreadhseet which will provide the Column name(s) in a pivot table created from command line parameters. + .PARAMETER PivotFilter + Name(s) columns from the spreadhseet which will provide the Filter name(s) in a pivot table created from command line parameters. .PARAMETER PivotData - Hash table in the form ColumnName = Average|Count|CountNums|Max|Min|Product|None|StdDev|StdDevP|Sum|Var|VarP to provide the data in the Pivot table. - .PARAMETER PivotTableDefinition, - HashTable(s) with Sheet PivotTows, PivotColumns, PivotData, IncludePivotChart and ChartType values to make it easier to specify a definition or multiple Pivots. - .PARAMETER IncludePivotChart, - Include a chart with the Pivot table - implies Include Pivot Table. + In a pivot table created from command line parameters, the fields to use in the table body is given as a Hash table in the form ColumnName = Average|Count|CountNums|Max|Min|Product|None|StdDev|StdDevP|Sum|Var|VarP . + .PARAMETER NoTotalsInPivot + In a pivot table created from command line parameters, prevents the addition of totals to rows and columns. + .PARAMETER PivotTableDefinition + Instead of describing a single pivot table with mutliple commandline paramters; you can use a HashTable in the form PivotTableName = Definition; + Definition is itself a hashtable with Sheet PivotTows, PivotColumns, PivotData, IncludePivotChart and ChartType values. + .PARAMETER IncludePivotChart + Include a chart with the Pivot table - implies -IncludePivotTable. + .PARAMETER ChartType + The type for Pivot chart (one of Excel's defined chart types) .PARAMETER NoLegend Exclude the legend from the pivot chart. .PARAMETER ShowCategory Add category labels to the pivot chart. .PARAMETER ShowPercent Add Percentage labels to the pivot chart. + .PARAMETER ConditionalFormat + One or more conditional formatting rules defined with New-ConditionalFormattingIconSet. .PARAMETER ConditionalText Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered. .PARAMETER NoNumberConversion @@ -66,6 +77,14 @@ Makes the data in the worksheet a table with a name applies a style to it. Name must not contain spaces. .PARAMETER TableStyle Selects the style for the named table - defaults to 'Medium6'. + .PARAMETER BarChart + Creates a "quick" bar chart using the first text column as labels and the first numeric column as values + .PARAMETER ColumnChart + Creates a "quick" column chart using the first text column as labels and the first numeric column as values + .PARAMETER LineChart + Creates a "quick" line chart using the first text column as labels and the first numeric column as values + .PARAMETER PieChart + Creates a "quick" pie chart using the first text column as labels and the first numeric column as values .PARAMETER ExcelChartDefinition A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. .PARAMETER HideSheet @@ -99,7 +118,7 @@ .PARAMETER FreezePane Freezes panes at specified coordinates (in the form RowNumber , ColumnNumber). .PARAMETER AutoFilter - Enables the 'Filter' in Excel on the complete header row. So users can easily sort, filter and/or search the data in the select column from within Excel. + Enables the 'Filter' in Excel on the complete header row. So users can easily sort, filter and/or search the data in the selected column from within Excel. .PARAMETER AutoSize Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell. .PARAMETER Now @@ -132,8 +151,12 @@ # Blue color for positive numbers and a red color for negative numbers. All numbers will be proceeded by a dollar sign '$'. '[Blue]$#,##0.00;[Red]-$#,##0.00' + .PARAMETER ReZip + If specified, Export-Excel will expand the contents of the .XLSX file (which is multiple files in a zip archive) and rebuilt it. .PARAMETER Show Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. + .PARAMETER ReturnRange + If specified, Export-Excel returns the range of added cells in the format "A1:Z100" .PARAMETER PassThru If specified, Export-Excel returns an object representing the Excel package without saving the package first. To save it you need to call the save or Saveas method or send it back to Export-Excel. @@ -396,6 +419,10 @@ [Parameter(ParameterSetName = 'Table')] [Parameter(ParameterSetName = 'PackageTable')] [OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6', + [Switch]$Barchart, + [Switch]$PieChart, + [Switch]$LineChart , + [Switch]$ColumnChart , [Object[]]$ExcelChartDefinition, [String[]]$HideSheet, [Switch]$MoveToStart, @@ -438,8 +465,8 @@ #> Param ( - [Object]$TargetCell, - [Object]$CellValue + $TargetCell, + $CellValue ) #The write-verbose commands have been commented out below - even if verbose is silenced they cause a significiant performance impact and if it's on they will cause a flood of messages. Switch ($CellValue) { @@ -824,6 +851,30 @@ Add-ExcelChart -Worksheet $ws @params } + if ($Barchart -or $PieChart -or $LineChart -or $ColumnChart) { + if ($NoHeader) {$FirstDataRow = $startRow} + else {$FirstDataRow = $startRow + 1 } + $range = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$FirstDataRow]C[$startColumn]:R[$FirstDataRow]C[$lastCol]",0,0) + $xCol = $ws.cells[$range] | Where-Object {$_.value -is [string] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 + $yCol = $ws.cells[$range] | Where-Object {$_.value -is [valueType] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 + $params = @{ + xrange = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$FirstDataRow]C[$xcol]:R[$($lastrow)]C[$xcol]",0,0) ; + yrange = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$FirstDataRow]C[$ycol]:R[$($lastrow)]C[$ycol]",0,0) ; + title = ""; + Column = ($lastCol +1) ; + Width = 1200 + } + if ($NoHeader) {$params["NoHeader"] = $true} + else {$Params["SeriesHeader"] = $ws.Cells[$startRow, $YCol].Value} + if ($ColumnChart) {$Params["chartType"] = "ColumnStacked" } + elseif ($Barchart) {$Params["chartType"] = "BarStacked" } + elseif ($PieChart) {$Params["chartType"] = "PieExploded3D" } + elseif ($LineChart) {$Params["chartType"] = "Line" } + + Add-ExcelChart -Worksheet $ws @params + + } + foreach ($ct in $ConditionalText) { try { $cfParams = @{RuleType = $ct.ConditionalType; ConditionValue = $ct.text ; @@ -872,7 +923,7 @@ } try { $TempZipPath = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName()) - [io.compression.zipfile]::ExtractToDirectory($pkg.File, $TempZipPath) | Out-Null + [io.compression.zipfile]::ExtractToDirectory($pkg.File, $TempZipPath) | Out-Null Remove-Item $pkg.File -Force [io.compression.zipfile]::CreateFromDirectory($TempZipPath, $pkg.File) | Out-Null } @@ -890,7 +941,7 @@ function New-PivotTableDefinition { <# .Synopsis - Creates Pivot table definitons for export excel + Creates Pivot table definitons for Export-Excel .Description Export-Excel allows a single Pivot table to be defined using the parameters -IncludePivotTable, -PivotColumns -PivotRows, =PivotData, -PivotFilter, -NoTotalsInPivot, -PivotDataToColumn, -IncludePivotChart and -ChartType. @@ -961,6 +1012,13 @@ function New-PivotTableDefinition { @{$PivotTableName = $parameters} } function Add-WorkSheet { + <# + .Synopsis + Adds a workshet to an existing workbook. + .Description + If the named worksheet already exists, the -clearsheet parameter decides whether it should be deleted and a new one returned, + or if not specified the existing sheet will be returned. + #> [cmdletBinding()] [OutputType([OfficeOpenXml.ExcelWorksheet])] param( @@ -1038,15 +1096,21 @@ function Add-WorkSheet { return $ws } function Add-PivotTable { - +<# + .Synopsis + Adds a Pivot table (and optional pivot chart) to a workbook + .Description + If the pivot table already exists, the source data will be updated. +#> param ( - # Parameter help description + #Name for the new Pivot table - this will be the name of a sheet in the workbook [Parameter(Mandatory = $true)] $PivotTableName, + #An excel package object for the workbook. $ExcelPackage, #Worksheet where the data is found $SourceWorkSheet, - #Address range in the worksheet e.g "A10:F20" - the first row must be column names: if not specified the whole sheet will be used/ + #Address range in the worksheet e.g "A10:F20" - the first row must be column names: if not specified the whole sheet will be used. $SourceRange, #Fields to set as rows in the Pivot table $PivotRows, @@ -1162,11 +1226,17 @@ function Add-PivotTable { } } function Add-ExcelChart { + <# + .Synopsis + Creates a chart in an Existing excel worksheet + #> [cmdletbinding()] param( + #An object representing the worksheet where the chart should be added. [OfficeOpenXml.ExcelWorksheet]$Worksheet, [String]$Title = "Chart Title", #$Header, Not used but referenced previously + #The Type of chart (Area, Line, Pie etc) [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = "ColumnStacked", $XRange, $YRange, @@ -1206,12 +1276,13 @@ function Add-ExcelChart { try { $ChartName = 'Chart' + (Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.', '' $chart = $Worksheet.Drawings.AddChart($ChartName, $ChartType) - $chart.Title.Text = $Title - if ($TitleBold) {$chart.Title.Font.Bold = $true} - if ($TitleSize) {$chart.Title.Font.Size = $TitleSize} - + if ($Title) { + $chart.Title.Text = $Title + if ($TitleBold) {$chart.Title.Font.Bold = $true} + 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 = $true} diff --git a/New-ExcelChart.ps1 b/New-ExcelChart.ps1 index 67e91b7..868b43e 100644 --- a/New-ExcelChart.ps1 +++ b/New-ExcelChart.ps1 @@ -13,12 +13,35 @@ function New-ExcelChartDefinition { $RowOffSetPixels = 10, $Column = 6, $ColumnOffSetPixels = 5, + $LegendSize, + [Switch]$legendBold, [Switch]$NoLegend, [Switch]$ShowCategory, [Switch]$ShowPercent, - $SeriesHeader + $SeriesHeader, + [Switch]$TitleBold, + [Int]$TitleSize , + [String]$XAxisTitleText, + [Switch]$XAxisTitleBold, + $XAxisTitleSize , + [string]$XAxisNumberformat, + $XMajorUnit, + $XMinorUnit, + $XMaxValue, + $XMinValue, + [OfficeOpenXml.Drawing.Chart.eAxisPosition]$XAxisPosition , + [String]$YAxisTitleText, + [Switch]$YAxisTitleBold, + $YAxisTitleSize, + [string]$YAxisNumberformat, + $YMajorUnit, + $YMinorUnit, + $YMaxValue, + $YMinValue, + [OfficeOpenXml.Drawing.Chart.eAxisPosition]$YAxisPosition ) if ( $Header ) {Write-Warning "The header parameter is ignored."} #Nothing was done with it when creating a chart. + #might be able to do [PSCustomObject]$PsboundParameters, the defaults here match those in Add-Excel Chart [PSCustomObject]@{ Title = $Title ChartType = $ChartType @@ -30,9 +53,29 @@ function New-ExcelChartDefinition { RowOffSetPixels = $RowOffSetPixels Column = $Column ColumnOffSetPixels = $ColumnOffSetPixels - NoLegend = $NoLegend -as [Boolean] - ShowCategory = $ShowCategory-as [Boolean] - ShowPercent = $ShowPercent -as [Boolean] + NoLegend = $NoLegend -as [Boolean] + ShowCategory = $ShowCategory -as [Boolean] + ShowPercent = $ShowPercent -as [Boolean] + TitleBold = $TitleBold -as [Boolean] + TitleSize = $TitleSize SeriesHeader = $SeriesHeader + XAxisTitleText = $XAxisTitleText + XAxisTitleBold = $XAxisTitleBold -as [Boolean] + XAxisTitleSize = $XAxisTitleSize + XAxisNumberformat = $XAxisNumberformat + XMajorUnit = $XMajorUnit + XMinorUnit = $XMinorUnit + XMaxValue = $XMaxValue + XMinValue = $XMinValue + XAxisPosition = $XAxisPosition + YAxisTitleText = $YAxisTitleText + YAxisTitleBold = $YAxisTitleBold -as [Boolean] + YAxisTitleSize = $YAxisTitleSize + YAxisNumberformat = $YAxisNumberformat + YMajorUnit = $YMajorUnit + YMinorUnit = $YMinorUnit + YMaxValue = $YMaxValue + YMinValue = $YMinValue + YAxisPosition = $YAxisPosition } } \ No newline at end of file diff --git a/README.md b/README.md index cda80e7..53902c7 100644 --- a/README.md +++ b/README.md @@ -31,18 +31,21 @@ 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 7th July 18 +# What's new to 11th 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 that -Header does nothing, so it isn't Add-Excel chart and there is a message that does nothing in New-ExcelChartDefinition . -- Added paramters for managing Axes and legend (these are currently in Add-ExcelChart but not new-ExcelChartDefinition) -- 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) +- Added -BarChart -ColumnChart -LineChart -PieChart parameters to Export-Excel for quick charts without giving a full chart definition. +- Added parameters for managing chart Axes and legend +- Added some chart tests to Export-Excel.tests.ps1. (but tests & examples for quick charts , axes or legends still on the to do list ) - 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. +- 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) - 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 -- Fixed an issue with parameter sets in Export-Excel not being determined correctly in some case (I think this has been resolved before and might have regressed) -- Reverted the [double]::tryParse in export excel to the previous way, as the shorter way was not behaving correctly with with the number formats in certain regions. (also #374) +- Fixed an issue with parameter sets in Export-Excel not being determined correctly in some cases (I think this had been resolved before and might have regressed) +- Reverted the [double]::tryParse in export excel to the previous (longer) way, as the shorter way 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) This means that if there are multiple inserts only inserted data is touched, rather than going as far down and/or right as the furthest used cell. Added a test for this. -- Added more of the Parameters from Export-Excel to Join-worksheet, join just calls export with these parameters so there is no code behind them (#383) +- Added more of the Parameters from Export-Excel to Join-worksheet, join just calls export-excel with these parameters so there is no code behind them (#383) +- Added more of the Parameters from Export-Excel to Send-SQLDataToExcel, send just calls export-excel with these parameters... +- Added support for passing a System.Data.DataTable directly to Send-SQLDataToExcel # New in June 18 - New commands - Diff , Merge and Join diff --git a/Send-SqlDataToExcel.ps1 b/Send-SqlDataToExcel.ps1 index 96d0db7..44e8437 100644 --- a/Send-SqlDataToExcel.ps1 +++ b/Send-SqlDataToExcel.ps1 @@ -1,55 +1,174 @@ Function Send-SQLDataToExcel { - [CmdLetBinding()] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")] - <# - .Synopsis - Runs a SQL query and inserts the results into an ExcelSheet, more efficiently than sending it via Export-Excel - .Description - This command takes either an object representing a session with a SQL server or ODBC database, or a connection String to make one. - It the runs a SQL command, and inserts the rows of data returned into a worksheet. - It takes most of the parameters of Export-Excel, but it is more efficient than getting dataRows and piping them into Export-Excel, - data-rows have additional properties which need to be stripped off. - .Example - C:\> Send-SQLDataToExcel -MsSQLserver -Connection localhost -SQL "select name,type,type_desc from [master].[sys].[all_objects]" -Path .\temp.xlsx -WorkSheetname master -AutoSize -FreezeTopRow -AutoFilter -BoldTopRow - Connects to the local SQL server and selects 3 columns from [Sys].[all_objects] and exports then to a sheet named master with some basic header manager - .Example + <# + .SYNOPSIS + Inserts a DataTable - returned by SQL query into an ExcelSheet, more efficiently than sending it via Export-Excel + .DESCRIPTION + This command can accept a data table object or take a SQL command and run it against a database connection. + If running the SQL command, it accepts an object representing a session with a SQL server or ODBC database, or a connection String to make a session. + It the DataTable is inserted into the Excel sheet + It takes most of the parameters of Export-Excel, but it is more efficient than getting dataRows and piping them into Export-Excel, + data-rows have additional properties which need to be stripped off. + .PARAMETER DataTable + A System.Data.DataTable object containing the data to be inserted into the spreadsheet without running a query. + .PARAMETER Session + An active ODBC Connection or SQL connection object representing a session with a database which will be queried to get the data . + .PARAMETER Connection + Database connection string; either DSN=ODBC_Data_Source_Name, a full odbc or SQL Connection string, or the name of a SQL server. This is used to create a database session. + .PARAMETER MSSQLServer + Specifies the connection string is for SQL server, not ODBC . + .PARAMETER SQL + The SQL query to run against the session which was passed in -Session or set up from $Connection. + .PARAMETER Database + Switches to a specific database on a SQL server. + .PARAMETER QueryTimeout + Override the default query time of 30 seconds. + .PARAMETER Path + Path to a new or existing .XLSX file. + .PARAMETER WorkSheetName + The name of a sheet within the workbook - "Sheet1" by default . + .PARAMETER KillExcel + Closes Excel - prevents errors writing to the file because Excel has it open + .PARAMETER Title + Text of a title to be placed in the top left cell. + .PARAMETER TitleBold + Sets the title in boldface type. + .PARAMETER TitleSize + Sets the point size for the title. + .PARAMETER TitleBackgroundColor + Sets the cell background color for the title cell. + .PARAMETER TitleFillPattern + Sets the fill pattern for the title cell. + .PARAMETER Password + Sets password protection on the workbook. + .PARAMETER IncludePivotTable + Adds a Pivot table using the data in the worksheet. + .PARAMETER PivotTableName + If a Pivot table is created from command line parameters, specificies the name of the new sheet holding the pivot. If Omitted this will be "WorksheetName-PivotTable" + .PARAMETER PivotRows + Name(s) columns from the spreadhseet which will provide the Row name(s) in a pivot table created from command line parameters. + .PARAMETER PivotColumns + Name(s) columns from the spreadhseet which will provide the Column name(s) in a pivot table created from command line parameters. + .PARAMETER PivotFilter + Name(s) columns from the spreadhseet which will provide the Filter name(s) in a pivot table created from command line parameters. + .PARAMETER PivotData + In a pivot table created from command line parameters, the fields to use in the table body is given as a Hash table in the form ColumnName = Average|Count|CountNums|Max|Min|Product|None|StdDev|StdDevP|Sum|Var|VarP . + .PARAMETER NoTotalsInPivot + In a pivot table created from command line parameters, prevents the addition of totals to rows and columns. + .PARAMETER IncludePivotChart + Include a chart with the Pivot table - implies -IncludePivotTable. + .PARAMETER ChartType + The type for Pivot chart (one of Excel's defined chart types) + .PARAMETER NoLegend + Exclude the legend from the pivot chart. + .PARAMETER ShowCategory + Add category labels to the pivot chart. + .PARAMETER ShowPercent + Add Percentage labels to the pivot chart. + .PARAMETER PivotTableDefinition + Instead of describing a single pivot table with mutliple commandline paramters; you can use a HashTable in the form PivotTableName = Definition; + Definition is itself a hashtable with Sheet PivotTows, PivotColumns, PivotData, IncludePivotChart and ChartType values. + .PARAMETER ConditionalFormat + One or more conditional formatting rules defined with New-ConditionalFormattingIconSet. + .PARAMETER ConditionalText + Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered. + .PARAMETER BoldTopRow + Makes the top Row boldface. + .PARAMETER NoHeader + Does not put field names at the top of columns. + .PARAMETER RangeName + Makes the data in the worksheet a named range. + .PARAMETER AutoNameRange + Makes each column a named range. + .PARAMETER TableName + Makes the data in the worksheet a table with a name applies a style to it. Name must not contain spaces. + .PARAMETER TableStyle + Selects the style for the named table - defaults to 'Medium6'. + .PARAMETER BarChart + Creates a "quick" bar chart using the first text column as labels and the first numeric column as values + .PARAMETER ColumnChart + Creates a "quick" column chart using the first text column as labels and the first numeric column as values + .PARAMETER LineChart + Creates a "quick" line chart using the first text column as labels and the first numeric column as values + .PARAMETER PieChart + Creates a "quick" pie chart using the first text column as labels and the first numeric column as values + .PARAMETER ExcelChartDefinition + A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. + .PARAMETER StartRow + Row to start adding data. 1 by default. Row 1 will contain the title if any. Then headers will appear (Unless -No header is specified) then the data appears. + .PARAMETER StartColumn + Column to start adding data - 1 by default. + .PARAMETER FreezeTopRow + Freezes headers etc. in the top row. + .PARAMETER FreezeFirstColumn + Freezes titles etc. in the left column. + .PARAMETER FreezeTopRowFirstColumn + Freezes top row and left column (equivalent to Freeze pane 2,2 ). + .PARAMETER FreezePane + Freezes panes at specified coordinates (in the form RowNumber , ColumnNumber). + .PARAMETER AutoFilter + Enables the 'Filter' in Excel on the complete header row. So users can easily sort, filter and/or search the data in the select column from within Excel. + .PARAMETER AutoSize + Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell. + .PARAMETER Show + Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. + .PARAMETER ReturnRange + If specified, Export-Excel returns the range of added cells in the format "A1:Z100" + .PARAMETER PassThru + If specified, Export-Excel returns an object representing the Excel package without saving the package first. To save it you need to call the save or Saveas method or send it back to Export-Excel. + + .EXAMPLE + C:\> Send-SQLDataToExcel -MsSQLserver -Connection localhost -SQL "select name,type,type_desc from [master].[sys].[all_objects]" -Path .\temp.xlsx -WorkSheetname master -AutoSize -FreezeTopRow -AutoFilter -BoldTopRow + Connects to the local SQL server and selects 3 columns from [Sys].[all_objects] and exports then to a sheet named master with some basic header manager + .EXAMPLE C:\> $SQL="SELECT top 25 DriverName, Count(RaceDate) as Races, Count(Win) as Wins, Count(Pole) as Poles, Count(FastestLap) as Fastlaps FROM Results GROUP BY DriverName ORDER BY (count(win)) DESC" C:\> $Connection = 'Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DriverId=790;ReadOnly=0;Dbq=C:\users\James\Documents\f1Results.xlsx;' - C:\> Send-SQLDataToExcel -Connection $connection -SQL $sql -path .\demo4.xlsx -WorkSheetname "Winners" -AutoSize -AutoNameRange + C:\> Send-SQLDataToExcel -Connection $connection -SQL $sql -path .\demo1.xlsx -WorkSheetname "Winners" -AutoSize -AutoNameRange - This declares a SQL statement and creates an ODBC connection string to read from an Excel file, it then runs the statement and outputs the resulting data to a new spreadsheet. - .Example - C:\> Send-SQLDataToExcel -path .\demo4.xlsx -WorkSheetname "LR" -Connection "DSN=LR" -sql "SELECT name AS CollectionName FROM AgLibraryCollection Collection ORDER BY CollectionName" + This declares a SQL statement and creates an ODBC connection string to read from an Excel file, it then runs the statement and outputs the resulting data to a new spreadsheet. + (the F1 results database is available from https://1drv.ms/x/s!AhfYu7-CJv4ehNdZWxJE9LMAX_N5sg ) + .EXAMPLE + C:\> $SQL = "SELECT top 25 DriverName, Count(RaceDate) as Races, Count(Win) as Wins, Count(Pole) as Poles, Count(FastestLap) as Fastlaps FROM Results GROUP BY DriverName ORDER BY (count(win)) DESC" + C:\> Get-SQL -Session F1 -excel -Connection "C:\Users\mcp\OneDrive\public\f1\f1Results.xlsx" -sql $sql -OutputVariable Table | out-null + C:\> Send-SQLDataToExcel -DataTable $Table -Path ".\demo3.xlsx" -WorkSheetname Gpwinners -autosize -TableName winners -TableStyle Light6 -show + + This uses Get-SQL (at least V1.1 download from the gallery with Install-Module -Name GetSQL - note the function is get-SQL the module is GetSQL without the "-" ) + to simplify making database connections and building /submitting SQL statements. + Here it uses the same SQL statement as before; -OutputVariable leaves a System.Data.DataTable object in $table + and Send-SQLDataToExcel puts $table into the worksheet and sets it as an Excel table. + (the F1 results database is available from https://1drv.ms/x/s!AhfYu7-CJv4ehNdZWxJE9LMAX_N5sg ) + .EXAMPLE + C:\> $SQL = "SELECT top 25 DriverName, Count(Win) as Wins FROM Results GROUP BY DriverName ORDER BY (count(win)) DESC" + C:\> Send-SQLDataToExcel -Session $DbSessions["f1"] -SQL $sql -Path ".\demo3.xlsx" -WorkSheetname Gpwinners -autosize -ColumnChart + + Like the previous example, this uses Get-SQL (download from the gallery with Install-Module -Name GetSQL).It uses the connection which Get-SQL made rather than an ODFBC connection string + Here the data is presented as a quick chart. + .EXAMPLE + C:\> Send-SQLDataToExcel -path .\demo3.xlsx -WorkSheetname "LR" -Connection "DSN=LR" -sql "SELECT name AS CollectionName FROM AgLibraryCollection Collection ORDER BY CollectionName" This example uses an Existing ODBC datasource name "LR" which maps to an adobe lightroom database and gets a list of collection names into a worksheet - - - - #> - param ( - #Database connection string; either DSN=ODBC_Data_Source_Name, a full odbc or SQL Connection string, or the name of a SQL server + #> + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")] + param ( [Parameter(ParameterSetName="SQLConnection", Mandatory=$true)] [Parameter(ParameterSetName="ODBCConnection",Mandatory=$true)] $Connection, - #A pre-existing database session object [Parameter(ParameterSetName="ExistingSession",Mandatory=$true)] [System.Data.Common.DbConnection]$Session, - #Specifies the connection string is for SQL server not ODBC [Parameter(ParameterSetName="SQLConnection",Mandatory=$true)] [switch]$MsSQLserver, - #Switches to a specific database on a SQL server [Parameter(ParameterSetName="SQLConnection")] [String]$DataBase, - #The SQL query to run - [Parameter(Mandatory=$true)] + [Parameter(ParameterSetName="SQLConnection", Mandatory=$true)] + [Parameter(ParameterSetName="ODBCConnection",Mandatory=$true)] + [Parameter(ParameterSetName="ExistingSession",Mandatory=$true)] [string]$SQL, - #Override the default query time of 30 seconds. - [int]$QueryTimeout, - #File name for the Excel File + [int]$QueryTimeout, + [Parameter(ParameterSetName="Pre-FetchedData",Mandatory=$true)] + [System.Data.DataTable]$DataTable, $Path, [String]$WorkSheetname = 'Sheet1', [Switch]$KillExcel, - #If Specified, open the file created. [Switch]$Show, [String]$Title, [OfficeOpenXml.Style.ExcelFillStyle]$TitleFillPattern = 'None', @@ -57,11 +176,14 @@ [Int]$TitleSize = 22, [System.Drawing.Color]$TitleBackgroundColor, [String]$Password, + [Hashtable]$PivotTableDefinition, + [Switch]$IncludePivotTable, [String[]]$PivotRows, [String[]]$PivotColumns, $PivotData, - [Switch]$PivotDataToColumn, - [Hashtable]$PivotTableDefinition, + [String[]]$PivotFilter, + [Switch]$PivotDataToColumn, + [Switch]$NoTotalsInPivot, [Switch]$IncludePivotChart, [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie', [Switch]$NoLegend, @@ -78,6 +200,10 @@ [String]$RangeName, [String]$TableName, [OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6', + [Switch]$Barchart, + [Switch]$PieChart, + [Switch]$LineChart , + [Switch]$ColumnChart , [Object[]]$ExcelChartDefinition, [Switch]$AutoNameRange, [Object[]]$ConditionalFormat, @@ -85,55 +211,58 @@ [ScriptBlock]$CellStyleSB, [Int]$StartRow = 1, [Int]$StartColumn = 1, - #If Specified, return an ExcelPackage object to allow further work to be done on the file. + [Switch]$ReturnRange, [Switch]$Passthru ) if ($KillExcel) { Get-Process excel -ErrorAction Ignore | Stop-Process - while (Get-Process excel -ErrorAction Ignore) {} + while (Get-Process excel -ErrorAction Ignore) {Start-Sleep -Milliseconds 250} } #We were either given a session object or a connection string (with, optionally a MSSQLServer parameter) # If we got -MSSQLServer, create a SQL connection, if we didn't but we got -Connection create an ODBC connection - if ($MsSQLserver) { + if ($MsSQLserver -and $Connection) { if ($Connection -notmatch "=") {$Connection = "server=$Connection;trusted_connection=true;timeout=60"} - $Session = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $Connection + $Session = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $Connection if ($Session.State -ne 'Open') {$Session.Open()} if ($DataBase) {$Session.ChangeDatabase($DataBase) } } elseif ($Connection) { - $Session = New-Object -TypeName System.Data.Odbc.OdbcConnection -ArgumentList $Connection ; $Session.ConnectionTimeout = 30 + $Session = New-Object -TypeName System.Data.Odbc.OdbcConnection -ArgumentList $Connection ; $Session.ConnectionTimeout = 30 } - #A session was either passed in or just created. If it's a SQL one make a SQL DataAdapter, otherwise make an ODBC one - if ($Session.GetType().name -match "SqlConnection") { - $dataAdapter = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter -ArgumentList ( - New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $SQL, $Session) + If ($session) { + #A session was either passed in or just created. If it's a SQL one make a SQL DataAdapter, otherwise make an ODBC one + if ($Session.GetType().name -match "SqlConnection") { + $dataAdapter = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter -ArgumentList ( + New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $SQL, $Session) + } + else { + $dataAdapter = New-Object -TypeName System.Data.Odbc.OdbcDataAdapter -ArgumentList ( + New-Object -TypeName System.Data.Odbc.OdbcCommand -ArgumentList $SQL, $Session ) + } + if ($QueryTimeout) {$dataAdapter.SelectCommand.CommandTimeout = $ServerTimeout} + + #Both adapter types output the same kind of table, create one and fill it from the adapter + $DataTable = New-Object -TypeName System.Data.DataTable + $rowCount = $dataAdapter.fill($dataTable) + Write-Verbose -Message "Query returned $rowCount row(s)" } - else { - $dataAdapter = New-Object -TypeName System.Data.Odbc.OdbcDataAdapter -ArgumentList ( - New-Object -TypeName System.Data.Odbc.OdbcCommand -ArgumentList $SQL, $Session ) + if ($DataTable.Rows) { + #ExportExcel user a -NoHeader parameter so that's what we use here, but needs to be the other way around. + $printHeaders = -not $NoHeader + if ($Title) {$r = $StartRow +1 } + else {$r = $StartRow} + #Get our Excel sheet and fill it with the data + $excelPackage = Export-Excel -Path $Path -WorkSheetname $WorkSheetname -PassThru + $excelPackage.Workbook.Worksheets[$WorkSheetname].Cells[$r,$StartColumn].LoadFromDataTable($dataTable, $printHeaders ) | Out-Null + + #Call export-excel with any parameters which don't relate to the SQL query + "Connection", "Database" , "Session", "MsSQLserver", "Destination" , "SQL" , "DataTable", "Path" | ForEach-Object {$null = $PSBoundParameters.Remove($_) } + Export-Excel -ExcelPackage $excelPackage @PSBoundParameters } - if ($QueryTimeout) {$dataAdapter.SelectCommand.CommandTimeout = $ServerTimeout} - - #Both adapter types output the same kind of table, create one and fill it from the adapter - $dataTable = New-Object -TypeName System.Data.DataTable - $rowCount = $dataAdapter.fill($dataTable) - Write-Verbose -Message "Query returned $rowCount row(s)" - - #ExportExcel user a -NoHeader parameter so that's what we use here, but needs to be the other way around. - $printHeaders = -not $NoHeader - if ($Title) {$r = $StartRow +1 } - else {$r = $StartRow} - #Get our Excel sheet and fill it with the data - $excelPackage = Export-Excel -Path $Path -WorkSheetname $WorkSheetname -PassThru - $excelPackage.Workbook.Worksheets[$WorkSheetname].Cells[$r,$StartColumn].LoadFromDataTable($dataTable, $printHeaders ) | Out-Null - - #Call export-excel with any parameters which don't relate to the SQL query - "Connection", "Database" , "Session", "MsSQLserver", "Destination" , "SQL" ,"Path" | ForEach-Object {$null = $PSBoundParameters.Remove($_) } - Export-Excel -ExcelPackage $excelPackage @PSBoundParameters - - #If we were not passed a session close the session we created. + else {Write-Warning -Message "No Data to insert."} + #If we were passed a connection and opened a session, close that session. if ($Connection) {$Session.close() } } diff --git a/ToDo.md b/ToDo.md index 304b468..60d0c5d 100644 --- a/ToDo.md +++ b/ToDo.md @@ -1,4 +1,9 @@ - [ ] Create an autocomplete for WorkSheetName param on ImportExcel -- [ ] Add help text for parmaters which don't have it in Export Excel +- [ ] Add help text for parmaters which don't have it ( PivotDataToColumn , NoClobber and CellStyleSB ) in Export Excel, copy to Send-SQLDataToExcel - [ ] Add checks for valid worksheet names (also check pivot names, range names and table names are valid) -- [ ] Investigate regional support for number conversion \ No newline at end of file +- [ ] Investigate regional support for number conversion +- [ ] Add help in ConvertToExcelXLSx.ps1, Copy-ExcelWorkSheet.ps1 (probably re-write copy) +- [ ] Add Help (continued) in Get-HTMLTable.ps1, GetRange.PS1, GetExcelTable.Ps1, Import-HTML.PS1, New-ConditionalFormattingIconSet.Ps1, NewConditionalText.PS1, New-Psitem.PS1, Remove-Worksheet.ps1 + [ ] Copy parameter help from function Add-ExcelChart into New-ExcelChart.ps1 +- [ ] Examples and tests for new "Quick charts" in Export Excel +- [ ] Charting.ps1,GetXYRange.ps1, InferData.PS1 move to deprecated. (replace examples) diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 index 541c7ae..4862751 100644 --- a/__tests__/Export-Excel.Tests.ps1 +++ b/__tests__/Export-Excel.Tests.ps1 @@ -81,7 +81,7 @@ Describe ExportExcel { $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. " { + 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. @@ -146,60 +146,92 @@ Describe ExportExcel { Context "#Examples 3 & 4 # Setting cells for different data types Also added test for URI type" { + if ((Get-Culture).NumberFormat.CurrencySymbol -eq "£") {$OtherCurrencySymbol = "$"} + else {$OtherCurrencySymbol = "£"} $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 + Date = Get-Date + Formula1 = '=SUM(F2:G2)' + String1 = 'My String' + Float = [math]::pi + IPAddress = '10.10.25.5' + StrLeadZero = '07670' + StrComma = '0,26' + StrEngThousand = '1,234.56' + StrEuroThousand = '1.555,83' + StrDot = '1.2' + StrNegInt = '-31' + StrTrailingNeg = '31-' + StrParens = '(123)' + strLocalCurrency = ('{0}123.45' -f (Get-Culture).NumberFormat.CurrencySymbol ) + strOtherCurrency = ('{0}123.45' -f $OtherCurrencySymbol ) + StrE164Phone = '+32 (444) 444 4444' + StrAltPhone1 = '+32 4 4444 444' + StrAltPhone2 = '+3244444444' + StrLeadSpace = ' 123' + StrTrailSpace = '123 ' + Link1 = [uri]"https://github.com/dfinke/ImportExcel" #2,15 + Link2 = "https://github.com/dfinke/ImportExcel" #2, 16 + } | Export-Excel -NoNumberConversion IPAddress, StrLeadZero, StrAltPhone2 -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.Columns | Should be 22 $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 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 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 strings in Cells E2, F2 and R2 (no number conversion) " { + $ws.Cells[2, 5].Value.GetType().name | Should be 'String' + $ws.Cells[2, 6].Value.GetType().name | Should be 'String' + $ws.Cells[2, 18].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 numbers in Cells K2,L2,M2 (diferent Negative integer formats) " { + ($ws.Cells[2, 11].Value -is [valuetype] ) | Should be $true + ($ws.Cells[2, 12].Value -is [valuetype] ) | Should be $true + ($ws.Cells[2, 13].Value -is [valuetype] ) | Should be $true + $ws.Cells[2, 11].Value | Should beLessThan 0 + $ws.Cells[2, 12].Value | Should beLessThan 0 + $ws.Cells[2, 13].Value | Should beLessThan 0 } - - it "Set a hyperlink in Cell N2 " { - $ws.Cells[2, 14].Hyperlink | Should be "https://github.com/dfinke/ImportExcel" + it "Set hyperlinks in Cells U2 and V2 " { + $ws.Cells[2, 21].Hyperlink | Should be "https://github.com/dfinke/ImportExcel" + $ws.Cells[2, 22].Hyperlink | Should be "https://github.com/dfinke/ImportExcel" } + it "Processed thousands according to local settings (Cells H2 and I2) " { + if ((Get-Culture).NumberFormat.NumberGroupSeparator = ",") { + ($ws.Cells[2, 8].Value -is [valuetype] ) | Should be $true + $ws.Cells[2, 9].Value.GetType().name | Should be 'String' + } + elseif ((Get-Culture).NumberFormat.NumberGroupSeparator = ".") { + ($ws.Cells[2, 9].Value -is [valuetype] ) | Should be $true + $ws.Cells[2, 8].Value.GetType().name | Should be 'String' + } + } + it "Processed local currency as a number and other currency as a string (N2 & O2) " { + ($ws.Cells[2, 14].Value -is [valuetype] ) | Should be $true + $ws.Cells[2, 15].Value.GetType().name | Should be 'String' + } + it "Processed numbers with spaces between digits as strings (P2 & Q2) " { + $ws.Cells[2, 16].Value.GetType().name | Should be 'String' + $ws.Cells[2, 17].Value.GetType().name | Should be 'String' + } + it "Processed numbers leading or trailing speaces as Numbers (S2 & T2) " { + ($ws.Cells[2, 19].Value -is [valuetype] ) | Should be $true + ($ws.Cells[2, 20].Value -is [valuetype] ) | Should be $true + } } Context "# # Setting cells for different data types with -noHeader" { @@ -438,7 +470,7 @@ Describe ExportExcel { $excel.Workbook.Worksheets[6].Name | Should be "Sheet1" } - it "Cloned 'Sheet1' to 'NewSheet' " { + 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) @@ -692,13 +724,13 @@ Describe ExportExcel { $r = Get-ChildItem -path C:\WINDOWS\system32 -File "Biggest files" | Export-Excel -Path $path -StartRow 1 -StartColumn 7 - $r | Sort-Object length -Descending | Select -First 14 Name, @{n="Size";e={$_.Length}} | + $r | Sort-Object length -Descending | Select-Object -First 14 Name, @{n="Size";e={$_.Length}} | Export-Excel -Path $path -TableName FileSize -StartRow 2 -StartColumn 7 -TableStyle Medium2 $r.extension | Group-Object | Sort-Object -Property count -Descending | Select-Object -First 12 Name, Count | Export-Excel -Path $path -TableName ExtSize -Title "Frequent Extensions" -TitleSize 11 - $r | Group-Object -Property extension | Select-Object Name, @{n="Size"; e={($_.group | measure -property length -sum).sum}} | + $r | Group-Object -Property extension | Select-Object Name, @{n="Size"; e={($_.group | Measure-Object -property length -sum).sum}} | Sort-Object -Property size -Descending | Select-Object -First 10 | Export-Excel -Path $path -TableName ExtCount -Title "Biggest extensions" -TitleSize 11 -StartColumn 4 -AutoSize @@ -707,17 +739,15 @@ Describe ExportExcel { it "Created 3 tables " { $ws.tables.count | should be 3 } - it "Created the FileSize table in the right places with the right size " { + it "Created the FileSize table in the right place with the right size and style " { $ws.Tables["FileSize"].Address.Address | should be "G2:H16" #Insert at row 2, Column 7, 14 rows x 2 columns of data $ws.Tables["FileSize"].StyleName | should be "TableStyleMedium2" } - it "Created the ExtSize table in the right places with the right size " { + it "Created the ExtSize table in the right place with the right size " { $ws.Tables["ExtSize"].Address.Address | should be "A2:B14" #tile, then 12 rows x 2 columns of data - $ws.Tables["ExtSize"].TableStyle.tostring() | should be "medium6" } - it "Created the ExtSize table in the right places with the right size " { - $ws.Tables["ExtSize"].Address.Address | should be "A2:B14" #tile, then 12 rows x 2 columns of data - $ws.Tables["ExtSize"].TableStyle.tostring() | should be "medium6" + it "Created the ExtCount table in the right place with the right size " { + $ws.Tables["ExtCount"].Address.Address | should be "D2:E12" #title, then 10 rows x 2 columns of data } } From e699fba96780f391a73768c24b35123e27cdd705 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 12 Jul 2018 16:53:55 +0100 Subject: [PATCH 09/21] new Merge worksheet test and a bug fix for merge with wide data --- __tests__/Compare-WorkSheet.tests.ps1 | 250 ++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 __tests__/Compare-WorkSheet.tests.ps1 diff --git a/__tests__/Compare-WorkSheet.tests.ps1 b/__tests__/Compare-WorkSheet.tests.ps1 new file mode 100644 index 0000000..f14e40c --- /dev/null +++ b/__tests__/Compare-WorkSheet.tests.ps1 @@ -0,0 +1,250 @@ +#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" + [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" | Sort-Object -Property _row, _file + + 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 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 + $comp[0].DisplayName | should be $row4Displayname + $comp[1].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 | Sort-Object _row,_file + $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 check and 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 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 + $comp[0].DisplayName | should be $row4Displayname + $comp[1].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 + + +} + +Describe "Merge Worksheet" { + + Remove-Item -Path "$env:temp\server*.xlsx" , "$env:temp\Combined*.xlsx" -ErrorAction SilentlyContinue + [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 + $s[2].DisplayName = "Changed from the orginal" + + $d = $s[-1] | Select-Object -Property * + $d.DisplayName = "Dummy Service" + $d.Name = "Dummy" + $s.Insert(3,$d) + + $s.RemoveAt(5) + + $s | Export-Excel -Path $env:temp\server2.xlsx + #Assume default worksheet name, (sheet1) and column header for key ("name") + Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined1.xlsx" -Property name,displayname,startType -Key name + $excel = Open-ExcelPackage -Path "$env:temp\combined1.xlsx" + $ws = $excel.Workbook.Worksheets["sheet1"] + Context "Merge with 3 properties" { + it "Created a worksheet with the correct headings " { + $ws | should not beNullOrEmpty + $ws.Cells[ 1,1].Value | should be "name" + $ws.Cells[ 1,2].Value | should be "DisplayName" + $ws.Cells[ 1,3].Value | should be "StartType" + $ws.Cells[ 1,4].Value | should be "Server2 DisplayName" + $ws.Cells[ 1,5].Value | should be "Server2 StartType" + } + it "Joined the two sheets correctly " { + $ws.Cells[ 2,2].Value | should be $ws.Cells[ 2,4].Value + $ws.Cells[ 2,3].Value | should be $ws.Cells[ 2,5].Value + $ws.cells[ 4,4].value | should be "Changed from the orginal" + $ws.cells[ 5,1].value | should be "Dummy" + $ws.cells[ 5,2].value | should beNullOrEmpty + $ws.cells[ 5,3].value | should beNullOrEmpty + $ws.cells[ 5,4].value | should be "Dummy Service" + $ws.cells[ 7,4].value | should beNullOrEmpty + $ws.cells[ 7,5].value | should beNullOrEmpty + $ws.Cells[12,2].Value | should be $ws.Cells[12,4].Value + $ws.Cells[12,3].Value | should be $ws.Cells[12,5].Value + } + it "highlighted the keys in the added / deleted / changed rows " { + $ws.cells[4,1].Style.font.color.rgb | should be "FF8b0000" + $ws.cells[5,1].Style.font.color.rgb | should be "FF8b0000" + $ws.cells[7,1].Style.font.color.rgb | should be "FF8b0000" + } + it "Set the background for the added / deleted /changed rows " { + $ws.cells["A3:E3"].style.Fill.BackgroundColor.Rgb | should beNullOrEmpty + $ws.cells["A4:E4"].style.Fill.BackgroundColor.Rgb | should be "FFFFA500" + $ws.cells["A5" ].style.Fill.BackgroundColor.Rgb | should be "FF98FB98" + $ws.cells["B5:C5"].style.Fill.BackgroundColor.rgb | should beNullOrEmpty + $ws.cells["D5:E5"].style.Fill.BackgroundColor.Rgb | should be "FF98FB98" + $ws.cells["A7:C7"].style.Fill.BackgroundColor.Rgb | should be "FFFFB6C1" + $ws.cells["D7:E7"].style.Fill.BackgroundColor.rgb | should beNullOrEmpty + } + } + Context "Wider data set" { + it "Copes with more columns beyond Z in the Output sheet " { + { Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined2.xlsx" } | should not throw + } + } +} \ No newline at end of file From a563857bf375f15ae55a6fb0e41997ae80ce50dd Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 12 Jul 2018 20:17:38 +0100 Subject: [PATCH 10/21] Removed duplicate of compare-Worksheet.tests.psq --- Compare-WorkSheet.tests.ps1 | 184 ------------------------------------ 1 file changed, 184 deletions(-) delete mode 100644 Compare-WorkSheet.tests.ps1 diff --git a/Compare-WorkSheet.tests.ps1 b/Compare-WorkSheet.tests.ps1 deleted file mode 100644 index 34fd98f..0000000 --- a/Compare-WorkSheet.tests.ps1 +++ /dev/null @@ -1,184 +0,0 @@ -#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" - [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" | Sort-Object -Property _row, _file - - 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 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 - $comp[0].DisplayName | should be $row4Displayname - $comp[1].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 | Sort-Object _row,_file - $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 check and 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 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 - $comp[0].DisplayName | should be $row4Displayname - $comp[1].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 - - -} From 8579ce0b784b9e433ec6e7c98318c32786df5629 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 12 Jul 2018 20:21:52 +0100 Subject: [PATCH 11/21] Merged two Join Worksheet sample directories --- .../{Join-Worksheet => JoinWorksheet}/Join-Worksheet.sample.ps1 | 0 .../Join-worksheet-blocks.sample.ps1 | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Examples/{Join-Worksheet => JoinWorksheet}/Join-Worksheet.sample.ps1 (100%) rename Examples/{Join-Worksheet => JoinWorksheet}/Join-worksheet-blocks.sample.ps1 (100%) diff --git a/Examples/Join-Worksheet/Join-Worksheet.sample.ps1 b/Examples/JoinWorksheet/Join-Worksheet.sample.ps1 similarity index 100% rename from Examples/Join-Worksheet/Join-Worksheet.sample.ps1 rename to Examples/JoinWorksheet/Join-Worksheet.sample.ps1 diff --git a/Examples/Join-Worksheet/Join-worksheet-blocks.sample.ps1 b/Examples/JoinWorksheet/Join-worksheet-blocks.sample.ps1 similarity index 100% rename from Examples/Join-Worksheet/Join-worksheet-blocks.sample.ps1 rename to Examples/JoinWorksheet/Join-worksheet-blocks.sample.ps1 From 08155c25f5f2182e623cdd3eb6b94ac09a52e4a4 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 12 Jul 2018 20:26:37 +0100 Subject: [PATCH 12/21] Merged two Join Worksheet sample directories --- .../Join-Worksheet.sample.ps1 | 14 +++++++------- .../Join-worksheet-blocks.sample.ps1 | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) rename Examples/{Join-Worksheet => JoinWorksheet}/Join-Worksheet.sample.ps1 (87%) rename Examples/{Join-Worksheet => JoinWorksheet}/Join-worksheet-blocks.sample.ps1 (90%) diff --git a/Examples/Join-Worksheet/Join-Worksheet.sample.ps1 b/Examples/JoinWorksheet/Join-Worksheet.sample.ps1 similarity index 87% rename from Examples/Join-Worksheet/Join-Worksheet.sample.ps1 rename to Examples/JoinWorksheet/Join-Worksheet.sample.ps1 index ca81895..94ba68f 100644 --- a/Examples/Join-Worksheet/Join-Worksheet.sample.ps1 +++ b/Examples/JoinWorksheet/Join-Worksheet.sample.ps1 @@ -1,8 +1,8 @@ -#Get rid of pre-exisiting sheet +#Get rid of pre-exisiting sheet $path = "$Env:TEMP\test.xlsx" remove-item -Path $path -ErrorAction SilentlyContinue -#Create simple pages for 3 stores with product ID, Product Name, quanity price and total +#Create simple pages for 3 stores with product ID, Product Name, quanity price and total @" ID,Product,Quantity,Price,Total @@ -31,13 +31,13 @@ ID,Product,Quantity,Price,Total 12012,Pliers,3,14.99,44.97 "@ | ConvertFrom-Csv| Export-Excel -Path $path -WorkSheetname Banbury -#define a pivot table with a chart to show a sales by store, broken down by product +#define a pivot table with a chart to show a sales by store, broken down by product $ptdef = New-PivotTableDefinition -PivotTableName "Summary" -PivotRows "Store" -PivotColumns "Product" -PivotData @{"Total"="SUM"} -IncludePivotChart -ChartTitle "Sales Breakdown" -ChartType ColumnStacked -ChartColumn 10 -#Join the 3 worksheets. +#Join the 3 worksheets. #Name the combined page "Total" and Name the column with the sheet names "store" (as the sheets 'Oxford','Abingdon' and 'Banbury' are the names of the stores -#Format the data as a table named "Summary", using the style "Light1", put the column headers in bold +#Format the data as a table named "Summary", using the style "Light1", put the column headers in bold #Put in a title and freeze to top of the sheet including title and colmun headings -#Add the Pivot table. +#Add the Pivot table. #Show the result -Join-Worksheet -Path $path -WorkSheetName "Total" -Clearsheet -FromLabel "Store" -TableName "Summary" -TableStyle Light1 -AutoSize -BoldTopRow -FreezePane 2,1 -Title "Store Sales Summary" -TitleBold -TitleSize 14 -PivotTableDefinition $ptdef -show +Join-Worksheet -Path $path -WorkSheetName "Total" -Clearsheet -FromLabel "Store" -TableName "Summary" -TableStyle Light1 -AutoSize -BoldTopRow -FreezePane 2,1 -Title "Store Sales Summary" -TitleBold -TitleSize 14 -PivotTableDefinition $ptdef -show diff --git a/Examples/Join-Worksheet/Join-worksheet-blocks.sample.ps1 b/Examples/JoinWorksheet/Join-worksheet-blocks.sample.ps1 similarity index 90% rename from Examples/Join-Worksheet/Join-worksheet-blocks.sample.ps1 rename to Examples/JoinWorksheet/Join-worksheet-blocks.sample.ps1 index 81976f4..5ee0506 100644 --- a/Examples/Join-Worksheet/Join-worksheet-blocks.sample.ps1 +++ b/Examples/JoinWorksheet/Join-worksheet-blocks.sample.ps1 @@ -2,10 +2,10 @@ $path = "$env:TEMP\Test.xlsx" Remove-item -Path $path -ErrorAction SilentlyContinue Get-WmiObject -Class win32_logicaldisk | - Select-Object -Property DeviceId,VolumeName, Size,Freespace | + Select-Object -Property DeviceId,VolumeName, Size,Freespace | Export-Excel -Path $path -WorkSheetname Volumes -NumberFormat "0,000" -Get-NetAdapter | - Select-Object -Property Name,InterfaceDescription,MacAddress,LinkSpeed | +Get-NetAdapter | + Select-Object -Property Name,InterfaceDescription,MacAddress,LinkSpeed | Export-Excel -Path $path -WorkSheetname NetAdapters -Join-Worksheet -Path $path -HideSource -WorkSheetName Summary -NoHeader -LabelBlocks -AutoSize -Title "Summary" -TitleBold -TitleSize 22 -show +Join-Worksheet -Path $path -HideSource -WorkSheetName Summary -NoHeader -LabelBlocks -AutoSize -Title "Summary" -TitleBold -TitleSize 22 -show From 87c2ff610d8d0ea22fc3880a8193e3b2cf94fbe2 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 12 Jul 2018 23:58:20 +0100 Subject: [PATCH 13/21] Added JoinWorksheet tests - addition to export-exceltests --- __tests__/Export-Excel.Tests.ps1 | 146 ++++++++++++++--------------- __tests__/Join-Worksheet.tests.ps1 | 103 ++++++++++++++++++++ 2 files changed, 176 insertions(+), 73 deletions(-) create mode 100644 __tests__/Join-Worksheet.tests.ps1 diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 index bc62322..67f1c5c 100644 --- a/__tests__/Export-Excel.Tests.ps1 +++ b/__tests__/Export-Excel.Tests.ps1 @@ -7,62 +7,62 @@ 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 + 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 "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 - # } + it "Started Excel to display the file " { + Get-process -Name Excel, xlim -ErrorAction SilentlyContinue | Should not benullorempty + } - # Start-Sleep -Seconds 5 ; + 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 - # } + #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 - # } + 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) - # } + $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 - # } - # } + $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 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 - # } - # } + 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" @@ -151,24 +151,24 @@ Describe ExportExcel { $path = "$env:TEMP\Test.xlsx" Remove-item -Path $path -ErrorAction SilentlyContinue [PSCustOmobject][Ordered]@{ - Date = Get-Date - Formula1 = '=SUM(F2:G2)' - String1 = 'My String' - Float = [math]::pi - IPAddress = '10.10.25.5' - StrLeadZero = '07670' - StrComma = '0,26' - StrEngThousand = '1,234.56' - StrEuroThousand = '1.555,83' - StrDot = '1.2' - StrNegInt = '-31' + Date = Get-Date + Formula1 = '=SUM(F2:G2)' + String1 = 'My String' + Float = [math]::pi + IPAddress = '10.10.25.5' + StrLeadZero = '07670' + StrComma = '0,26' + StrEngThousand = '1,234.56' + StrEuroThousand = '1.555,83' + StrDot = '1.2' + StrNegInt = '-31' StrTrailingNeg = '31-' StrParens = '(123)' strLocalCurrency = ('{0}123.45' -f (Get-Culture).NumberFormat.CurrencySymbol ) - strOtherCurrency = ('{0}123.45' -f $OtherCurrencySymbol ) - StrE164Phone = '+32 (444) 444 4444' - StrAltPhone1 = '+32 4 4444 444' - StrAltPhone2 = '+3244444444' + strOtherCurrency = ('{0}123.45' -f $OtherCurrencySymbol ) + StrE164Phone = '+32 (444) 444 4444' + StrAltPhone1 = '+32 4 4444 444' + StrAltPhone2 = '+3244444444' StrLeadSpace = ' 123' StrTrailSpace = '123 ' Link1 = [uri]"https://github.com/dfinke/ImportExcel" #2,15 @@ -211,27 +211,27 @@ Describe ExportExcel { $ws.Cells[2, 22].Hyperlink | Should be "https://github.com/dfinke/ImportExcel" } it "Processed thousands according to local settings (Cells H2 and I2) " { - if ((Get-Culture).NumberFormat.NumberGroupSeparator = ",") { + if ((Get-Culture).NumberFormat.NumberGroupSeparator -EQ ",") { ($ws.Cells[2, 8].Value -is [valuetype] ) | Should be $true $ws.Cells[2, 9].Value.GetType().name | Should be 'String' } - elseif ((Get-Culture).NumberFormat.NumberGroupSeparator = ".") { + elseif ((Get-Culture).NumberFormat.NumberGroupSeparator -EQ ".") { ($ws.Cells[2, 9].Value -is [valuetype] ) | Should be $true $ws.Cells[2, 8].Value.GetType().name | Should be 'String' } - } + } it "Processed local currency as a number and other currency as a string (N2 & O2) " { ($ws.Cells[2, 14].Value -is [valuetype] ) | Should be $true $ws.Cells[2, 15].Value.GetType().name | Should be 'String' - } + } it "Processed numbers with spaces between digits as strings (P2 & Q2) " { - $ws.Cells[2, 16].Value.GetType().name | Should be 'String' + $ws.Cells[2, 16].Value.GetType().name | Should be 'String' $ws.Cells[2, 17].Value.GetType().name | Should be 'String' - } + } it "Processed numbers leading or trailing speaces as Numbers (S2 & T2) " { ($ws.Cells[2, 19].Value -is [valuetype] ) | Should be $true ($ws.Cells[2, 20].Value -is [valuetype] ) | Should be $true - } + } } Context "# # Setting cells for different data types with -noHeader" { @@ -724,7 +724,7 @@ Describe ExportExcel { $r = Get-ChildItem -path C:\WINDOWS\system32 -File "Biggest files" | Export-Excel -Path $path -StartRow 1 -StartColumn 7 - $r | Sort-Object length -Descending | Select-Object -First 14 Name, @{n="Size";e={$_.Length}} | + $r | Sort-Object length -Descending | Select-Object -First 14 Name, @{n="Size";e={$_.Length}} | Export-Excel -Path $path -TableName FileSize -StartRow 2 -StartColumn 7 -TableStyle Medium2 $r.extension | Group-Object | Sort-Object -Property count -Descending | Select-Object -First 12 Name, Count | @@ -732,12 +732,12 @@ Describe ExportExcel { $r | Group-Object -Property extension | Select-Object Name, @{n="Size"; e={($_.group | Measure-Object -property length -sum).sum}} | Sort-Object -Property size -Descending | Select-Object -First 10 | - Export-Excel -Path $path -TableName ExtCount -Title "Biggest extensions" -TitleSize 11 -StartColumn 4 -AutoSize + Export-Excel -Path $path -TableName ExtCount -Title "Biggest extensions" -TitleSize 11 -StartColumn 4 -AutoSize $excel = Open-ExcelPackage -Path $path $ws = $excel.Workbook.Worksheets[1] it "Created 3 tables " { - $ws.tables.count | should be 3 + $ws.tables.count | should be 3 } it "Created the FileSize table in the right place with the right size and style " { $ws.Tables["FileSize"].Address.Address | should be "G2:H16" #Insert at row 2, Column 7, 14 rows x 2 columns of data @@ -749,7 +749,7 @@ Describe ExportExcel { it "Created the ExtCount table in the right place with the right size " { $ws.Tables["ExtCount"].Address.Address | should be "D2:E12" #title, then 10 rows x 2 columns of data } - } + } diff --git a/__tests__/Join-Worksheet.tests.ps1 b/__tests__/Join-Worksheet.tests.ps1 new file mode 100644 index 0000000..8e8ce0b --- /dev/null +++ b/__tests__/Join-Worksheet.tests.ps1 @@ -0,0 +1,103 @@ +$data1 = ConvertFrom-Csv -InputObject @" +ID,Product,Quantity,Price,Total +12001,Nails,37,3.99,147.63 +12002,Hammer,5,12.10,60.5 +12003,Saw,12,15.37,184.44 +12010,Drill,20,8,160 +12011,Crowbar,7,23.48,164.36 +"@ +$data2 = ConvertFrom-Csv -InputObject @" +ID,Product,Quantity,Price,Total +12001,Nails,53,3.99,211.47 +12002,Hammer,6,12.10,72.60 +12003,Saw,10,15.37,153.70 +12010,Drill,10,8,80 +12012,Pliers,2,14.99,29.98 +"@ +$data3 = ConvertFrom-Csv -InputObject @" +ID,Product,Quantity,Price,Total +12001,Nails,20,3.99,79.80 +12002,Hammer,2,12.10,24.20 +12010,Drill,11,8,88 +12012,Pliers,3,14.99,44.97 +"@ + +Describe "Join Worksheet" { + BeforeAll { + $path = "$Env:TEMP\test.xlsx" + Remove-Item -Path $path -ErrorAction SilentlyContinue + $data1 | Export-Excel -Path $path -WorkSheetname Oxford + $data2 | Export-Excel -Path $path -WorkSheetname Abingdon + $data3 | Export-Excel -Path $path -WorkSheetname Banbury + $ptdef = New-PivotTableDefinition -PivotTableName "Summary" -PivotRows "Store" -PivotColumns "Product" -PivotData @{"Total"="SUM"} -IncludePivotChart -ChartTitle "Sales Breakdown" -ChartType ColumnStacked -ChartColumn 10 + Join-Worksheet -Path $path -WorkSheetName "Total" -Clearsheet -FromLabel "Store" -TableName "Summary" -TableStyle Light1 -AutoSize -BoldTopRow -FreezePane 2,1 -Title "Store Sales Summary" -TitleBold -TitleSize 14 -PivotTableDefinition $ptdef + $excel = Open-ExcelPackage -Path $path + $ws = $excel.Workbook.Worksheets["Total"] + $pt = $excel.Workbook.Worksheets["Summary"].pivottables[0] + $pc = $excel.Workbook.Worksheets["Summary"].Drawings[0] + } + Context "Merge 3 blocks" { + it "Created sheet of the right size with a title and a table " { + $ws.Dimension.Address | Should be "A1:F16" + $ws.Tables[0].Address.Address | Should be "A2:F16" + $ws.cells["A1"].Value | Should be "Store Sales Summary" + $ws.cells["A1"].Style.Font.Size | Should be 14 + $ws.Tables[0].StyleName | Should be "TableStyleLight1" + $ws.cells["A2:F2"].Style.Font.Bold | Should be $True + } + it "Added a from column with the right heading " { + $ws.cells["F2" ].Value | Should be "Store" + $ws.cells["F3" ].Value | Should be "Oxford" + $ws.cells["F8" ].Value | Should be "Abingdon" + $ws.cells["F13"].Value | Should be "Banbury" + } + it "Filled in the data " { + $ws.cells["C3" ].Value | Should be $data1[0].quantity + $ws.cells["C8" ].Value | Should be $data2[0].quantity + $ws.cells["C13"].Value | Should be $data3[0].quantity + } + it "Created the pivot table " { + $pt | Should not beNullOrEmpty + $pt.StyleName | Should be "PivotStyleMedium9" + $pt.RowFields[0].Name | Should be "Store" + $pt.ColumnFields[0].name | Should be "Product" + $pt.DataFields[0].name | Should be "Sum of Total" + $pc.ChartType | Should be "ColumnStacked" + $pc.Title.text | Should be "Sales Breakdown" + } + } + $path = "$env:TEMP\Test.xlsx" + Remove-item -Path $path -ErrorAction SilentlyContinue + Get-WmiObject -Class win32_logicaldisk | + Select-Object -Property DeviceId,VolumeName, Size,Freespace | + Export-Excel -Path $path -WorkSheetname Volumes -NumberFormat "0,000" + Get-NetAdapter | + Select-Object -Property Name,InterfaceDescription,MacAddress,LinkSpeed | + Export-Excel -Path $path -WorkSheetname NetAdapters + + Join-Worksheet -Path $path -HideSource -WorkSheetName Summary -NoHeader -LabelBlocks -AutoSize -Title "Summary" -TitleBold -TitleSize 22 + $excel = Open-ExcelPackage -Path $path + $ws = $excel.Workbook.Worksheets["Summary"] + Context "3 Unlinked blocks" { + it "Hid the source worksheets " { + $excel.Workbook.Worksheets[1].Hidden.tostring() | should be "Hidden" + $excel.Workbook.Worksheets[2].Hidden.tostring() | should be "Hidden" + } + it "Created the Summary sheet with title, and block labels, and copied the correct data " { + $ws.Cells["A1"].Value | should be "Summary" + $ws.Cells["A2"].Value | should be $excel.Workbook.Worksheets[1].name + $ws.Cells["A3"].Value | should be $excel.Workbook.Worksheets[1].Cells["A1"].value + $ws.Cells["A4"].Value | should be $excel.Workbook.Worksheets[1].Cells["A2"].value + $ws.Cells["B4"].Value | should be $excel.Workbook.Worksheets[1].Cells["B2"].value + $nextRow = $excel.Workbook.Worksheets[1].Dimension.Rows + 3 + $ws.Cells["A$NextRow"].Value | should be $excel.Workbook.Worksheets[2].name + $nextRow ++ + $ws.Cells["A$NextRow"].Value | should be $excel.Workbook.Worksheets[2].Cells["A1"].value + $nextRow ++ + $ws.Cells["A$NextRow"].Value | should be $excel.Workbook.Worksheets[2].Cells["A2"].value + $ws.Cells["B$NextRow"].Value | should be $excel.Workbook.Worksheets[2].Cells["B2"].value + } + } + +} + From 82efbef979322e35894d1e46dfde8607ae5f2870 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Fri, 13 Jul 2018 00:09:21 +0100 Subject: [PATCH 14/21] Mod to export-excel to be appveyor friendly --- __tests__/Export-Excel.Tests.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 index 67f1c5c..a53a6af 100644 --- a/__tests__/Export-Excel.Tests.ps1 +++ b/__tests__/Export-Excel.Tests.ps1 @@ -13,15 +13,15 @@ Describe ExportExcel { $processes = Get-Process $propertyNames = $Processes[0].psobject.properties.name $rowcount = $Processes.Count - $Processes | Export-Excel $path -show + $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 - } + # it "Started Excel to display the file " { + # Get-process -Name Excel, xlim -ErrorAction SilentlyContinue | Should not benullorempty + # } Start-Sleep -Seconds 5 ; From cd1dcde6d028bd084eac88bfa84130fa21734b15 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Fri, 13 Jul 2018 11:10:31 +0100 Subject: [PATCH 15/21] Compare-Worksheet.tests.ps1 changed for latest style guide rules --- __tests__/Compare-WorkSheet.tests.ps1 | 168 +++++++++++++------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/__tests__/Compare-WorkSheet.tests.ps1 b/__tests__/Compare-WorkSheet.tests.ps1 index f14e40c..286190d 100644 --- a/__tests__/Compare-WorkSheet.tests.ps1 +++ b/__tests__/Compare-WorkSheet.tests.ps1 @@ -9,48 +9,48 @@ Describe "Compare Worksheet" { Remove-Item -Path "$env:temp\server*.xlsx" [System.Collections.ArrayList]$s = get-service | Select-Object -Property * - $s | Export-Excel -Path $env:temp\server1.xlsx + $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 + #$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" + $s[2].DisplayName = "Changed from the orginal" - $d = $s[-1] | Select-Object -Property * - $d.DisplayName = "Dummy Service" - $d.Name = "Dummy" - $s.Insert(3,$d) + $d = $s[-1] | Select-Object -Property * + $d.DisplayName = "Dummy Service" + $d.Name = "Dummy" + $s.Insert(3,$d) - $row6Name = $s[5].name + $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" | Sort-Object -Property _row, _file + $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" | Sort-Object -Property _row, _file Context "Simple comparison output" { it "Found the right number of differences " { - $comp | should not beNullOrEmpty + $comp | should not beNullOrEmpty $comp.Count | should be 4 } it "Found the data row with a changed property " { - $comp | should not beNullOrEmpty + $comp | should not beNullOrEmpty $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 - $comp[0].DisplayName | should be $row4Displayname - $comp[1].DisplayName | should be "Changed from the orginal" + $comp[0]._Row | should be 4 + $comp[1]._Row | should be 4 + $comp[1].Name | should be $comp[0].Name + $comp[0].DisplayName | should be $row4Displayname + $comp[1].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 | 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 | should not beNullOrEmpty + $comp[3]._Side | should be '<=' + $comp[3]._Row | should be 6 $comp[3].Name | should be $row6Name } } @@ -70,20 +70,20 @@ Describe "Compare Worksheet" { } 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 + $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 + $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" @@ -94,67 +94,67 @@ Describe "Compare Worksheet" { $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 - + $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 - + + [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 + #$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" + $s[2].DisplayName = "Changed from the orginal" - $d = $s[-1] | Select-Object -Property * - $d.DisplayName = "Dummy Service" - $d.ServiceName = "Dummy" - $s.Insert(3,$d) + $d = $s[-1] | Select-Object -Property * + $d.DisplayName = "Dummy Service" + $d.ServiceName = "Dummy" + $s.Insert(3,$d) - $row6Name = $s[5].ServiceName + $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 | Sort-Object _row,_file + $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 | Sort-Object _row,_file $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 check and different worksheet names " { it "Found the right number of differences " { - $comp | should not beNullOrEmpty + $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 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 - $comp[0].DisplayName | should be $row4Displayname - $comp[1].DisplayName | should be "Changed from the orginal" + $comp | should not beNullOrEmpty + $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 + $comp[0].DisplayName | should be $row4Displayname + $comp[1].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 | 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 | should not beNullOrEmpty + $comp[3]._Side | should be '<=' + $comp[3]._Row | should be 6 $comp[3].ServiceName | should be $row6Name } @@ -163,23 +163,23 @@ Describe "Compare Worksheet" { $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 + $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 - + } @@ -188,20 +188,20 @@ Describe "Merge Worksheet" { Remove-Item -Path "$env:temp\server*.xlsx" , "$env:temp\Combined*.xlsx" -ErrorAction SilentlyContinue [System.Collections.ArrayList]$s = get-service | Select-Object -Property * - $s | Export-Excel -Path $env:temp\server1.xlsx + $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 - $s[2].DisplayName = "Changed from the orginal" + #$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 + $s[2].DisplayName = "Changed from the orginal" - $d = $s[-1] | Select-Object -Property * - $d.DisplayName = "Dummy Service" - $d.Name = "Dummy" - $s.Insert(3,$d) + $d = $s[-1] | Select-Object -Property * + $d.DisplayName = "Dummy Service" + $d.Name = "Dummy" + $s.Insert(3,$d) $s.RemoveAt(5) - $s | Export-Excel -Path $env:temp\server2.xlsx - #Assume default worksheet name, (sheet1) and column header for key ("name") + $s | Export-Excel -Path $env:temp\server2.xlsx + #Assume default worksheet name, (sheet1) and column header for key ("name") Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined1.xlsx" -Property name,displayname,startType -Key name $excel = Open-ExcelPackage -Path "$env:temp\combined1.xlsx" $ws = $excel.Workbook.Worksheets["sheet1"] @@ -217,8 +217,8 @@ Describe "Merge Worksheet" { it "Joined the two sheets correctly " { $ws.Cells[ 2,2].Value | should be $ws.Cells[ 2,4].Value $ws.Cells[ 2,3].Value | should be $ws.Cells[ 2,5].Value - $ws.cells[ 4,4].value | should be "Changed from the orginal" - $ws.cells[ 5,1].value | should be "Dummy" + $ws.cells[ 4,4].value | should be "Changed from the orginal" + $ws.cells[ 5,1].value | should be "Dummy" $ws.cells[ 5,2].value | should beNullOrEmpty $ws.cells[ 5,3].value | should beNullOrEmpty $ws.cells[ 5,4].value | should be "Dummy Service" @@ -234,17 +234,17 @@ Describe "Merge Worksheet" { } it "Set the background for the added / deleted /changed rows " { $ws.cells["A3:E3"].style.Fill.BackgroundColor.Rgb | should beNullOrEmpty - $ws.cells["A4:E4"].style.Fill.BackgroundColor.Rgb | should be "FFFFA500" + $ws.cells["A4:E4"].style.Fill.BackgroundColor.Rgb | should be "FFFFA500" $ws.cells["A5" ].style.Fill.BackgroundColor.Rgb | should be "FF98FB98" $ws.cells["B5:C5"].style.Fill.BackgroundColor.rgb | should beNullOrEmpty $ws.cells["D5:E5"].style.Fill.BackgroundColor.Rgb | should be "FF98FB98" $ws.cells["A7:C7"].style.Fill.BackgroundColor.Rgb | should be "FFFFB6C1" $ws.cells["D7:E7"].style.Fill.BackgroundColor.rgb | should beNullOrEmpty - } + } } Context "Wider data set" { it "Copes with more columns beyond Z in the Output sheet " { - { Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined2.xlsx" } | should not throw + { Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined2.xlsx" } | should not throw } } } \ No newline at end of file From 3697d53bdd4d95deb1487c6661796e16e5512593 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Fri, 13 Jul 2018 18:42:45 +0100 Subject: [PATCH 16/21] Put Get-ExcelColumnName Back in Violated the "break nothing" rule by taking it out --- Get-ExcelColumnName.ps1 | 22 ++++++++++++++++++++++ Get-XYRange.ps1 | 8 ++++---- 2 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 Get-ExcelColumnName.ps1 diff --git a/Get-ExcelColumnName.ps1 b/Get-ExcelColumnName.ps1 new file mode 100644 index 0000000..7562391 --- /dev/null +++ b/Get-ExcelColumnName.ps1 @@ -0,0 +1,22 @@ +function Get-ExcelColumnName { + param( + [Parameter(ValueFromPipeline=$true)] + $columnNumber=1 + ) + + Process { + $dividend = $columnNumber + $columnName = @() + while($dividend -gt 0) { + $modulo = ($dividend - 1) % 26 + $columnName += [char](65 + $modulo) + $dividend = [int](($dividend -$modulo)/26) + } + + [PSCustomObject] @{ + ColumnNumber = $columnNumber + ColumnName = $columnName -join '' + } + + } +} \ No newline at end of file diff --git a/Get-XYRange.ps1 b/Get-XYRange.ps1 index 9aa0cff..179679d 100644 --- a/Get-XYRange.ps1 +++ b/Get-XYRange.ps1 @@ -1,7 +1,7 @@ function Get-XYRange { param($targetData) - $record = $targetData| Select-Object -First 1 + $record = $targetData| select -First 1 $p=$record.psobject.Properties.name $infer = for ($idx = 0; $idx -lt $p.Count; $idx++) { @@ -15,12 +15,12 @@ function Get-XYRange { Name = $name Value = $value DataType = $result.DataType - ExcelColumn = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[1]C[$($idx+1)]", 0 , 0) -replace "\d+", "" #(Get-ExcelColumnName ($idx + 1)).ColumnName + ExcelColumn = (Get-ExcelColumnName ($idx+1)).ColumnName } } [PSCustomObject]@{ - XRange = $infer | ? {$_.datatype -match 'string'} | Select-Object -First 1 excelcolumn, name - YRange = $infer | ? {$_.datatype -match 'int|double'} |Select-Object -First 1 excelcolumn, name + XRange = $infer | ? {$_.datatype -match 'string'} | select -First 1 excelcolumn, name + YRange = $infer | ? {$_.datatype -match 'int|double'} |select -First 1 excelcolumn, name } } \ No newline at end of file From 7de956b3437fe80de508cf2dcf533cb284ca5313 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sat, 14 Jul 2018 23:40:50 +0100 Subject: [PATCH 17/21] Fixed column name handling in merge-multipleWorksheets. More tests --- AddConditionalFormatting.ps1 | 8 +- ImportExcel.psm1 | 3 +- Join-Worksheet.ps1 | 78 ++--- Merge-worksheet.ps1 | 464 ++++++++++++------------- New-ExcelChart.ps1 | 30 +- Open-ExcelPackage.ps1 | 38 +-- README.md | 61 ++-- Send-SqlDataToExcel.ps1 | 140 ++++---- Set-Column.ps1 | 4 +- __tests__/Compare-WorkSheet.tests.ps1 | 474 +++++++++++++++----------- compare-worksheet.ps1 | 234 ++++++------- 11 files changed, 812 insertions(+), 722 deletions(-) diff --git a/AddConditionalFormatting.ps1 b/AddConditionalFormatting.ps1 index db18fc6..b362dc1 100644 --- a/AddConditionalFormatting.ps1 +++ b/AddConditionalFormatting.ps1 @@ -12,7 +12,7 @@ $excel.Save() ; $excel.Dispose() Here Export-Excel is called with the -passThru parameter so the Excel Package object is stored in $Excel - The desired worksheet is selected and the then columns B and i are conditially formatted (excluding the top row) to show red text if + The desired worksheet is selected and the then columns B and i are conditially formatted (excluding the top row) to show red text if the columns contain "2003" or "Disabled respectively. A fixed date formats are then applied to columns D..G, and the top row is formatted. Finally the workbook is saved and the Excel object closed. @@ -84,11 +84,11 @@ #Strikethrough text of matching items [switch]$StrikeThru ) - #Allow conditional formatting to work like Set-Format (with single ADDRESS parameter), split it to get worksheet and range of cells. + #Allow conditional formatting to work like Set-Format (with single ADDRESS parameter), split it to get worksheet and range of cells. If ($Address -and -not $WorkSheet -and -not $Range) { $WorkSheet = $Address.Worksheet[0] - $Range = $Address.Address - } + $Range = $Address.Address + } If ($ThreeIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)} elseif ($FourIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFourIconSet( $Range , $FourIconsSet) } elseif ($FiveIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFiveIconSet( $Range , $IconType) } diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index a81259e..c214007 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -11,6 +11,7 @@ . $PSScriptRoot\Copy-ExcelWorkSheet.ps1 . $PSScriptRoot\Export-Excel.ps1 . $PSScriptRoot\Export-ExcelSheet.ps1 + . $PSScriptRoot\Get-ExcelColumnName.ps1 . $PSScriptRoot\Get-ExcelSheetInfo.ps1 . $PSScriptRoot\Get-ExcelWorkbookInfo.ps1 . $PSScriptRoot\Get-HtmlTable.ps1 @@ -369,7 +370,7 @@ function Import-Excel { $colHash = @{} $rowHash = @{} foreach ($cell in $Worksheet.Cells[$range]) { - if ($cell.Value -ne $null) {$colHash[$cell.Start.Column]=1; $rowHash[$cell.Start.row]=1 } + if ($null -ne $cell.Value ) {$colHash[$cell.Start.Column]=1; $rowHash[$cell.Start.row]=1 } } $rows = ( $StartRow..$EndRow ).Where({$rowHash[$_]}) $columns = ($StartColumn..$EndColumn).Where({$colHash[$_]}) diff --git a/Join-Worksheet.ps1 b/Join-Worksheet.ps1 index 0779f29..8786d1e 100644 --- a/Join-Worksheet.ps1 +++ b/Join-Worksheet.ps1 @@ -1,11 +1,11 @@ -function Join-Worksheet { +function Join-Worksheet { [CmdletBinding(DefaultParameterSetName = 'Default')] <# .SYNOPSIS Combines data on all the sheets in an Excel worksheet onto a single sheet. .DESCRIPTION Join worksheet can work in two main ways: - Either Combining data which has the same layout from many pages into one, or combining pages which have nothing in common. + Either Combining data which has the same layout from many pages into one, or combining pages which have nothing in common. In the former case the header row is copied from the first sheet and, by default, each row of data is labelled with the name of the sheet it came from. In the latter case -NoHeader is specified, and each copied block can have the sheet it came from placed above it as a title. .EXAMPLE @@ -14,37 +14,37 @@ Export-Excel -Path .\test.xlsx -WorkSheetname $computerName -AutoSize } $ptDef =New-PivotTableDefinition -PivotTableName "Pivot1" -SourceWorkSheet "Combined" -PivotRows "Status" -PivotFilter "MachineName" -PivotData @{Status='Count'} -IncludePivotChart -ChartType BarClustered3D - Join-Worksheet -Path .\test.xlsx -WorkSheetName combined -FromLabel "MachineName" -HideSource -AutoSize -FreezeTopRow -BoldTopRow -PivotTableDefinition $pt -Show + Join-Worksheet -Path .\test.xlsx -WorkSheetName combined -FromLabel "MachineName" -HideSource -AutoSize -FreezeTopRow -BoldTopRow -PivotTableDefinition $pt -Show The foreach command gets the services running on four servers and exports each to its own page in Test.xlsx. $PtDef= creates a defintion for a single Pivot table. - The Join-Worksheet command uses the same file and merges the results onto a sheet named "Combined". It sets a column header of "Machinename", - this column will contain the name of the sheet the data was copied from; after copying the data to the sheet "combined", the other sheets will be hidden. - Join-Worksheet finishes by calling export-Excel to AutoSize cells, freeze the top row and make it bold and add the Pivot table. + The Join-Worksheet command uses the same file and merges the results onto a sheet named "Combined". It sets a column header of "Machinename", + this column will contain the name of the sheet the data was copied from; after copying the data to the sheet "combined", the other sheets will be hidden. + Join-Worksheet finishes by calling export-Excel to AutoSize cells, freeze the top row and make it bold and add the Pivot table. .EXAMPLE - Get-WmiObject -Class win32_logicaldisk | select -Property DeviceId,VolumeName, Size,Freespace | + Get-WmiObject -Class win32_logicaldisk | select -Property DeviceId,VolumeName, Size,Freespace | Export-Excel -Path "$env:computerName.xlsx" -WorkSheetname Volumes -NumberFormat "0,000" - Get-NetAdapter | Select-Object Name,InterfaceDescription,MacAddress,LinkSpeed | + Get-NetAdapter | Select-Object Name,InterfaceDescription,MacAddress,LinkSpeed | Export-Excel -Path "$env:COMPUTERNAME.xlsx" -WorkSheetname NetAdapter Join-Worksheet -Path "$env:COMPUTERNAME.xlsx" -WorkSheetName Summary -Title "Summary" -TitleBold -TitleSize 22 -NoHeader -LabelBlocks -AutoSize -HideSource -show - The first two command get logical disk and network card information; each type is exported to its own sheet in a workbook. - The Join-worksheet command copies both onto a page named "Summary". Because the data is disimilar -NoHeader is specified, ensuring the whole of each page is copied. - Specifying -LabelBlocks causes each sheet's name to become a title on the summary page above the copied data. + The first two command get logical disk and network card information; each type is exported to its own sheet in a workbook. + The Join-worksheet command copies both onto a page named "Summary". Because the data is disimilar -NoHeader is specified, ensuring the whole of each page is copied. + Specifying -LabelBlocks causes each sheet's name to become a title on the summary page above the copied data. The source data is hidden, a title is addded in 22 point boldface and the columns are sized to fit the data. #> param ( # Path to a new or existing .XLSX file. [Parameter(ParameterSetName = "Default", Position = 0)] [Parameter(ParameterSetName = "Table" , Position = 0)] - [String]$Path , + [String]$Path , # An object representing an Excel Package - usually this is returned by specifying -Passthru allowing multiple commands to work on the same Workbook without saving and reloading each time. [Parameter(Mandatory = $true, ParameterSetName = "PackageDefault")] [Parameter(Mandatory = $true, ParameterSetName = "PackageTable")] [OfficeOpenXml.ExcelPackage]$ExcelPackage, # The name of a sheet within the workbook where the other sheets will be joined together - "Combined" by default. - $WorkSheetName = 'Combined', + $WorkSheetName = 'Combined', # If specified any pre-existing target for the joined data will be deleted and re-created; otherwise data will be appended on this sheet. [switch]$Clearsheet, #Join-Worksheet assumes each sheet has identical headers and the headers should be copied to the target sheet, unless -NoHeader is specified. @@ -53,7 +53,7 @@ $FromLabel = "From" , #If specified, the copied blocks of data will have the name of the sheet they were copied from inserted above them as a title. [switch]$LabelBlocks, - #Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell. + #Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell. [Switch]$AutoSize, #Freezes headers etc. in the top row. [Switch]$FreezeTopRow, @@ -69,7 +69,7 @@ [Switch]$AutoFilter, #Makes the top Row boldface. [Switch]$BoldTopRow, - #If Specified hides the sheets that the data is copied from. + #If Specified hides the sheets that the data is copied from. [switch]$HideSource, #Text of a title to be placed in Cell A1. [String]$Title, @@ -80,7 +80,7 @@ #Sets the title in boldface type. [Switch]$TitleBold, #Sets the point size for the title. - [Int]$TitleSize = 22, + [Int]$TitleSize = 22, #Hashtable(s) with Sheet PivotRows, PivotColumns, PivotData, IncludePivotChart and ChartType values to specify a definition for one or more pivot table(s). [Hashtable]$PivotTableDefinition, #A hashtable containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. @@ -113,18 +113,18 @@ [switch]$ReturnRange, #Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. [switch]$Show, - #If specified, an object representing the unsaved Excel package will be returned, it then needs to be saved. + #If specified, an object representing the unsaved Excel package will be returned, it then needs to be saved. [switch]$PassThru ) - #region get target worksheet, select it and move it to the end. + #region get target worksheet, select it and move it to the end. if ($Path -and -not $ExcelPackage) {$ExcelPackage = Open-ExcelPackage -path $Path } $destinationSheet = Add-WorkSheet -ExcelPackage $ExcelPackage -WorkSheetname $WorkSheetName -ClearSheet:$Clearsheet foreach ($w in $ExcelPackage.Workbook.Worksheets) {$w.view.TabSelected = $false} $destinationSheet.View.TabSelected = $true $ExcelPackage.Workbook.Worksheets.MoveToEnd($WorkSheetName) #row to insert at will be 1 on a blank sheet and lastrow + 1 on populated one - $row = (1 + $destinationSheet.Dimension.End.Row ) - #endregion + $row = (1 + $destinationSheet.Dimension.End.Row ) + #endregion #region Setup title and header rows #Title parameters work as they do in Export-Excel . @@ -139,7 +139,7 @@ } elseif ($TitleBackgroundColor) { Write-Warning "Title Background Color ignored. You must set the TitleFillPattern parameter to a value other than 'None'. Try 'Solid'." } $row = 2 - } + } if (-not $noHeader) { #Assume every row has titles in row 1, copy row 1 from first sheet to new sheet. @@ -148,48 +148,48 @@ if ($FromLabel ) { #Add a column which says where the data comes from. $fromColumn = ($destinationSheet.Dimension.Columns + 1) - $destinationSheet.Cells[$row, $fromColumn].Value = $FromLabel + $destinationSheet.Cells[$row, $fromColumn].Value = $FromLabel } - $row += 1 + $row += 1 } #endregion foreach ($i in 1..($ExcelPackage.Workbook.Worksheets.Count - 1) ) { - $sourceWorksheet = $ExcelPackage.Workbook.Worksheets[$i] - #Assume row one is titles, so data itself starts at A2. + $sourceWorksheet = $ExcelPackage.Workbook.Worksheets[$i] + #Assume row one is titles, so data itself starts at A2. if ($NoHeader) {$sourceRange = $sourceWorksheet.Dimension.Address} - else {$sourceRange = $sourceWorksheet.Dimension.Address -replace "A1:", "A2:"} - #Position insertion point/ + else {$sourceRange = $sourceWorksheet.Dimension.Address -replace "A1:", "A2:"} + #Position insertion point/ $destinationSheet.Select("A$row") if ($LabelBlocks) { $destinationSheet.Cells[$row, 1].value = $sourceWorksheet.Name $destinationSheet.Cells[$row, 1].Style.Font.Bold = $true $destinationSheet.Cells[$row, 1].Style.Font.Size += 2 - $row += 1 + $row += 1 } $destinationSheet.Select("A$row") - - #And finally we're ready to copy the data. + + #And finally we're ready to copy the data. $sourceWorksheet.Cells[$sourceRange].Copy($destinationSheet.SelectedRange) - #Fill in column saying where data came from. + #Fill in column saying where data came from. if ($fromColumn) { $row..$destinationSheet.Dimension.Rows | ForEach-Object {$destinationSheet.Cells[$_, $fromColumn].Value = $sourceWorksheet.Name} } - #Update where next insertion will go. - $row = $destinationSheet.Dimension.Rows + 1 - if ($HideSource) {$sourceWorksheet.Hidden = [OfficeOpenXml.eWorkSheetHidden]::Hidden} + #Update where next insertion will go. + $row = $destinationSheet.Dimension.Rows + 1 + if ($HideSource) {$sourceWorksheet.Hidden = [OfficeOpenXml.eWorkSheetHidden]::Hidden} } - - #We accept a bunch of parameters work to pass on to Export-excel ( Autosize, Autofilter, boldtopRow Freeze ); if we have any of those call export-excel otherwise close the package here. + + #We accept a bunch of parameters work to pass on to Export-excel ( Autosize, Autofilter, boldtopRow Freeze ); if we have any of those call export-excel otherwise close the package here. $params = @{} + $PSBoundParameters 'Path', 'Clearsheet', 'NoHeader', 'FromLabel', 'LabelBlocks', 'HideSource', - 'Title', 'TitleFillPattern', 'TitleBackgroundColor', 'TitleBold', 'TitleSize' | ForEach-Object {[void]$params.Remove($_)} + 'Title', 'TitleFillPattern', 'TitleBackgroundColor', 'TitleBold', 'TitleSize' | ForEach-Object {[void]$params.Remove($_)} if ($params.Keys.Count) { if ($Title) { $params.StartRow = 2} $params.WorkSheetName = $WorkSheetName $params.ExcelPackage = $ExcelPackage - Export-Excel @Params + Export-Excel @Params } else { - Close-ExcelPackage -ExcelPackage $ExcelPackage + Close-ExcelPackage -ExcelPackage $ExcelPackage $ExcelPackage.Dispose() $ExcelPackage = $null } diff --git a/Merge-worksheet.ps1 b/Merge-worksheet.ps1 index 6d239eb..3046bfb 100644 --- a/Merge-worksheet.ps1 +++ b/Merge-worksheet.ps1 @@ -5,68 +5,68 @@ .Description The Compare-Worksheet command takes two worksheets and marks differences in the source document, and optionally outputs a grid showing the changes. By contrast the Merge-Worksheet command takes the worksheets and combines them into a single sheet showing the old and new data side by side . - Although it is designed to work with Excel data it can work with arrays of any kind of object; so it can be a merge *of* worksheets, or a merge *to* worksheet. - .Example + Although it is designed to work with Excel data it can work with arrays of any kind of object; so it can be a merge *of* worksheets, or a merge *to* worksheet. + .Example merge-worksheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -OutputFile Services.xlsx -OutputSheetName 54-55 -show - The workbooks contain audit information for two servers, one page contains a list of services. This command creates a worksheet named 54-55 - in a workbook named services which shows all the services and their differences, and opens it in Excel. - .Example + The workbooks contain audit information for two servers, one page contains a list of services. This command creates a worksheet named 54-55 + in a workbook named services which shows all the services and their differences, and opens it in Excel. + .Example merge-worksheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -OutputFile Services.xlsx -OutputSheetName 54-55 -HideEqual -AddBackgroundColor LightBlue -show - This modifies the previous command to hide the equal rows in the output sheet and changes the color used to mark rows added to the second file. + This modifies the previous command to hide the equal rows in the output sheet and changes the color used to mark rows added to the second file. .Example merge-worksheet -OutputFile .\j1.xlsx -OutputSheetName test11 -ReferenceObject (dir .\ImportExcel\4.0.7) -DifferenceObject (dir .\ImportExcel\4.0.8) -Property Length -Show - This version compares two directories, and marks what has changed. - Because no "Key" property is given, "Name" is assumed to be the key and the only other property examined is length. + This version compares two directories, and marks what has changed. + Because no "Key" property is given, "Name" is assumed to be the key and the only other property examined is length. Files which are added or deleted or have changed size will be highlighed in the output sheet. Changes to dates or other attributes will be ignored. .Example - merge-worksheet -RefO (dir .\ImportExcel\4.0.7) -DiffO (dir .\ImportExcel\4.0.8) -Pr Length | Out-GridView - This time no file is written and the results -which include all properties, not just length, are output and sent to Out-Gridview. + merge-worksheet -RefO (dir .\ImportExcel\4.0.7) -DiffO (dir .\ImportExcel\4.0.8) -Pr Length | Out-GridView + This time no file is written and the results -which include all properties, not just length, are output and sent to Out-Gridview. This version uses aliases to shorten the parameters, - (OutputFileName can be "outFile" and the sheet "OutSheet" : DifferenceObject & ReferenceObject can be DiffObject & RefObject). + (OutputFileName can be "outFile" and the sheet "OutSheet" : DifferenceObject & ReferenceObject can be DiffObject & RefObject). #> - [cmdletbinding(SupportsShouldProcess=$true)] + [cmdletbinding(SupportsShouldProcess=$true)] Param( #First Excel file to compare. You can compare two Excel files or two other objects but not one of each. [parameter(ParameterSetName='A',Mandatory=$true,Position=0)] [parameter(ParameterSetName='B',Mandatory=$true,Position=0)] [parameter(ParameterSetName='C',Mandatory=$true,Position=0)] $Referencefile , - + #Second Excel file to compare. [parameter(ParameterSetName='A',Mandatory=$true,Position=1)] [parameter(ParameterSetName='B',Mandatory=$true,Position=1)] [parameter(ParameterSetName='C',Mandatory=$true,Position=1)] [parameter(ParameterSetName='E',Mandatory=$true,Position=1)] - $Differencefile , - + $Differencefile , + #Name(s) of worksheets to compare, [parameter(ParameterSetName='A',Position=2)] [parameter(ParameterSetName='B',Position=2)] [parameter(ParameterSetName='C',Position=2)] [parameter(ParameterSetName='E',Position=2)] $WorkSheetName = "Sheet1", - + #The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row. [parameter(ParameterSetName='A')] [parameter(ParameterSetName='B')] [parameter(ParameterSetName='C')] [parameter(ParameterSetName='E')] - [int]$Startrow = 1, - + [int]$Startrow = 1, + #Specifies custom property names to use, instead of the values defined in the column headers of the TopRow. [Parameter(ParameterSetName='B',Mandatory=$true)] - [String[]]$Headername, - + [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=$true)] - [switch]$NoHeader, - - #Object to compare if a worksheet is NOT being used. + [switch]$NoHeader, + + #Object to compare if a worksheet is NOT being used. [parameter(ParameterSetName='D',Mandatory=$true)] [parameter(ParameterSetName='E',Mandatory=$true)] [Alias('RefObject')] $ReferenceObject , - #Object to compare if a worksheet is NOT being used. + #Object to compare if a worksheet is NOT being used. [parameter(ParameterSetName='D',Mandatory=$true,Position=1)] [Alias('DiffObject')] $DifferenceObject , @@ -76,183 +76,183 @@ #File to hold merged data. [parameter(Position=3)] [Alias('OutFile')] - $OutputFile , - #Name of worksheet to output - if none specified will use the reference worksheet name. - [parameter(Position=4)] + $OutputFile , + #Name of worksheet to output - if none specified will use the reference worksheet name. + [parameter(Position=4)] [Alias('OutSheet')] $OutputSheetName = "Sheet1", #Properties to include in the DIFF - supports wildcards, default is "*". $Property = "*" , - #Properties to exclude from the the search - supports wildcards. + #Properties to exclude from the the search - supports wildcards. $ExcludeProperty , #Name of a column which is unique used to pair up rows from the refence and difference side, default is "Name". $Key = "Name" , - #Sets the font color for the "key" field; this means you can filter by color to get only changed rows. - [System.Drawing.Color]$KeyFontColor = "DarkRed", - #Sets the background color for changed rows. + #Sets the font color for the "key" field; this means you can filter by color to get only changed rows. + [System.Drawing.Color]$KeyFontColor = "DarkRed", + #Sets the background color for changed rows. [System.Drawing.Color]$ChangeBackgroundColor = "Orange", - #Sets the background color for rows in the reference but deleted from the difference sheet. - [System.Drawing.Color]$DeleteBackgroundColor = "LightPink", - #Sets the background color for rows not in the reference but added to the difference sheet. - [System.Drawing.Color]$AddBackgroundColor = "PaleGreen", - #if Specified hides the rows in the spreadsheet that are equal and only shows changes, added or deleted rows. + #Sets the background color for rows in the reference but deleted from the difference sheet. + [System.Drawing.Color]$DeleteBackgroundColor = "LightPink", + #Sets the background color for rows not in the reference but added to the difference sheet. + [System.Drawing.Color]$AddBackgroundColor = "PaleGreen", + #if Specified hides the rows in the spreadsheet that are equal and only shows changes, added or deleted rows. [switch]$HideEqual , #If specified outputs the data to the pipeline (you can add -whatif so it the command only outputs to the pipeline). [switch]$Passthru , - #If specified, opens the output workbook. + #If specified, opens the output workbook. [Switch]$Show ) - + #region Read Excel data if ($Referencefile -and $Differencefile) { - #if the filenames don't resolve, give up now. + #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 must have two different worksheet names. If we have two files $worksheetName can be a single string or two strings. + Catch { Write-Warning -Message "Could not Resolve the filenames." ; return } + + #If we have one file , we must have two different worksheet names. If we have two files $worksheetName can be 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" + 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) {$workSheet2 = $DiffPrefix = $WorkSheetName[1] ; $worksheet1 = $WorkSheetName[0] ; } - elseif ($WorkSheetName -is [string]) {$worksheet2 = $workSheet1 = $WorkSheetName ; + if ($WorkSheetName.count -eq 2) {$workSheet2 = $DiffPrefix = $WorkSheetName[1] ; $worksheet1 = $WorkSheetName[0] ; } + elseif ($WorkSheetName -is [string]) {$worksheet2 = $workSheet1 = $WorkSheetName ; $DiffPrefix = (Split-Path -Path $Differencefile -Leaf) -replace "\.xlsx$","" } - else {Write-Warning -Message "You must provide either a single worksheet name or two names." ; return } - - $params= @{ ErrorAction = [System.Management.Automation.ActionPreference]::Stop } + 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 { - $ReferenceObject = Import-Excel -Path $Referencefile -WorksheetName $WorkSheet1 @params - $DifferenceObject = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 @Params + $ReferenceObject = Import-Excel -Path $Referencefile -WorksheetName $WorkSheet1 @params + $DifferenceObject = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 @Params } - Catch {Write-Warning -Message "Could not read the worksheet from $Referencefile::$worksheet1 and/or $Differencefile::$worksheet2." ; return } - if ($NoHeader) {$firstDataRow = $Startrow } else {$firstDataRow = $Startrow + 1} + Catch {Write-Warning -Message "Could not read the worksheet from $Referencefile::$worksheet1 and/or $Differencefile::$worksheet2." ; return } + if ($NoHeader) {$firstDataRow = $Startrow } else {$firstDataRow = $Startrow + 1} } - elseif ( $Differencefile) { - if ($WorkSheetName -isnot [string]) {Write-Warning -Message "You must provide a single worksheet name." ; return } - $params = @{WorkSheetName=$WorkSheetName; Path=$Differencefile; ErrorAction = [System.Management.Automation.ActionPreference]::Stop ;} + elseif ( $Differencefile) { + if ($WorkSheetName -isnot [string]) {Write-Warning -Message "You must provide a single worksheet name." ; return } + $params = @{WorkSheetName=$WorkSheetName; Path=$Differencefile; ErrorAction = [System.Management.Automation.ActionPreference]::Stop ;} try {$DifferenceObject = Import-Excel @Params } - Catch {Write-Warning -Message "Could not read the worksheet '$WorkSheetName' from $Differencefile::$WorkSheetName." ; return } - if ($DiffPrefix -eq "=>" ) { - $DiffPrefix = (Split-Path -Path $Differencefile -Leaf) -replace "\.xlsx$","" + Catch {Write-Warning -Message "Could not read the worksheet '$WorkSheetName' from $Differencefile::$WorkSheetName." ; return } + if ($DiffPrefix -eq "=>" ) { + $DiffPrefix = (Split-Path -Path $Differencefile -Leaf) -replace "\.xlsx$","" } - if ($NoHeader) {$firstDataRow = $Startrow } else {$firstDataRow = $Startrow + 1} + if ($NoHeader) {$firstDataRow = $Startrow } else {$firstDataRow = $Startrow + 1} } - else { $firstDataRow = 1 } - #endregion + else { $firstDataRow = 1 } + #endregion #region Set lists of properties and row numbers - #Make a list of properties/headings using the Property (default "*") and ExcludeProperty parameters - $propList = @() + #Make a list of properties/headings using the Property (default "*") and ExcludeProperty parameters + $propList = @() $DifferenceObject = $DifferenceObject | Update-FirstObjectProperties - $headings = $DifferenceObject[0].psobject.Properties.Name # This preserves the sequence - using get-member would sort them alphabetically! There may be extra properties in - if ($NoHeader -and "Name" -eq $Key) {$Key = "p1"} + $headings = $DifferenceObject[0].psobject.Properties.Name # This preserves the sequence - using get-member would sort them alphabetically! There may be extra properties in + if ($NoHeader -and "Name" -eq $Key) {$Key = "p1"} if ($headings -notcontains $Key -and ('*' -ne $Key)) {Write-Warning -Message "You need to specify one of the headings in the sheet '$worksheet1' as a key." ; return } - foreach ($p in $Property) { $propList += ($headings.where({$_ -like $p}) )} - foreach ($p in $ExcludeProperty) { $propList = $propList.where({$_ -notlike $p}) } + foreach ($p in $Property) { $propList += ($headings.where({$_ -like $p}) )} + foreach ($p in $ExcludeProperty) { $propList = $propList.where({$_ -notlike $p}) } if (($propList -notcontains $Key) -and - ('*' -ne $Key)) { $propList += $Key} #If $key isn't one of the headings we will have bailed by now - $propList = $propList | Select-Object -Unique #so, prolist must contain at least $key if nothing else - - #If key is "*" we treat it differently , and we will create a script property which concatenates all the Properties in $Proplist - $ConCatblock = [scriptblock]::Create( ($proplist | ForEach-Object {'$this."' + $_ + '"'}) -join " + ") + ('*' -ne $Key)) { $propList += $Key} #If $key isn't one of the headings we will have bailed by now + $propList = $propList | Select-Object -Unique #so, prolist must contain at least $key if nothing else - #Build the list of the properties to output, in order. - $diffpart = @() + #If key is "*" we treat it differently , and we will create a script property which concatenates all the Properties in $Proplist + $ConCatblock = [scriptblock]::Create( ($proplist | ForEach-Object {'$this."' + $_ + '"'}) -join " + ") + + #Build the list of the properties to output, in order. + $diffpart = @() $refpart = @() - foreach ($p in $proplist.Where({$key -ne $_}) ) {$refPart += $p ; $diffPart += "$DiffPrefix $p" } - $lastRefColNo = $proplist.count - $FirstDiffColNo = $lastRefColNo + 1 - - if ($key -ne '*') { - $outputProps = @($key) + $refpart + $diffpart - #If we are using a single column as the key, don't duplicate it, so the last difference column will be A if there is one property, C if there are two, E if there are 3 - $lastDiffColNo = (2 * $proplist.count) - 1 + foreach ($p in $proplist.Where({$key -ne $_}) ) {$refPart += $p ; $diffPart += "$DiffPrefix $p" } + $lastRefColNo = $proplist.count + $FirstDiffColNo = $lastRefColNo + 1 + + if ($key -ne '*') { + $outputProps = @($key) + $refpart + $diffpart + #If we are using a single column as the key, don't duplicate it, so the last difference column will be A if there is one property, C if there are two, E if there are 3 + $lastDiffColNo = (2 * $proplist.count) - 1 } else { - $outputProps = @( ) + $refpart + $diffpart - #If we not using a single column as a key all columns are duplicated so, the Last difference column will be B if there is one property, D if there are two, F if there are 3 + $outputProps = @( ) + $refpart + $diffpart + #If we not using a single column as a key all columns are duplicated so, the Last difference column will be B if there is one property, D if there are two, F if there are 3 $lastDiffColNo = (2 * $proplist.count ) - } - + } + #Add RowNumber to every row - #If one sheet has extra rows we can get a single "==" result from compare, with the row from the reference sheet, but + #If one sheet has extra rows we can get a single "==" result from compare, with the row from the reference sheet, but #the row in the other sheet might be different so we will look up the row number from the key field - build a hash table for that here - #If we have "*" as the key ad the script property to concatenate the [selected] properties. - - $Rowhash = @{} + #If we have "*" as the key ad the script property to concatenate the [selected] properties. + + $Rowhash = @{} $rowNo = $firstDataRow foreach ($row in $ReferenceObject) { - if ($row._row -eq $null) {Add-Member -InputObject $row -MemberType NoteProperty -Value ($rowNo ++) -Name "_Row" } - else {$rowNo++ } + if ($null -eq $row._row) {Add-Member -InputObject $row -MemberType NoteProperty -Value ($rowNo ++) -Name "_Row" } + else {$rowNo++ } if ($Key -eq '*' ) {Add-Member -InputObject $row -MemberType ScriptProperty -Value $ConCatblock -Name "_All" } } - $rowNo = $firstDataRow + $rowNo = $firstDataRow foreach ($row in $DifferenceObject) { - Add-Member -InputObject $row -MemberType NoteProperty -Value $rowNo -Name "$DiffPrefix Row" -Force + Add-Member -InputObject $row -MemberType NoteProperty -Value $rowNo -Name "$DiffPrefix Row" -Force if ($Key -eq '*' ) { - Add-Member -InputObject $row -MemberType ScriptProperty -Value $ConCatblock -Name "_All" - $Rowhash[$row._All] = $rowNo - } + Add-Member -InputObject $row -MemberType ScriptProperty -Value $ConCatblock -Name "_All" + $Rowhash[$row._All] = $rowNo + } else {$Rowhash[$row.$key] = $rowNo } $rowNo ++ } - if ($Key -eq '*') {$key = "_ALL"} - #endregion + if ($Key -eq '*') {$key = "_ALL"} + #endregion $expandedDiff = Compare-Object -ReferenceObject $ReferenceObject -DifferenceObject $DifferenceObject -Property $propList -PassThru -IncludeEqual | - Group-Object -Property $key | ForEach-Object { - #The value of the key column is the name of the group. + Group-Object -Property $key | ForEach-Object { + #The value of the key column is the name of the group. $keyval = $_.name - #we're going to create a custom object from a hash table. ??Might no longer need to preserve the field order - $hash = [ordered]@{} + #we're going to create a custom object from a hash table. ??Might no longer need to preserve the field order + $hash = [ordered]@{} foreach ($result in $_.Group) { - if ($result.SideIndicator -ne "=>") {$hash["_Row"] = $result._Row } + if ($result.SideIndicator -ne "=>") {$hash["_Row"] = $result._Row } elseif (-not $hash["$DiffPrefix Row"]) {$hash["_Row"] = "" } - #if we have already set the side, be this must the second record, so set side to indicate "changed" + #if we have already set the side, be this must the second record, so set side to indicate "changed" if ($hash.Side) {$hash.Side = "<>"} else {$hash["Side"] = $result.SideIndicator} switch ($hash.side) { '==' { $hash["$DiffPrefix is"] = 'Same' } '=>' { $hash["$DiffPrefix is"] = 'Added' } '<>' { if (-not $hash["_Row"]) { - $hash["$DiffPrefix is"] = 'Added' + $hash["$DiffPrefix is"] = 'Added' } else { $hash["$DiffPrefix is"] = 'Changed' - } - } - '<=' { $hash["$DiffPrefix is"] = 'Removed'} + } + } + '<=' { $hash["$DiffPrefix is"] = 'Removed'} } - #find the number of the row in the the "difference" object which has this key. If it is the object is only the reference this will be blank. - $hash["$DiffPrefix Row"] = $Rowhash[$keyval] - $hash[$key] = $keyval - #Create FieldName and/or =>FieldName columns + #find the number of the row in the the "difference" object which has this key. If it is the object is only the reference this will be blank. + $hash["$DiffPrefix Row"] = $Rowhash[$keyval] + $hash[$key] = $keyval + #Create FieldName and/or =>FieldName columns foreach ($p in $result.psobject.Properties.name.where({$_ -ne $key -and $_ -ne "SideIndicator" -and $_ -ne "$DiffPrefix Row" })) { - if ($result.SideIndicator -eq "==" -and $p -in $propList) + if ($result.SideIndicator -eq "==" -and $p -in $propList) {$hash[("$p")] = $hash[("$DiffPrefix $p")] = $result.$P} elseif ($result.SideIndicator -eq "==" -or $result.SideIndicator -eq "<=") {$hash[("$p")] = $result.$P} elseif ($result.SideIndicator -eq "=>") { $hash[("$DiffPrefix $p")] = $result.$P} } - } + } [Pscustomobject]$hash - } | Sort-Object -Property "_row" - - #Already sorted by reference row number, fill in any blanks in the difference-row column. - for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i]."$DiffPrefix Row") {$expandedDiff[$i]."$DiffPrefix Row" = $expandedDiff[$i-1]."$DiffPrefix Row" } } - - #Now re-Sort by difference row number, and fill in any blanks in the reference-row column. - $expandedDiff = $expandedDiff | Sort-Object -Property "$DiffPrefix Row" - for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i]."_Row") {$expandedDiff[$i]."_Row" = $expandedDiff[$i-1]."_Row" } } - - $AllProps = @("_Row") + $OutputProps + $expandedDiff[0].psobject.properties.name.where({$_ -notin ($outputProps + @("_row","side","SideIndicator","_ALL" ))}) + } | Sort-Object -Property "_row" - if ($PassThru -or -not $OutputFile) {return ($expandedDiff | Select-Object -Property $allprops | Sort-Object -Property "_row", "$DiffPrefix Row" | Update-FirstObjectProperties ) } - elseif ($PSCmdlet.ShouldProcess($OutputFile,"Write Output to Excel file")) { - $expandedDiff = $expandedDiff | Sort-Object -Property "_row", "$DiffPrefix Row" - $xl = $expandedDiff | Select-Object -Property $OutputProps | Update-FirstObjectProperties | - Export-Excel -Path $OutputFile -WorkSheetname $OutputSheetName -FreezeTopRow -BoldTopRow -AutoSize -AutoFilter -PassThru - $ws = $xl.Workbook.Worksheets[$OutputSheetName] + #Already sorted by reference row number, fill in any blanks in the difference-row column. + for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i]."$DiffPrefix Row") {$expandedDiff[$i]."$DiffPrefix Row" = $expandedDiff[$i-1]."$DiffPrefix Row" } } + + #Now re-Sort by difference row number, and fill in any blanks in the reference-row column. + $expandedDiff = $expandedDiff | Sort-Object -Property "$DiffPrefix Row" + for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i]."_Row") {$expandedDiff[$i]."_Row" = $expandedDiff[$i-1]."_Row" } } + + $AllProps = @("_Row") + $OutputProps + $expandedDiff[0].psobject.properties.name.where({$_ -notin ($outputProps + @("_row","side","SideIndicator","_ALL" ))}) + + if ($PassThru -or -not $OutputFile) {return ($expandedDiff | Select-Object -Property $allprops | Sort-Object -Property "_row", "$DiffPrefix Row" | Update-FirstObjectProperties ) } + elseif ($PSCmdlet.ShouldProcess($OutputFile,"Write Output to Excel file")) { + $expandedDiff = $expandedDiff | Sort-Object -Property "_row", "$DiffPrefix Row" + $xl = $expandedDiff | Select-Object -Property $OutputProps | Update-FirstObjectProperties | + Export-Excel -Path $OutputFile -WorkSheetname $OutputSheetName -FreezeTopRow -BoldTopRow -AutoSize -AutoFilter -PassThru + $ws = $xl.Workbook.Worksheets[$OutputSheetName] for ($i = 0; $i -lt $expandedDiff.Count; $i++ ) { if ( $expandedDiff[$i].side -ne "==" ) { Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -FontColor $KeyFontColor @@ -264,213 +264,215 @@ } elseif ( $expandedDiff[$i].side -eq "<=" ) { $rangeR1C1 = "R[{0}]C[1]:R[{0}]C[{1}]" -f ($i + 2 ) , $lastRefColNo - $range = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1($rangeR1C1,0,0) - Set-Format -WorkSheet $ws -Range $range -BackgroundColor $DeleteBackgroundColor + $range = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1($rangeR1C1,0,0) + Set-Format -WorkSheet $ws -Range $range -BackgroundColor $DeleteBackgroundColor } elseif ( $expandedDiff[$i].side -eq "=>" ) { if ($propList.count -gt 1) { $rangeR1C1 = "R[{0}]C[{1}]:R[{0}]C[{2}]" -f ($i + 2 ) , $FirstDiffColNo , $lastDiffColNo - $range = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1($rangeR1C1,0,0) + $range = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1($rangeR1C1,0,0) Set-Format -WorkSheet $ws -Range $range -BackgroundColor $AddBackgroundColor } - Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -BackgroundColor $AddBackgroundColor - } - } - Close-ExcelPackage -ExcelPackage $xl -Show:$Show - } + Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -BackgroundColor $AddBackgroundColor + } + } + Close-ExcelPackage -ExcelPackage $xl -Show:$Show + } } Function Merge-MultipleSheets { +[cmdletbinding()] +[Alias("Merge-MulipleSheets")] <# .Synopsis - Merges worksheets into a single worksheet with differences marked up. + Merges worksheets into a single worksheet with differences marked up. .Description - The Merge worksheet command combines 2 sheets. Merge-MultipleSheets is designed to merge more than 2. - So if asked to merge sheets A,B,C which contain Services, with a Name, Displayname and Start mode, where "name" is treated as the key - Merge-MultipleSheets calls Merge-Worksheet to merge Name, Displayname and Start mode, from sheets A and C + The Merge worksheet command combines 2 sheets. Merge-MultipleSheets is designed to merge more than 2. + So if asked to merge sheets A,B,C which contain Services, with a Name, Displayname and Start mode, where "name" is treated as the key + Merge-MultipleSheets calls Merge-Worksheet to merge Name, Displayname and Start mode, from sheets A and C the result has column headings -Row, Name, DisplayName, Startmode, C-DisplayName, C-StartMode C-Is, C-Row Merge-MultipleSheets then calls Merge-Worsheet with this result and sheet B, comparing 'Name', 'Displayname' and 'Start mode' columns on each side - which outputs _Row, Name, DisplayName, Startmode, B-DisplayName, B-StartMode B-Is, B-Row, C-DisplayName, C-StartMode C-Is, C-Row - Any columns in the "reference" side which are not used in the comparison are appended on the right, which is we compare the sheets in reverse order. - - The "Is" column holds "Same", "Added", "Removed" or "Changed" and is used for conditional formatting in the output sheet (this is hidden by default), - and when the data is written to Excel the "reference" columns, in this case "DisplayName" and "Start" are renamed to reflect their source, + which outputs _Row, Name, DisplayName, Startmode, B-DisplayName, B-StartMode B-Is, B-Row, C-DisplayName, C-StartMode C-Is, C-Row + Any columns in the "reference" side which are not used in the comparison are appended on the right, which is we compare the sheets in reverse order. + + The "Is" column holds "Same", "Added", "Removed" or "Changed" and is used for conditional formatting in the output sheet (this is hidden by default), + and when the data is written to Excel the "reference" columns, in this case "DisplayName" and "Start" are renamed to reflect their source, so become "A-DisplayName" and "A-Start". - - Conditional formatting is also applied to the "key" column (name in this case) so the view can be filtered to rows with changes by filtering this column on color. - - Note: the processing order can affect what is seen as a change. For example if there is an extra item in sheet B in the example above, + + Conditional formatting is also applied to the "key" column (name in this case) so the view can be filtered to rows with changes by filtering this column on color. + + Note: the processing order can affect what is seen as a change. For example if there is an extra item in sheet B in the example above, Sheet C will be processed and that row and will not be seen to be missing. When sheet B is processed it is marked as an addition, and the conditional formatting marks - the entries from sheet A to show that a values were added in at least one sheet. - However if Sheet B is the reference sheet, A and C will be seen to have an item removed; - and if B is processed before C, the extra item is known when C is processed and so C is considered to be missing that item. + the entries from sheet A to show that a values were added in at least one sheet. + However if Sheet B is the reference sheet, A and C will be seen to have an item removed; + and if B is processed before C, the extra item is known when C is processed and so C is considered to be missing that item. .Example dir Server*.xlsx | Merge-MulipleSheets -WorkSheetName Services -OutputFile Test2.xlsx -OutputSheetName Services -Show - We are auditing servers and each one has a workbook in the current directory which contains a "Services" worksheet (the result of + We are auditing servers and each one has a workbook in the current directory which contains a "Services" worksheet (the result of Get-WmiObject -Class win32_service | Select-Object -Property Name, Displayname, Startmode - No key is specified so the key is assumed to be the "Name" column. The files are merged and the result is opened on completion. - .Example - dir Serv*.xlsx | Merge-MulipleSheets -WorkSheetName Software -Key "*" -ExcludeProperty Install* -OutputFile Test2.xlsx -OutputSheetName Software -Show - The server audit files in the previous example also have "Software" worksheet, but no single field on that sheet works as a key. - Specifying "*" for the key produces a compound key using all non-excluded fields (and the installation date and file location are excluded). - .Example + No key is specified so the key is assumed to be the "Name" column. The files are merged and the result is opened on completion. + .Example + dir Serv*.xlsx | Merge-MulipleSheets -WorkSheetName Software -Key "*" -ExcludeProperty Install* -OutputFile Test2.xlsx -OutputSheetName Software -Show + The server audit files in the previous example also have "Software" worksheet, but no single field on that sheet works as a key. + Specifying "*" for the key produces a compound key using all non-excluded fields (and the installation date and file location are excluded). + .Example Merge-MulipleSheets -Path hotfixes.xlsx -WorkSheetName Serv* -Key hotfixid -OutputFile test2.xlsx -OutputSheetName hotfixes -HideRowNumbers -Show This time all the servers have written their hofix information to their own worksheets in a shared Excel workbook named "Hotfixes" (the information was obtained by running Get-Hotfix | Sort-Object -Property description,hotfixid | Select-Object -Property Description,HotfixID) - This ignores any sheets which are not named "Serv*", and uses the HotfixID as the key ; in this version the row numbers are hidden. + This ignores any sheets which are not named "Serv*", and uses the HotfixID as the key ; in this version the row numbers are hidden. #> param ( - [Parameter(Mandatory=$true,ValueFromPipeline=$true)] + [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [string[]]$Path , #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, - + [int]$Startrow = 1, + #Specifies custom property names to use, instead of the values defined in the column headers of the TopRow. - [String[]]$Headername, + [String[]]$Headername, #Automatically generate property names (P1, P2, P3, ..) instead of the using the values the top row of the sheet. - [switch]$NoHeader, - + [switch]$NoHeader, + #Name(s) of worksheets to compare, $WorkSheetName = "Sheet1", - #File to write output to + #File to write output to [Alias('OutFile')] - $OutputFile = ".\temp.xlsx", - #Name of worksheet to output - if none specified will use the reference worksheet name. + $OutputFile = ".\temp.xlsx", + #Name of worksheet to output - if none specified will use the reference worksheet name. [Alias('OutSheet')] $OutputSheetName = "Sheet1", #Properties to include in the DIFF - supports wildcards, default is "*". $Property = "*" , - #Properties to exclude from the the search - supports wildcards. + #Properties to exclude from the the search - supports wildcards. $ExcludeProperty , #Name of a column which is unique used to pair up rows from the refence and difference side, default is "Name". $Key = "Name" , - #Sets the font color for the "key" field; this means you can filter by color to get only changed rows. - [System.Drawing.Color]$KeyFontColor = "Red", - #Sets the background color for changed rows. + #Sets the font color for the "key" field; this means you can filter by color to get only changed rows. + [System.Drawing.Color]$KeyFontColor = "Red", + #Sets the background color for changed rows. [System.Drawing.Color]$ChangeBackgroundColor = "Orange", - #Sets the background color for rows in the reference but deleted from the difference sheet. - [System.Drawing.Color]$DeleteBackgroundColor = "LightPink", - #Sets the background color for rows not in the reference but added to the difference sheet. - [System.Drawing.Color]$AddBackgroundColor = "Orange", - #if Specified hides the columns in the spreadsheet that contain the row numbers + #Sets the background color for rows in the reference but deleted from the difference sheet. + [System.Drawing.Color]$DeleteBackgroundColor = "LightPink", + #Sets the background color for rows not in the reference but added to the difference sheet. + [System.Drawing.Color]$AddBackgroundColor = "Orange", + #if Specified hides the columns in the spreadsheet that contain the row numbers [switch]$HideRowNumbers , #If specified outputs the data to the pipeline (you can add -whatif so it the command only outputs to the command) [switch]$Passthru , - #If specified, opens the output workbook. + #If specified, opens the output workbook. [Switch]$Show ) begin { $filestoProcess = @() } - process { $filestoProcess += $Path} + process { $filestoProcess += $Path} end { if ($filestoProcess.Count -eq 1 -and $WorkSheetName -match '\*') { Write-Progress -Activity "Merging sheets" -CurrentOperation "Expanding * to names of sheets in $($filestoProcess[0]). " - $excel = Open-ExcelPackage -Path $filestoProcess + $excel = Open-ExcelPackage -Path $filestoProcess $WorksheetName = $excel.Workbook.Worksheets.Name.where({$_ -like $WorkSheetName}) Close-ExcelPackage -NoSave -ExcelPackage $excel } - #Merge indentically named sheets in different work books; + #Merge indentically named sheets in different work books; if ($filestoProcess.Count -ge 2 -and $WorkSheetName -is "string" ) { Get-Variable -Name 'HeaderName','NoHeader','StartRow','Key','Property','ExcludeProperty','WorkSheetName' -ErrorAction SilentlyContinue | - Where-Object {$_.Value} | ForEach-Object -Begin {$params= @{} } -Process {$params[$_.Name] = $_.Value} - + Where-Object {$_.Value} | ForEach-Object -Begin {$params= @{} } -Process {$params[$_.Name] = $_.Value} + Write-Progress -Activity "Merging sheets" -CurrentOperation "Comparing $($filestoProcess[-1]) against $($filestoProcess[0]). " $merged = Merge-Worksheet @params -Referencefile $filestoProcess[0] -Differencefile $filestoProcess[-1] $nextFileNo = 2 - while ($nextFileNo -lt $filestoProcess.count -and $merged) { + while ($nextFileNo -lt $filestoProcess.count -and $merged) { Write-Progress -Activity "Merging sheets" -CurrentOperation "Comparing $($filestoProcess[-$nextFileNo]) against $($filestoProcess[0]). " - $merged = Merge-Worksheet @params -ReferenceObject $merged -Differencefile $filestoProcess[-$nextFileNo] + $merged = Merge-Worksheet @params -ReferenceObject $merged -Differencefile $filestoProcess[-$nextFileNo] $nextFileNo ++ } } - #Merge different sheets from one workbook + #Merge different sheets from one workbook elseif ($filestoProcess.Count -eq 1 -and $WorkSheetName.Count -ge 2 ) { Get-Variable -Name 'HeaderName','NoHeader','StartRow','Key','Property','ExcludeProperty' -ErrorAction SilentlyContinue | - Where-Object {$_.Value} | ForEach-Object -Begin {$params= @{} } -Process {$params[$_.Name] = $_.Value} - + Where-Object {$_.Value} | ForEach-Object -Begin {$params= @{} } -Process {$params[$_.Name] = $_.Value} + Write-Progress -Activity "Merging sheets" -CurrentOperation "Comparing $($WorkSheetName[-1]) against $($WorkSheetName[0]). " $merged = Merge-Worksheet @params -Referencefile $filestoProcess[0] -Differencefile $filestoProcess[0] -WorkSheetName $WorkSheetName[0,-1] $nextSheetNo = 2 - while ($nextSheetNo -lt $WorkSheetName.count -and $merged) { + while ($nextSheetNo -lt $WorkSheetName.count -and $merged) { Write-Progress -Activity "Merging sheets" -CurrentOperation "Comparing $($WorkSheetName[-$nextSheetNo]) against $($WorkSheetName[0]). " $merged = Merge-Worksheet @params -ReferenceObject $merged -Differencefile $filestoProcess[0] -WorkSheetName $WorkSheetName[-$nextSheetNo] -DiffPrefix $WorkSheetName[-$nextSheetNo] $nextSheetNo ++ } } - #We either need one worksheet name and many files or one file and many sheets. + #We either need one worksheet name and many files or one file and many sheets. else { Write-Warning -Message "Need at least two files to process" ; return } - #if the process didn't return data then abandon now. + #if the process didn't return data then abandon now. if (-not $merged) {Write-Warning -Message "The merge operation did not return any data."; return } Write-Progress -Activity "Merging sheets" -CurrentOperation "Creating output sheet '$OutputSheetName' in $OutputFile" $excel = $merged | Sort-Object "_row" | Update-FirstObjectProperties | - Export-Excel -Path $OutputFile -WorkSheetname $OutputSheetName -ClearSheet -BoldTopRow -AutoFilter -PassThru - $sheet = $excel.Workbook.Worksheets[$OutputSheetName] - + Export-Excel -Path $OutputFile -WorkSheetname $OutputSheetName -ClearSheet -BoldTopRow -AutoFilter -PassThru + $sheet = $excel.Workbook.Worksheets[$OutputSheetName] + #We will put in a conditional format for "if all the others are not flagged as 'same'" to mark rows where something is added, removed or changed $sameChecks = @() - - #All the 'difference' columns in the sheet are labeled with the file they came from, 'reference' columns need their - #headers prefixed with the ref file name, $colnames is the basis of a regular expression to identify what should have $refPrefix appended - $colNames = @("_Row") - if ($key -ne "*") - {$colnames += $Key} + + #All the 'difference' columns in the sheet are labeled with the file they came from, 'reference' columns need their + #headers prefixed with the ref file name, $colnames is the basis of a regular expression to identify what should have $refPrefix appended + $colNames = @("^_Row$") + if ($key -ne "*") + {$colnames += "^$Key$"} if ($filesToProcess.Count -ge 2) { - $refPrefix = (Split-Path -Path $filestoProcess[0] -Leaf) -replace "\.xlsx$"," " + $refPrefix = (Split-Path -Path $filestoProcess[0] -Leaf) -replace "\.xlsx$"," " } else {$refPrefix = $WorkSheetName[0] } Write-Progress -Activity "Merging sheets" -CurrentOperation "Applying formatting to sheet '$OutputSheetName' in $OutputFile" - #Find the column headings which are in the form "diffFile is"; which will hold 'Same', 'Added' or 'Changed' + #Find the column headings which are in the form "diffFile is"; which will hold 'Same', 'Added' or 'Changed' foreach ($cell in $sheet.Cells[($sheet.Dimension.Address -replace "\d+$","1")].Where({$_.value -match "\sIS$"}) ) { - #Work leftwards across the headings applying conditional formatting which says - # 'Format this cell if the "IS" column has a value of ...' until you find a heading which doesn't have the prefix. - $prefix = $cell.value -replace "\sIS$","" - $columnNo = $cell.start.Column -1 - $cellAddr = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R1C$columnNo",1,$columnNo) + #Work leftwards across the headings applying conditional formatting which says + # 'Format this cell if the "IS" column has a value of ...' until you find a heading which doesn't have the prefix. + $prefix = $cell.value -replace "\sIS$","" + $columnNo = $cell.start.Column -1 + $cellAddr = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R1C$columnNo",1,$columnNo) while ($sheet.cells[$cellAddr].value -match $prefix) { - $condFormattingParams = @{RuleType='Expression'; BackgroundPattern='None'; WorkSheet=$sheet; Range=$([OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[1]C[$columnNo]:R[1048576]C[$columnNo]",0,0)) } - Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Added"' ) -BackgroundColor $AddBackgroundColor - Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Changed"') -BackgroundColor $ChangeBackgroundColor - Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Removed"') -BackgroundColor $DeleteBackgroundColor - $columnNo -- - $cellAddr = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R1C$columnNo",1,$columnNo) + $condFormattingParams = @{RuleType='Expression'; BackgroundPattern='None'; WorkSheet=$sheet; Range=$([OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[1]C[$columnNo]:R[1048576]C[$columnNo]",0,0)) } + Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Added"' ) -BackgroundColor $AddBackgroundColor + Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Changed"') -BackgroundColor $ChangeBackgroundColor + Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Removed"') -BackgroundColor $DeleteBackgroundColor + $columnNo -- + $cellAddr = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R1C$columnNo",1,$columnNo) } #build up a list of prefixes in $colnames - we'll use that to set headers on rows from the reference file; and build up the "if the 'is' cell isn't same" list $colNames += $prefix $sameChecks += (($cell.Address -replace "1","2") +'<>"Same"') } - - #For all the columns which don't match one of the Diff-file prefixes or "_Row" or the 'Key' columnn name; add the reference file prefix to their header. - $nameRegex = $colNames -Join "|" + + #For all the columns which don't match one of the Diff-file prefixes or "_Row" or the 'Key' columnn name; add the reference file prefix to their header. + $nameRegex = $colNames -Join '|' foreach ($cell in $sheet.Cells[($sheet.Dimension.Address -replace "\d+$","1")].Where({$_.value -Notmatch $nameRegex}) ) { $cell.Value = $refPrefix + $cell.Value $condFormattingParams = @{RuleType='Expression'; BackgroundPattern='None'; WorkSheet=$sheet; Range=[OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[2]C[$($cell.start.column)]:R[1048576]C[$($cell.start.column)]",0,0)} - Add-ConditionalFormatting @condFormattingParams -ConditionValue ("OR(" +(($sameChecks -join ",") -replace '<>"Same"','="Added"') +")" ) -BackgroundColor $DeleteBackgroundColor + Add-ConditionalFormatting @condFormattingParams -ConditionValue ("OR(" +(($sameChecks -join ",") -replace '<>"Same"','="Added"') +")" ) -BackgroundColor $DeleteBackgroundColor } - #We've made a bunch of things wider so now is the time to autofit columns. Any hiding has to come AFTER this, because it unhides things - $sheet.Cells.AutoFitColumns() - - #if we have a key field (we didn't concatenate all fields) use what we built up in $sameChecks to apply conditional formatting to it (Row no will be in column A, Key in Column B) + #We've made a bunch of things wider so now is the time to autofit columns. Any hiding has to come AFTER this, because it unhides things + $sheet.Cells.AutoFitColumns() + + #if we have a key field (we didn't concatenate all fields) use what we built up in $sameChecks to apply conditional formatting to it (Row no will be in column A, Key in Column B) if ($Key -ne '*') { - Add-ConditionalFormatting -WorkSheet $sheet -Range "B2:B1048576" -ForeGroundColor $KeyFontColor -BackgroundPattern 'None' -RuleType Expression -ConditionValue ("OR(" +($sameChecks -join ",") +")" ) + Add-ConditionalFormatting -WorkSheet $sheet -Range "B2:B1048576" -ForeGroundColor $KeyFontColor -BackgroundPattern 'None' -RuleType Expression -ConditionValue ("OR(" +($sameChecks -join ",") +")" ) $sheet.view.FreezePanes(2, 3) } else {$sheet.view.FreezePanes(2, 2) } - #Go back over the headings to find and hide the "is" columns; + #Go back over the headings to find and hide the "is" columns; foreach ($cell in $sheet.Cells[($sheet.Dimension.Address -replace "\d+$","1")].Where({$_.value -match "\sIS$"}) ) { $sheet.Column($cell.start.Column).HIDDEN = $true } - - #If specified, look over the headings for "row" and hide the columns which say "this was in row such-and-such" + + #If specified, look over the headings for "row" and hide the columns which say "this was in row such-and-such" if ($HideRowNumbers) { foreach ($cell in $sheet.Cells[($sheet.Dimension.Address -replace "\d+$","1")].Where({$_.value -match "Row$"}) ) { $sheet.Column($cell.start.Column).HIDDEN = $true } } - - Close-ExcelPackage -ExcelPackage $excel -Show:$Show + + Close-ExcelPackage -ExcelPackage $excel -Show:$Show Write-Progress -Activity "Merging sheets" -Completed } } diff --git a/New-ExcelChart.ps1 b/New-ExcelChart.ps1 index 868b43e..841151c 100644 --- a/New-ExcelChart.ps1 +++ b/New-ExcelChart.ps1 @@ -1,6 +1,6 @@ function New-ExcelChartDefinition { [cmdletbinding()] - [Alias("New-ExcelChart")] #This was the former name. The new name reflects that we are defining a chart, not making one in the workbook. + [Alias("New-ExcelChart")] #This was the former name. The new name reflects that we are defining a chart, not making one in the workbook. param( $Title = "Chart Title", $Header, @@ -21,26 +21,26 @@ function New-ExcelChartDefinition { $SeriesHeader, [Switch]$TitleBold, [Int]$TitleSize , - [String]$XAxisTitleText, + [String]$XAxisTitleText, [Switch]$XAxisTitleBold, $XAxisTitleSize , [string]$XAxisNumberformat, - $XMajorUnit, - $XMinorUnit, + $XMajorUnit, + $XMinorUnit, $XMaxValue, $XMinValue, [OfficeOpenXml.Drawing.Chart.eAxisPosition]$XAxisPosition , - [String]$YAxisTitleText, + [String]$YAxisTitleText, [Switch]$YAxisTitleBold, $YAxisTitleSize, [string]$YAxisNumberformat, - $YMajorUnit, - $YMinorUnit, + $YMajorUnit, + $YMinorUnit, $YMaxValue, $YMinValue, [OfficeOpenXml.Drawing.Chart.eAxisPosition]$YAxisPosition ) - if ( $Header ) {Write-Warning "The header parameter is ignored."} #Nothing was done with it when creating a chart. + if ( $Header ) {Write-Warning "The header parameter is ignored."} #Nothing was done with it when creating a chart. #might be able to do [PSCustomObject]$PsboundParameters, the defaults here match those in Add-Excel Chart [PSCustomObject]@{ Title = $Title @@ -57,22 +57,22 @@ function New-ExcelChartDefinition { ShowCategory = $ShowCategory -as [Boolean] ShowPercent = $ShowPercent -as [Boolean] TitleBold = $TitleBold -as [Boolean] - TitleSize = $TitleSize + TitleSize = $TitleSize SeriesHeader = $SeriesHeader XAxisTitleText = $XAxisTitleText XAxisTitleBold = $XAxisTitleBold -as [Boolean] - XAxisTitleSize = $XAxisTitleSize + XAxisTitleSize = $XAxisTitleSize XAxisNumberformat = $XAxisNumberformat - XMajorUnit = $XMajorUnit + XMajorUnit = $XMajorUnit XMinorUnit = $XMinorUnit XMaxValue = $XMaxValue XMinValue = $XMinValue XAxisPosition = $XAxisPosition - YAxisTitleText = $YAxisTitleText - YAxisTitleBold = $YAxisTitleBold -as [Boolean] + YAxisTitleText = $YAxisTitleText + YAxisTitleBold = $YAxisTitleBold -as [Boolean] YAxisTitleSize = $YAxisTitleSize - YAxisNumberformat = $YAxisNumberformat - YMajorUnit = $YMajorUnit + YAxisNumberformat = $YAxisNumberformat + YMajorUnit = $YMajorUnit YMinorUnit = $YMinorUnit YMaxValue = $YMaxValue YMinValue = $YMinValue diff --git a/Open-ExcelPackage.ps1 b/Open-ExcelPackage.ps1 index 49a77f2..da81907 100644 --- a/Open-ExcelPackage.ps1 +++ b/Open-ExcelPackage.ps1 @@ -1,22 +1,22 @@ Function Open-ExcelPackage { <# -.Synopsis - Returns an Excel Package Object with for the specified XLSX ile +.Synopsis + Returns an Excel Package Object with for the specified XLSX ile .Example - $excel = Open-ExcelPackage -path $xlPath - $sheet1 = $excel.Workbook.Worksheets["sheet1"] - Set-Format -Address $sheet1.Cells["E1:S1048576"], $sheet1.Cells["V1:V1048576"] -NFormat ([cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern) + $excel = Open-ExcelPackage -path $xlPath + $sheet1 = $excel.Workbook.Worksheets["sheet1"] + Set-Format -Address $sheet1.Cells["E1:S1048576"], $sheet1.Cells["V1:V1048576"] -NFormat ([cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern) Close-ExcelPackage $excel -Show - This will open the file at $xlPath, select sheet1 apply formatting to two blocks of the sheet and save the package, and launch it in Excel. + This will open the file at $xlPath, select sheet1 apply formatting to two blocks of the sheet and save the package, and launch it in Excel. #> [OutputType([OfficeOpenXml.ExcelPackage])] Param ( #The Path to the file to open [Parameter(Mandatory=$true)]$Path, - #If specified, any running instances of Excel will be terminated before opening the file. + #If specified, any running instances of Excel will be terminated before opening the file. [switch]$KillExcel, - #By default open only opens an existing file; -Create instructs it to create a new file if required. + #By default open only opens an existing file; -Create instructs it to create a new file if required. [switch]$Create ) @@ -24,33 +24,33 @@ Get-Process -Name "excel" -ErrorAction Ignore | Stop-Process while (Get-Process -Name "excel" -ErrorAction Ignore) {} } - + $Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) - #If -Create was not specified only open the file if it exists already (send a warning if it doesn't exist). + #If -Create was not specified only open the file if it exists already (send a warning if it doesn't exist). if ($Create) { - #Create the directory if required. + #Create the directory if required. $targetPath = Split-Path -Parent -Path $Path if (!(Test-Path -Path $targetPath)) { Write-Debug "Base path $($targetPath) does not exist, creating" $null = New-item -ItemType Directory -Path $targetPath -ErrorAction Ignore } - New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path + New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path } elseif (Test-Path -Path $path) {New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path } - else {Write-Warning "Could not find $path" } + else {Write-Warning "Could not find $path" } } Function Close-ExcelPackage { <# -.Synopsis - Closes an Excel Package, saving, saving under a new name or abandoning changes and opening the file in Excel as required. +.Synopsis + Closes an Excel Package, saving, saving under a new name or abandoning changes and opening the file in Excel as required. #> Param ( #File to close. [parameter(Mandatory=$true, ValueFromPipeline=$true)] [OfficeOpenXml.ExcelPackage]$ExcelPackage, #Open the file. - [switch]$Show, + [switch]$Show, #Abandon the file without saving. [Switch]$NoSave, #Save file with a new name (ignored if -NoSave Specified). @@ -58,10 +58,10 @@ Function Close-ExcelPackage { ) if ( $NoSave) {$ExcelPackage.Dispose()} else { - if ($SaveAs) {$ExcelPackage.SaveAs( $SaveAs ) } + if ($SaveAs) {$ExcelPackage.SaveAs( $SaveAs ) } Else {$ExcelPackage.Save(); $SaveAs = $ExcelPackage.File.FullName } - $ExcelPackage.Dispose() - if ($show) {Start-Process -FilePath $SaveAs } + $ExcelPackage.Dispose() + if ($show) {Start-Process -FilePath $SaveAs } } } diff --git a/README.md b/README.md index 53902c7..3c51951 100644 --- a/README.md +++ b/README.md @@ -31,57 +31,58 @@ 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 11th 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 that -Header does nothing, so it isn't Add-Excel chart and there is a message that does nothing in New-ExcelChartDefinition . -- Added -BarChart -ColumnChart -LineChart -PieChart parameters to Export-Excel for quick charts without giving a full chart definition. -- Added parameters for managing chart Axes and legend +# What's new to 14th July 18 +- Moved chart creation 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 that -Header does nothing, so it isn't Add-Excel chart and there is a message that does nothing in New-ExcelChartDefinition . +- Added -BarChart -ColumnChart -LineChart -PieChart parameters to Export-Excel for quick charts without giving a full chart definition. +- Added parameters for managing chart Axes and legend - Added some chart tests to Export-Excel.tests.ps1. (but tests & examples for quick charts , axes or legends still on the to do list ) -- Fixed some bad code which had been checked-in in-error and caused adding charts to break. (This was not seen outside Github #377) +- Fixed some bad code which had been checked-in in-error and caused adding charts to break. (This was not seen outside Github #377) - 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) -- Removed (2) calls to Get-ExcelColumnName +- Removed (2) calls to Get-ExcelColumnName (Removed and then restored function itself) - 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 -- Fixed an issue with parameter sets in Export-Excel not being determined correctly in some cases (I think this had been resolved before and might have regressed) +- Fixed an issue with parameter sets in Export-Excel not being determined correctly in some cases (I think this had been resolved before and might have regressed) - Reverted the [double]::tryParse in export excel to the previous (longer) way, as the shorter way 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) This means that if there are multiple inserts only inserted data is touched, rather than going as far down and/or right as the furthest used cell. Added a test for this. - Added more of the Parameters from Export-Excel to Join-worksheet, join just calls export-excel with these parameters so there is no code behind them (#383) -- Added more of the Parameters from Export-Excel to Send-SQLDataToExcel, send just calls export-excel with these parameters... +- Added more of the Parameters from Export-Excel to Send-SQLDataToExcel, send just calls export-excel with these parameters... - Added support for passing a System.Data.DataTable directly to Send-SQLDataToExcel +- Fixed a bug in Merge-MultipleSheets where if the key was "name", columns like "displayName" would not be processed correctly, nor would names like "something_ROW". Added tests for Compare, Merge and Join Worksheet # New in June 18 - 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, 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 + 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 - I have moved this from ImportExcel.psm1 to ExportExcel.ps1 and it now can move a new worksheet to the right place, and can copy an existing worksheet (from the same or a different workbook) to a new one, and I set the Set return-type to aid intellisense -- New-PivotTableDefinition - - Now Supports `-PivotFilter` and `-PivotDataToColumn`, `-ChartHeight/width` `-ChartRow/Column`, `-ChartRow/ColumnPixelOffset` parameters -- Set-Format - - Fixed a bug where the `-address` parameter had to be named, although the examples in `export-excel` help showed it working by position (which works now. ) -- Export-Excel - - I've done some re-factoring +- New-PivotTableDefinition + - Now Supports `-PivotFilter` and `-PivotDataToColumn`, `-ChartHeight/width` `-ChartRow/Column`, `-ChartRow/ColumnPixelOffset` parameters +- Set-Format + - Fixed a bug where the `-address` parameter had to be named, although the examples in `export-excel` help showed it working by position (which works now. ) +- Export-Excel + - I've done some re-factoring 1. I "flattened out" small "called-once" functions , add-title, convert-toNumber and Stop-ExcelProcess. - 2. It now uses Add-Worksheet, Open-ExcelPackage and Add-ConditionalFormat instead of duplicating their functionality. + 2. It now uses Add-Worksheet, Open-ExcelPackage and Add-ConditionalFormat instead of duplicating their functionality. 3. I've moved the PivotTable functionality (which was doubled up) out to a new function "Add-PivotTable" which supports some extra parameters PivotFilter and PivotDataToColumn, ChartHeight/width ChartRow/Column, ChartRow/ColumnPixelOffsets. - 4. I've made the try{} catch{} blocks cover smaller blocks of code to give a better idea where a failure happend, some of these now Warn instead of throwing - I'd rather save the data with warnings than throw it away because we can't add a chart. Along with this I've added some extra write-verbose messages + 4. I've made the try{} catch{} blocks cover smaller blocks of code to give a better idea where a failure happend, some of these now Warn instead of throwing - I'd rather save the data with warnings than throw it away because we can't add a chart. Along with this I've added some extra write-verbose messages - Bad column-names specified for Pivots now generate warnings instead of throwing. - Fixed issues when pivottables / charts already exist and an export tries to create them again. - Fixed issue where AutoNamedRange, NamedRange, and TableName do not work when appending to a sheet which already contains the range(s) / table - - Fixed issue where AutoNamedRange may try to create ranges with an illegal name. + - Fixed issue where AutoNamedRange may try to create ranges with an illegal name. - Added check for illegal characters in RangeName or Table Name (replace them with "_"), changed tablename validation to allow spaces and applied same validation to RangeName - - Fixed a bug where BoldTopRow is always bolds row 1 even if the export is told to start at a lower row. - - Fixed a bug where titles throw pivot table creation out of alignment. - - Fixed a bug where Append can overwrite the last rows of data if the initial export had blank rows at the top of the sheet. + - Fixed a bug where BoldTopRow is always bolds row 1 even if the export is told to start at a lower row. + - Fixed a bug where titles throw pivot table creation out of alignment. + - Fixed a bug where Append can overwrite the last rows of data if the initial export had blank rows at the top of the sheet. - Removed the need to specify a fill type when specifying a title background color - - Added MoveToStart, MoveToEnd, MoveBefore and MoveAfter Parameters - these go straight through to Add worksheet - - Added "NoScriptOrAliasProperties" "DisplayPropertySet" switches (names subject to change) - combined with ExcludeProperty these are a quick way to reduce the data exported (and speed things up) - - Added PivotTableName Switch (in line with 5.0.1 release) + - Added MoveToStart, MoveToEnd, MoveBefore and MoveAfter Parameters - these go straight through to Add worksheet + - Added "NoScriptOrAliasProperties" "DisplayPropertySet" switches (names subject to change) - combined with ExcludeProperty these are a quick way to reduce the data exported (and speed things up) + - Added PivotTableName Switch (in line with 5.0.1 release) - Add-CellValue now understands URI item properties. If a property is of type URI it is created as a hyperlink to speed up Add-CellValue - - 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. + - 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 diff --git a/Send-SqlDataToExcel.ps1 b/Send-SqlDataToExcel.ps1 index 44e8437..30eb821 100644 --- a/Send-SqlDataToExcel.ps1 +++ b/Send-SqlDataToExcel.ps1 @@ -1,27 +1,27 @@ Function Send-SQLDataToExcel { <# - .SYNOPSIS + .SYNOPSIS Inserts a DataTable - returned by SQL query into an ExcelSheet, more efficiently than sending it via Export-Excel .DESCRIPTION - This command can accept a data table object or take a SQL command and run it against a database connection. + This command can accept a data table object or take a SQL command and run it against a database connection. If running the SQL command, it accepts an object representing a session with a SQL server or ODBC database, or a connection String to make a session. - It the DataTable is inserted into the Excel sheet + It the DataTable is inserted into the Excel sheet It takes most of the parameters of Export-Excel, but it is more efficient than getting dataRows and piping them into Export-Excel, - data-rows have additional properties which need to be stripped off. - .PARAMETER DataTable - A System.Data.DataTable object containing the data to be inserted into the spreadsheet without running a query. - .PARAMETER Session + data-rows have additional properties which need to be stripped off. + .PARAMETER DataTable + A System.Data.DataTable object containing the data to be inserted into the spreadsheet without running a query. + .PARAMETER Session An active ODBC Connection or SQL connection object representing a session with a database which will be queried to get the data . .PARAMETER Connection Database connection string; either DSN=ODBC_Data_Source_Name, a full odbc or SQL Connection string, or the name of a SQL server. This is used to create a database session. - .PARAMETER MSSQLServer - Specifies the connection string is for SQL server, not ODBC . + .PARAMETER MSSQLServer + Specifies the connection string is for SQL server, not ODBC . .PARAMETER SQL - The SQL query to run against the session which was passed in -Session or set up from $Connection. - .PARAMETER Database + The SQL query to run against the session which was passed in -Session or set up from $Connection. + .PARAMETER Database Switches to a specific database on a SQL server. .PARAMETER QueryTimeout - Override the default query time of 30 seconds. + Override the default query time of 30 seconds. .PARAMETER Path Path to a new or existing .XLSX file. .PARAMETER WorkSheetName @@ -43,7 +43,7 @@ .PARAMETER IncludePivotTable Adds a Pivot table using the data in the worksheet. .PARAMETER PivotTableName - If a Pivot table is created from command line parameters, specificies the name of the new sheet holding the pivot. If Omitted this will be "WorksheetName-PivotTable" + If a Pivot table is created from command line parameters, specificies the name of the new sheet holding the pivot. If Omitted this will be "WorksheetName-PivotTable" .PARAMETER PivotRows Name(s) columns from the spreadhseet which will provide the Row name(s) in a pivot table created from command line parameters. .PARAMETER PivotColumns @@ -57,7 +57,7 @@ .PARAMETER IncludePivotChart Include a chart with the Pivot table - implies -IncludePivotTable. .PARAMETER ChartType - The type for Pivot chart (one of Excel's defined chart types) + The type for Pivot chart (one of Excel's defined chart types) .PARAMETER NoLegend Exclude the legend from the pivot chart. .PARAMETER ShowCategory @@ -66,9 +66,9 @@ Add Percentage labels to the pivot chart. .PARAMETER PivotTableDefinition Instead of describing a single pivot table with mutliple commandline paramters; you can use a HashTable in the form PivotTableName = Definition; - Definition is itself a hashtable with Sheet PivotTows, PivotColumns, PivotData, IncludePivotChart and ChartType values. - .PARAMETER ConditionalFormat - One or more conditional formatting rules defined with New-ConditionalFormattingIconSet. + Definition is itself a hashtable with Sheet PivotTows, PivotColumns, PivotData, IncludePivotChart and ChartType values. + .PARAMETER ConditionalFormat + One or more conditional formatting rules defined with New-ConditionalFormattingIconSet. .PARAMETER ConditionalText Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered. .PARAMETER BoldTopRow @@ -88,9 +88,9 @@ .PARAMETER ColumnChart Creates a "quick" column chart using the first text column as labels and the first numeric column as values .PARAMETER LineChart - Creates a "quick" line chart using the first text column as labels and the first numeric column as values + Creates a "quick" line chart using the first text column as labels and the first numeric column as values .PARAMETER PieChart - Creates a "quick" pie chart using the first text column as labels and the first numeric column as values + Creates a "quick" pie chart using the first text column as labels and the first numeric column as values .PARAMETER ExcelChartDefinition A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. .PARAMETER StartRow @@ -108,52 +108,52 @@ .PARAMETER AutoFilter Enables the 'Filter' in Excel on the complete header row. So users can easily sort, filter and/or search the data in the select column from within Excel. .PARAMETER AutoSize - Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell. + Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell. .PARAMETER Show Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. .PARAMETER ReturnRange - If specified, Export-Excel returns the range of added cells in the format "A1:Z100" + If specified, Export-Excel returns the range of added cells in the format "A1:Z100" .PARAMETER PassThru If specified, Export-Excel returns an object representing the Excel package without saving the package first. To save it you need to call the save or Saveas method or send it back to Export-Excel. .EXAMPLE - C:\> Send-SQLDataToExcel -MsSQLserver -Connection localhost -SQL "select name,type,type_desc from [master].[sys].[all_objects]" -Path .\temp.xlsx -WorkSheetname master -AutoSize -FreezeTopRow -AutoFilter -BoldTopRow + C:\> Send-SQLDataToExcel -MsSQLserver -Connection localhost -SQL "select name,type,type_desc from [master].[sys].[all_objects]" -Path .\temp.xlsx -WorkSheetname master -AutoSize -FreezeTopRow -AutoFilter -BoldTopRow Connects to the local SQL server and selects 3 columns from [Sys].[all_objects] and exports then to a sheet named master with some basic header manager - .EXAMPLE - C:\> $SQL="SELECT top 25 DriverName, Count(RaceDate) as Races, Count(Win) as Wins, Count(Pole) as Poles, Count(FastestLap) as Fastlaps FROM Results GROUP BY DriverName ORDER BY (count(win)) DESC" - C:\> $Connection = 'Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DriverId=790;ReadOnly=0;Dbq=C:\users\James\Documents\f1Results.xlsx;' - C:\> Send-SQLDataToExcel -Connection $connection -SQL $sql -path .\demo1.xlsx -WorkSheetname "Winners" -AutoSize -AutoNameRange + .EXAMPLE + C:\> $SQL="SELECT top 25 DriverName, Count(RaceDate) as Races, Count(Win) as Wins, Count(Pole) as Poles, Count(FastestLap) as Fastlaps FROM Results GROUP BY DriverName ORDER BY (count(win)) DESC" + C:\> $Connection = 'Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DriverId=790;ReadOnly=0;Dbq=C:\users\James\Documents\f1Results.xlsx;' + C:\> Send-SQLDataToExcel -Connection $connection -SQL $sql -path .\demo1.xlsx -WorkSheetname "Winners" -AutoSize -AutoNameRange - This declares a SQL statement and creates an ODBC connection string to read from an Excel file, it then runs the statement and outputs the resulting data to a new spreadsheet. - (the F1 results database is available from https://1drv.ms/x/s!AhfYu7-CJv4ehNdZWxJE9LMAX_N5sg ) + This declares a SQL statement and creates an ODBC connection string to read from an Excel file, it then runs the statement and outputs the resulting data to a new spreadsheet. + (the F1 results database is available from https://1drv.ms/x/s!AhfYu7-CJv4ehNdZWxJE9LMAX_N5sg ) .EXAMPLE C:\> $SQL = "SELECT top 25 DriverName, Count(RaceDate) as Races, Count(Win) as Wins, Count(Pole) as Poles, Count(FastestLap) as Fastlaps FROM Results GROUP BY DriverName ORDER BY (count(win)) DESC" - C:\> Get-SQL -Session F1 -excel -Connection "C:\Users\mcp\OneDrive\public\f1\f1Results.xlsx" -sql $sql -OutputVariable Table | out-null - C:\> Send-SQLDataToExcel -DataTable $Table -Path ".\demo3.xlsx" -WorkSheetname Gpwinners -autosize -TableName winners -TableStyle Light6 -show + C:\> Get-SQL -Session F1 -excel -Connection "C:\Users\mcp\OneDrive\public\f1\f1Results.xlsx" -sql $sql -OutputVariable Table | out-null + C:\> Send-SQLDataToExcel -DataTable $Table -Path ".\demo3.xlsx" -WorkSheetname Gpwinners -autosize -TableName winners -TableStyle Light6 -show - This uses Get-SQL (at least V1.1 download from the gallery with Install-Module -Name GetSQL - note the function is get-SQL the module is GetSQL without the "-" ) - to simplify making database connections and building /submitting SQL statements. - Here it uses the same SQL statement as before; -OutputVariable leaves a System.Data.DataTable object in $table - and Send-SQLDataToExcel puts $table into the worksheet and sets it as an Excel table. + This uses Get-SQL (at least V1.1 download from the gallery with Install-Module -Name GetSQL - note the function is get-SQL the module is GetSQL without the "-" ) + to simplify making database connections and building /submitting SQL statements. + Here it uses the same SQL statement as before; -OutputVariable leaves a System.Data.DataTable object in $table + and Send-SQLDataToExcel puts $table into the worksheet and sets it as an Excel table. (the F1 results database is available from https://1drv.ms/x/s!AhfYu7-CJv4ehNdZWxJE9LMAX_N5sg ) - .EXAMPLE - C:\> $SQL = "SELECT top 25 DriverName, Count(Win) as Wins FROM Results GROUP BY DriverName ORDER BY (count(win)) DESC" - C:\> Send-SQLDataToExcel -Session $DbSessions["f1"] -SQL $sql -Path ".\demo3.xlsx" -WorkSheetname Gpwinners -autosize -ColumnChart - - Like the previous example, this uses Get-SQL (download from the gallery with Install-Module -Name GetSQL).It uses the connection which Get-SQL made rather than an ODFBC connection string - Here the data is presented as a quick chart. .EXAMPLE - C:\> Send-SQLDataToExcel -path .\demo3.xlsx -WorkSheetname "LR" -Connection "DSN=LR" -sql "SELECT name AS CollectionName FROM AgLibraryCollection Collection ORDER BY CollectionName" + C:\> $SQL = "SELECT top 25 DriverName, Count(Win) as Wins FROM Results GROUP BY DriverName ORDER BY (count(win)) DESC" + C:\> Send-SQLDataToExcel -Session $DbSessions["f1"] -SQL $sql -Path ".\demo3.xlsx" -WorkSheetname Gpwinners -autosize -ColumnChart - This example uses an Existing ODBC datasource name "LR" which maps to an adobe lightroom database and gets a list of collection names into a worksheet + Like the previous example, this uses Get-SQL (download from the gallery with Install-Module -Name GetSQL).It uses the connection which Get-SQL made rather than an ODFBC connection string + Here the data is presented as a quick chart. + .EXAMPLE + C:\> Send-SQLDataToExcel -path .\demo3.xlsx -WorkSheetname "LR" -Connection "DSN=LR" -sql "SELECT name AS CollectionName FROM AgLibraryCollection Collection ORDER BY CollectionName" + + This example uses an Existing ODBC datasource name "LR" which maps to an adobe lightroom database and gets a list of collection names into a worksheet #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")] param ( [Parameter(ParameterSetName="SQLConnection", Mandatory=$true)] [Parameter(ParameterSetName="ODBCConnection",Mandatory=$true)] - $Connection, - [Parameter(ParameterSetName="ExistingSession",Mandatory=$true)] + $Connection, + [Parameter(ParameterSetName="ExistingSession",Mandatory=$true)] [System.Data.Common.DbConnection]$Session, [Parameter(ParameterSetName="SQLConnection",Mandatory=$true)] [switch]$MsSQLserver, @@ -161,14 +161,14 @@ [String]$DataBase, [Parameter(ParameterSetName="SQLConnection", Mandatory=$true)] [Parameter(ParameterSetName="ODBCConnection",Mandatory=$true)] - [Parameter(ParameterSetName="ExistingSession",Mandatory=$true)] - [string]$SQL, + [Parameter(ParameterSetName="ExistingSession",Mandatory=$true)] + [string]$SQL, [int]$QueryTimeout, - [Parameter(ParameterSetName="Pre-FetchedData",Mandatory=$true)] + [Parameter(ParameterSetName="Pre-FetchedData",Mandatory=$true)] [System.Data.DataTable]$DataTable, - $Path, - [String]$WorkSheetname = 'Sheet1', - [Switch]$KillExcel, + $Path, + [String]$WorkSheetname = 'Sheet1', + [Switch]$KillExcel, [Switch]$Show, [String]$Title, [OfficeOpenXml.Style.ExcelFillStyle]$TitleFillPattern = 'None', @@ -182,8 +182,8 @@ [String[]]$PivotColumns, $PivotData, [String[]]$PivotFilter, - [Switch]$PivotDataToColumn, - [Switch]$NoTotalsInPivot, + [Switch]$PivotDataToColumn, + [Switch]$NoTotalsInPivot, [Switch]$IncludePivotChart, [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie', [Switch]$NoLegend, @@ -203,7 +203,7 @@ [Switch]$Barchart, [Switch]$PieChart, [Switch]$LineChart , - [Switch]$ColumnChart , + [Switch]$ColumnChart , [Object[]]$ExcelChartDefinition, [Switch]$AutoNameRange, [Object[]]$ConditionalFormat, @@ -214,18 +214,18 @@ [Switch]$ReturnRange, [Switch]$Passthru ) - + if ($KillExcel) { Get-Process excel -ErrorAction Ignore | Stop-Process while (Get-Process excel -ErrorAction Ignore) {Start-Sleep -Milliseconds 250} } - + #We were either given a session object or a connection string (with, optionally a MSSQLServer parameter) # If we got -MSSQLServer, create a SQL connection, if we didn't but we got -Connection create an ODBC connection if ($MsSQLserver -and $Connection) { - if ($Connection -notmatch "=") {$Connection = "server=$Connection;trusted_connection=true;timeout=60"} + if ($Connection -notmatch "=") {$Connection = "server=$Connection;trusted_connection=true;timeout=60"} $Session = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $Connection - if ($Session.State -ne 'Open') {$Session.Open()} + if ($Session.State -ne 'Open') {$Session.Open()} if ($DataBase) {$Session.ChangeDatabase($DataBase) } } elseif ($Connection) { @@ -233,36 +233,36 @@ } If ($session) { - #A session was either passed in or just created. If it's a SQL one make a SQL DataAdapter, otherwise make an ODBC one - if ($Session.GetType().name -match "SqlConnection") { + #A session was either passed in or just created. If it's a SQL one make a SQL DataAdapter, otherwise make an ODBC one + if ($Session.GetType().name -match "SqlConnection") { $dataAdapter = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter -ArgumentList ( New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $SQL, $Session) } else { $dataAdapter = New-Object -TypeName System.Data.Odbc.OdbcDataAdapter -ArgumentList ( - New-Object -TypeName System.Data.Odbc.OdbcCommand -ArgumentList $SQL, $Session ) + New-Object -TypeName System.Data.Odbc.OdbcCommand -ArgumentList $SQL, $Session ) } if ($QueryTimeout) {$dataAdapter.SelectCommand.CommandTimeout = $ServerTimeout} - #Both adapter types output the same kind of table, create one and fill it from the adapter + #Both adapter types output the same kind of table, create one and fill it from the adapter $DataTable = New-Object -TypeName System.Data.DataTable $rowCount = $dataAdapter.fill($dataTable) - Write-Verbose -Message "Query returned $rowCount row(s)" + Write-Verbose -Message "Query returned $rowCount row(s)" } if ($DataTable.Rows) { - #ExportExcel user a -NoHeader parameter so that's what we use here, but needs to be the other way around. + #ExportExcel user a -NoHeader parameter so that's what we use here, but needs to be the other way around. $printHeaders = -not $NoHeader - if ($Title) {$r = $StartRow +1 } - else {$r = $StartRow} - #Get our Excel sheet and fill it with the data + if ($Title) {$r = $StartRow +1 } + else {$r = $StartRow} + #Get our Excel sheet and fill it with the data $excelPackage = Export-Excel -Path $Path -WorkSheetname $WorkSheetname -PassThru $excelPackage.Workbook.Worksheets[$WorkSheetname].Cells[$r,$StartColumn].LoadFromDataTable($dataTable, $printHeaders ) | Out-Null - + #Call export-excel with any parameters which don't relate to the SQL query "Connection", "Database" , "Session", "MsSQLserver", "Destination" , "SQL" , "DataTable", "Path" | ForEach-Object {$null = $PSBoundParameters.Remove($_) } - Export-Excel -ExcelPackage $excelPackage @PSBoundParameters + Export-Excel -ExcelPackage $excelPackage @PSBoundParameters } - else {Write-Warning -Message "No Data to insert."} + else {Write-Warning -Message "No Data to insert."} #If we were passed a connection and opened a session, close that session. - if ($Connection) {$Session.close() } + if ($Connection) {$Session.close() } } diff --git a/Set-Column.ps1 b/Set-Column.ps1 index 02e3081..76e1d6c 100644 --- a/Set-Column.ps1 +++ b/Set-Column.ps1 @@ -26,7 +26,7 @@ $Worksheet, #Column to fill down - first column is 1. 0 will be interpreted as first unused column $Column = 0 , - #First row to fill data in + #First row to fill data in [Int]$StartRow , #value, formula or script block for to fill in. Script block can use $row, $column [number], $ColumnName [letter(s)], $startRow, $startColumn, $endRow, $endColumn [parameter(Mandatory=$true)] @@ -79,7 +79,7 @@ [float]$Width, #Set the inserted data to be a named range (ignored if header is not specified) [Switch]$AutoNameRange, - #If Specified, return an ExcelPackage object to allow further work to be done on the file. + #If Specified, return an ExcelPackage object to allow further work to be done on the file. [switch]$PassThru ) #if we were passed a package object and a worksheet name , get the worksheet. diff --git a/__tests__/Compare-WorkSheet.tests.ps1 b/__tests__/Compare-WorkSheet.tests.ps1 index 286190d..be2cc8c 100644 --- a/__tests__/Compare-WorkSheet.tests.ps1 +++ b/__tests__/Compare-WorkSheet.tests.ps1 @@ -1,250 +1,336 @@ -#Requires -Modules Pester - -# $here = Split-Path -Parent $MyInvocation.MyCommand.Path -# Import-Module $here -Force -Verbose +#Requires -Modules Pester Import-Module $PSScriptRoot\..\ImportExcel.psd1 -Force Describe "Compare Worksheet" { - - Remove-Item -Path "$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" | Sort-Object -Property _row, _file - Context "Simple comparison output" { + BeforeAll { + Remove-Item -Path "$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" | Sort-Object -Property _row, _file + } it "Found the right number of differences " { - $comp | should not beNullOrEmpty - $comp.Count | should be 4 + $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 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 - $comp[0].DisplayName | should be $row4Displayname - $comp[1].DisplayName | should be "Changed from the orginal" + $comp | should not beNullOrEmpty + $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 + $comp[0].DisplayName | should be $row4Displayname + $comp[1].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" + $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 + $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" + BeforeAll { + $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] + } + 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" + $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" + } + AfterAll { + Close-ExcelPackage -ExcelPackage $xl1 -NoSave + Close-ExcelPackage -ExcelPackage $xl2 -NoSave } } - 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" { + BeforeAll { + $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] + } 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" + $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 + $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 } + AfterAll { + Close-ExcelPackage -ExcelPackage $xl1 -NoSave + Close-ExcelPackage -ExcelPackage $xl2 -NoSave + } } - 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 | Sort-Object _row,_file - $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 check and different worksheet names " { + BeforeAll { + [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 | Sort-Object _row,_file + $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"] + } it "Found the right number of differences " { - $comp | should not beNullOrEmpty - $comp.Count | should be 4 + $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 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 - $comp[0].DisplayName | should be $row4Displayname - $comp[1].DisplayName | should be "Changed from the orginal" + $comp | should not beNullOrEmpty + $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 + $comp[0].DisplayName | should be $row4Displayname + $comp[1].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" + $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 + $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" - 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" + $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 + $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 + } + AfterAll { + Close-ExcelPackage -ExcelPackage $xl1 -NoSave -Show + Close-ExcelPackage -ExcelPackage $xl2 -NoSave -Show } - } - Close-ExcelPackage -ExcelPackage $xl1 -NoSave -Show - Close-ExcelPackage -ExcelPackage $xl2 -NoSave -Show - - } Describe "Merge Worksheet" { - - Remove-Item -Path "$env:temp\server*.xlsx" , "$env:temp\Combined*.xlsx" -ErrorAction SilentlyContinue - [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 - $s[2].DisplayName = "Changed from the orginal" - - $d = $s[-1] | Select-Object -Property * - $d.DisplayName = "Dummy Service" - $d.Name = "Dummy" - $s.Insert(3,$d) - - $s.RemoveAt(5) - - $s | Export-Excel -Path $env:temp\server2.xlsx - #Assume default worksheet name, (sheet1) and column header for key ("name") - Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined1.xlsx" -Property name,displayname,startType -Key name - $excel = Open-ExcelPackage -Path "$env:temp\combined1.xlsx" - $ws = $excel.Workbook.Worksheets["sheet1"] Context "Merge with 3 properties" { - it "Created a worksheet with the correct headings " { - $ws | should not beNullOrEmpty - $ws.Cells[ 1,1].Value | should be "name" - $ws.Cells[ 1,2].Value | should be "DisplayName" - $ws.Cells[ 1,3].Value | should be "StartType" - $ws.Cells[ 1,4].Value | should be "Server2 DisplayName" - $ws.Cells[ 1,5].Value | should be "Server2 StartType" + BeforeAll { + Remove-Item -Path "$env:temp\server*.xlsx" , "$env:temp\Combined*.xlsx" -ErrorAction SilentlyContinue + [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 + $s[2].DisplayName = "Changed from the orginal" + + $d = $s[-1] | Select-Object -Property * + $d.DisplayName = "Dummy Service" + $d.Name = "Dummy" + $s.Insert(3,$d) + + $s.RemoveAt(5) + + $s | Export-Excel -Path $env:temp\server2.xlsx + #Assume default worksheet name, (sheet1) and column header for key ("name") + Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined1.xlsx" -Property name,displayname,startType -Key name + $excel = Open-ExcelPackage -Path "$env:temp\combined1.xlsx" + $ws = $excel.Workbook.Worksheets["sheet1"] } - it "Joined the two sheets correctly " { - $ws.Cells[ 2,2].Value | should be $ws.Cells[ 2,4].Value - $ws.Cells[ 2,3].Value | should be $ws.Cells[ 2,5].Value - $ws.cells[ 4,4].value | should be "Changed from the orginal" - $ws.cells[ 5,1].value | should be "Dummy" - $ws.cells[ 5,2].value | should beNullOrEmpty - $ws.cells[ 5,3].value | should beNullOrEmpty - $ws.cells[ 5,4].value | should be "Dummy Service" - $ws.cells[ 7,4].value | should beNullOrEmpty - $ws.cells[ 7,5].value | should beNullOrEmpty - $ws.Cells[12,2].Value | should be $ws.Cells[12,4].Value - $ws.Cells[12,3].Value | should be $ws.Cells[12,5].Value + it "Created a worksheet with the correct headings " { + $ws | should not beNullOrEmpty + $ws.Cells[ 1,1].Value | Should be "name" + $ws.Cells[ 1,2].Value | Should be "DisplayName" + $ws.Cells[ 1,3].Value | Should be "StartType" + $ws.Cells[ 1,4].Value | Should be "Server2 DisplayName" + $ws.Cells[ 1,5].Value | Should be "Server2 StartType" } - it "highlighted the keys in the added / deleted / changed rows " { - $ws.cells[4,1].Style.font.color.rgb | should be "FF8b0000" - $ws.cells[5,1].Style.font.color.rgb | should be "FF8b0000" - $ws.cells[7,1].Style.font.color.rgb | should be "FF8b0000" + it "Joined the two sheets correctly " { + $ws.Cells[ 2,2].Value | Should be $ws.Cells[ 2,4].Value + $ws.Cells[ 2,3].Value | Should be $ws.Cells[ 2,5].Value + $ws.cells[ 4,4].value | Should be "Changed from the orginal" + $ws.cells[ 5,1].value | Should be "Dummy" + $ws.cells[ 5,2].value | Should beNullOrEmpty + $ws.cells[ 5,3].value | Should beNullOrEmpty + $ws.cells[ 5,4].value | Should be "Dummy Service" + $ws.cells[ 7,4].value | Should beNullOrEmpty + $ws.cells[ 7,5].value | Should beNullOrEmpty + $ws.Cells[12,2].Value | Should be $ws.Cells[12,4].Value + $ws.Cells[12,3].Value | Should be $ws.Cells[12,5].Value } - it "Set the background for the added / deleted /changed rows " { - $ws.cells["A3:E3"].style.Fill.BackgroundColor.Rgb | should beNullOrEmpty - $ws.cells["A4:E4"].style.Fill.BackgroundColor.Rgb | should be "FFFFA500" - $ws.cells["A5" ].style.Fill.BackgroundColor.Rgb | should be "FF98FB98" - $ws.cells["B5:C5"].style.Fill.BackgroundColor.rgb | should beNullOrEmpty - $ws.cells["D5:E5"].style.Fill.BackgroundColor.Rgb | should be "FF98FB98" - $ws.cells["A7:C7"].style.Fill.BackgroundColor.Rgb | should be "FFFFB6C1" - $ws.cells["D7:E7"].style.Fill.BackgroundColor.rgb | should beNullOrEmpty + it "Highlighted the keys in the added / deleted / changed rows " { + $ws.cells[4,1].Style.font.color.rgb | Should be "FF8b0000" + $ws.cells[5,1].Style.font.color.rgb | Should be "FF8b0000" + $ws.cells[7,1].Style.font.color.rgb | Should be "FF8b0000" + } + it "Set the background for the added / deleted /changed rows " { + $ws.cells["A3:E3"].style.Fill.BackgroundColor.Rgb | Should beNullOrEmpty + $ws.cells["A4:E4"].style.Fill.BackgroundColor.Rgb | Should be "FFFFA500" + $ws.cells["A5" ].style.Fill.BackgroundColor.Rgb | Should be "FF98FB98" + $ws.cells["B5:C5"].style.Fill.BackgroundColor.rgb | Should beNullOrEmpty + $ws.cells["D5:E5"].style.Fill.BackgroundColor.Rgb | Should be "FF98FB98" + $ws.cells["A7:C7"].style.Fill.BackgroundColor.Rgb | Should be "FFFFB6C1" + $ws.cells["D7:E7"].style.Fill.BackgroundColor.rgb | Should beNullOrEmpty } } Context "Wider data set" { it "Copes with more columns beyond Z in the Output sheet " { - { Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined2.xlsx" } | should not throw + { Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined2.xlsx" } | Should not throw + } + } +} +Describe "Merge Multiple sheets" { + Context "Merge 3 sheets with 3 properties" { + BeforeAll { + Remove-Item -Path "$env:temp\server*.xlsx" , "$env:temp\Combined*.xlsx" -ErrorAction SilentlyContinue + [System.Collections.ArrayList]$s = get-service | Select-Object -Property Name,DisplayName,StartType + $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) + + $s.RemoveAt(5) + + $s | Export-Excel -Path $env:temp\server2.xlsx + + $s[2].displayname = $row4Displayname + + $d = $s[-1] | Select-Object -Property * + $d.DisplayName = "Second Service" + $d.Name = "Service2" + $s.Insert(6,$d) + $s.RemoveAt(8) + + $s | Export-Excel -Path $env:temp\server3.xlsx + + Merge-MultipleSheets -Path "$env:temp\server1.xlsx", "$env:temp\Server2.xlsx","$env:temp\Server3.xlsx" -OutputFile "$env:temp\combined3.xlsx" -Property name,displayname,startType -Key name + $excel = Open-ExcelPackage -Path "$env:temp\combined3.xlsx" + $ws = $excel.Workbook.Worksheets["sheet1"] + + } + it "Created a worksheet with the correct headings " { + $ws | Should not beNullOrEmpty + $ws.Cells[ 1,2 ].Value | Should be "name" + $ws.Cells[ 1,3 ].Value | Should be "Server1 DisplayName" + $ws.Cells[ 1,4 ].Value | Should be "Server1 StartType" + $ws.Cells[ 1,5 ].Value | Should be "Server2 DisplayName" + $ws.Cells[ 1,6 ].Value | Should be "Server2 StartType" + $ws.Column(7).hidden | Should be $true + $ws.Cells[ 1,8].Value | Should be "Server2 Row" + $ws.Cells[ 1,9 ].Value | Should be "Server3 DisplayName" + $ws.Cells[ 1,10].Value | Should be "Server3 StartType" + $ws.Column(11).hidden | Should be $true + $ws.Cells[ 1,12].Value | Should be "Server3 Row" + } + it "Joined the three sheets correctly " { + $ws.Cells[ 2,3 ].Value | Should be $ws.Cells[ 2,5 ].Value + $ws.Cells[ 2,4 ].Value | Should be $ws.Cells[ 2,6 ].Value + $ws.Cells[ 2,5 ].Value | Should be $ws.Cells[ 2,9 ].Value + $ws.Cells[ 2,6 ].Value | Should be $ws.Cells[ 2,10].Value + $ws.cells[ 4,5 ].value | Should be "Changed from the orginal" + $ws.cells[ 4,9 ].value | Should be $ws.Cells[ 4,3 ].Value + $ws.cells[ 5,2 ].value | Should be "Dummy" + $ws.cells[ 5,3 ].value | Should beNullOrEmpty + $ws.cells[ 5,4 ].value | Should beNullOrEmpty + $ws.cells[ 5,5 ].value | Should be "Dummy Service" + $ws.cells[ 5,8 ].value | Should be ($ws.cells[ 4,1].value +1) + $ws.cells[ 5,9 ].value | Should be $ws.cells[ 5,5 ].value + $ws.cells[ 7,5 ].value | Should beNullOrEmpty + $ws.cells[ 7,6 ].value | Should beNullOrEmpty + $ws.cells[ 7,9 ].value | Should beNullOrEmpty + $ws.cells[ 7,10].value | Should beNullOrEmpty + $ws.cells[ 8,3 ].value | Should beNullOrEmpty + $ws.cells[ 8,4 ].value | Should beNullOrEmpty + $ws.cells[ 8,5 ].value | Should beNullOrEmpty + $ws.cells[ 8,6 ].value | Should beNullOrEmpty + $ws.cells[11,9 ].value | Should beNullOrEmpty + $ws.cells[11,10].value | Should beNullOrEmpty + $ws.Cells[12,3 ].Value | Should be $ws.Cells[12,5].Value + $ws.Cells[12,4 ].Value | Should be $ws.Cells[12,6].Value + $ws.Cells[12,9 ].Value | Should be $ws.Cells[12,5].Value + $ws.Cells[12,10].Value | Should be $ws.Cells[12,6].Value + } + it "Creared Conditional formatting rules " { + $cf=$ws.ConditionalFormatting + $cf.Count | Should be 15 + $cf[14].Address.Address | Should be 'B2:B1048576' + $cf[14].Type | Should be 'Expression' + $cf[14].Formula | Should be 'OR(G2<>"Same",K2<>"Same")' + $cf[14].Style.Font.Color.Color.Name | Should be "FFFF0000" + $cf[13].Address.Address | Should be 'D2:D1048576' + $cf[13].Type | Should be 'Expression' + $cf[13].Formula | Should be 'OR(G2="Added",K2="Added")' + $cf[13].Style.Fill.BackgroundColor.Color.Name | Should be 'ffffb6c1' + $cf[ 0].Address.Address | Should be 'F1:F1048576' + $cf[ 0].Type | Should be 'Expression' + $cf[ 0].Formula | Should be 'G1="Added"' + $cf[ 0].Style.Fill.BackgroundColor.Color.Name | Should be 'ffffa500' } } } \ No newline at end of file diff --git a/compare-worksheet.ps1 b/compare-worksheet.ps1 index 0eaf1f4..95fa36e 100644 --- a/compare-worksheet.ps1 +++ b/compare-worksheet.ps1 @@ -1,53 +1,53 @@ Function Compare-WorkSheet { <# - .Synopsis - Compares two worksheets with the same name in different files. + .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. + 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 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 + 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 + 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 + 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 + 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. + 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 + 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. + 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 + 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 + #First file to compare [parameter(Mandatory=$true,Position=0)] $Referencefile , #Second file to compare @@ -57,106 +57,106 @@ Function Compare-WorkSheet { $WorkSheetName = "Sheet1", #Properties to include in the DIFF - supports wildcards, default is "*" $Property = "*" , - #Properties to exclude from the the search - supports wildcards + #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, + [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, + [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. + [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 + #If specified, highlights the DIFF rows [System.Drawing.Color]$BackgroundColor, - #If specified identifies the tabs which contain DIFF rows (ignored if -backgroundColor is omitted) + #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" + #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. + #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) + #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 + #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 + #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 + #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. + + #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. + 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" + 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]} + 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 } + 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 + $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. + 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}) } + + #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 + $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 + #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 ++) + 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 "_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 + 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 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 + #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} + 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[$_] + $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 + 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 + Set-Format -WorkSheet $ws -Range $range -BackgroundColor $BackgroundColor } if ($TabColor) { foreach ($tab in ($file.group._sheet | Select-Object -Unique)) { @@ -166,12 +166,12 @@ Function Compare-WorkSheet { $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 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} + $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} + if ($oneFile ) {$xl2 = $xl1} else {$xl2 = Open-ExcelPackage -path $Differencefile } foreach ($u in $updates) { foreach ($p in $propList) { @@ -186,75 +186,75 @@ 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 $ws2 -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." } + 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 ($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 ($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 + $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 + 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 } + #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]@{} + $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["") {$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 + #if result is "in reference" and we don't have a matching "in difference" (meaning a change) the lookup will be blank. Which we want. + $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" } } + $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" } + $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 (=>)" + 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 ($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 } } From 50c02cd8f1a49290f629e7ad1bbb170c4c38a7d0 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 15 Jul 2018 11:19:53 +0100 Subject: [PATCH 18/21] Changes to match latest style rules (trailing spaces & position of null) --- Export-Excel.ps1 | 114 +++++++++++++++++++++++------------------------ Set-Row.ps1 | 2 +- SetFormat.ps1 | 18 ++++---- ToDo.md | 2 + 4 files changed, 69 insertions(+), 67 deletions(-) diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 409ced8..ded2d94 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -37,7 +37,7 @@ .PARAMETER IncludePivotTable Adds a Pivot table using the data in the worksheet. .PARAMETER PivotTableName - If a Pivot table is created from command line parameters, specificies the name of the new sheet holding the pivot. If Omitted this will be "WorksheetName-PivotTable" + If a Pivot table is created from command line parameters, specificies the name of the new sheet holding the pivot. If Omitted this will be "WorksheetName-PivotTable" .PARAMETER PivotRows Name(s) columns from the spreadhseet which will provide the Row name(s) in a pivot table created from command line parameters. .PARAMETER PivotColumns @@ -50,19 +50,19 @@ In a pivot table created from command line parameters, prevents the addition of totals to rows and columns. .PARAMETER PivotTableDefinition Instead of describing a single pivot table with mutliple commandline paramters; you can use a HashTable in the form PivotTableName = Definition; - Definition is itself a hashtable with Sheet PivotTows, PivotColumns, PivotData, IncludePivotChart and ChartType values. + Definition is itself a hashtable with Sheet PivotTows, PivotColumns, PivotData, IncludePivotChart and ChartType values. .PARAMETER IncludePivotChart Include a chart with the Pivot table - implies -IncludePivotTable. .PARAMETER ChartType - The type for Pivot chart (one of Excel's defined chart types) + The type for Pivot chart (one of Excel's defined chart types) .PARAMETER NoLegend Exclude the legend from the pivot chart. .PARAMETER ShowCategory Add category labels to the pivot chart. .PARAMETER ShowPercent Add Percentage labels to the pivot chart. - .PARAMETER ConditionalFormat - One or more conditional formatting rules defined with New-ConditionalFormattingIconSet. + .PARAMETER ConditionalFormat + One or more conditional formatting rules defined with New-ConditionalFormattingIconSet. .PARAMETER ConditionalText Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered. .PARAMETER NoNumberConversion @@ -82,9 +82,9 @@ .PARAMETER ColumnChart Creates a "quick" column chart using the first text column as labels and the first numeric column as values .PARAMETER LineChart - Creates a "quick" line chart using the first text column as labels and the first numeric column as values + Creates a "quick" line chart using the first text column as labels and the first numeric column as values .PARAMETER PieChart - Creates a "quick" pie chart using the first text column as labels and the first numeric column as values + Creates a "quick" pie chart using the first text column as labels and the first numeric column as values .PARAMETER ExcelChartDefinition A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. .PARAMETER HideSheet @@ -152,11 +152,11 @@ '[Blue]$#,##0.00;[Red]-$#,##0.00' .PARAMETER ReZip - If specified, Export-Excel will expand the contents of the .XLSX file (which is multiple files in a zip archive) and rebuilt it. + If specified, Export-Excel will expand the contents of the .XLSX file (which is multiple files in a zip archive) and rebuilt it. .PARAMETER Show Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. .PARAMETER ReturnRange - If specified, Export-Excel returns the range of added cells in the format "A1:Z100" + If specified, Export-Excel returns the range of added cells in the format "A1:Z100" .PARAMETER PassThru If specified, Export-Excel returns an object representing the Excel package without saving the package first. To save it you need to call the save or Saveas method or send it back to Export-Excel. @@ -422,7 +422,7 @@ [Switch]$Barchart, [Switch]$PieChart, [Switch]$LineChart , - [Switch]$ColumnChart , + [Switch]$ColumnChart , [Object[]]$ExcelChartDefinition, [String[]]$HideSheet, [Switch]$MoveToStart, @@ -616,7 +616,7 @@ if ($isDataTypeValueType) { $ColumnIndex = $StartColumn - $Row += 1 + $Row += 1 Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData } else { @@ -654,7 +654,7 @@ $ColumnIndex += 1 #endregion } - $ColumnIndex -= 1 # column index will be the last column whether isDataTypeValueType was true or false + $ColumnIndex -= 1 # column index will be the last column whether isDataTypeValueType was true or false } } Catch { @@ -670,11 +670,11 @@ $endAddress = $ws.Dimension.End.Address } else { - $LastRow = $Row + $LastRow = $Row $LastCol = $ColumnIndex $endAddress = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$LastRow]C[$LastCol]", 0, 0) } - $startAddress = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$StartRow]C[$StartColumn]", 0, 0) + $startAddress = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$StartRow]C[$StartColumn]", 0, 0) $dataRange = "{0}:{1}" -f $startAddress, $endAddress Write-Debug "Data Range '$dataRange'" @@ -711,7 +711,7 @@ } Catch {Write-Warning -Message "Failed adding named ranges to worksheet '$WorkSheetname': $_" } } - + if ($RangeName) { try { if ($RangeName -match "\W") { @@ -817,7 +817,7 @@ catch {Write-Warning -Message "Failed adding Freezing the panes in worksheet '$WorkSheetname': $_"} if ($BoldTopRow) { #it sets bold as far as there are populated cells: for whole row could do $ws.row($x).style.font.bold = $true - try { + try { if ($Title) { $range = $ws.Dimension.Address -replace '\d+', ($StartRow + 1) } @@ -847,33 +847,33 @@ foreach ($chartDef in $ExcelChartDefinition) { $params = @{} - $chartDef.PSObject.Properties | ForEach-Object {if ($_.value -ne $null) {$params[$_.name] = $_.value}} + $chartDef.PSObject.Properties | ForEach-Object {if ( $null -ne $_.value) {$params[$_.name] = $_.value}} Add-ExcelChart -Worksheet $ws @params } if ($Barchart -or $PieChart -or $LineChart -or $ColumnChart) { - if ($NoHeader) {$FirstDataRow = $startRow} + if ($NoHeader) {$FirstDataRow = $startRow} else {$FirstDataRow = $startRow + 1 } - $range = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$FirstDataRow]C[$startColumn]:R[$FirstDataRow]C[$lastCol]",0,0) - $xCol = $ws.cells[$range] | Where-Object {$_.value -is [string] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 - $yCol = $ws.cells[$range] | Where-Object {$_.value -is [valueType] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 + $range = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$FirstDataRow]C[$startColumn]:R[$FirstDataRow]C[$lastCol]",0,0) + $xCol = $ws.cells[$range] | Where-Object {$_.value -is [string] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 + $yCol = $ws.cells[$range] | Where-Object {$_.value -is [valueType] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 $params = @{ xrange = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$FirstDataRow]C[$xcol]:R[$($lastrow)]C[$xcol]",0,0) ; - yrange = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$FirstDataRow]C[$ycol]:R[$($lastrow)]C[$ycol]",0,0) ; + yrange = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$FirstDataRow]C[$ycol]:R[$($lastrow)]C[$ycol]",0,0) ; title = ""; - Column = ($lastCol +1) ; - Width = 1200 + Column = ($lastCol +1) ; + Width = 1200 } - if ($NoHeader) {$params["NoHeader"] = $true} + if ($NoHeader) {$params["NoHeader"] = $true} else {$Params["SeriesHeader"] = $ws.Cells[$startRow, $YCol].Value} - if ($ColumnChart) {$Params["chartType"] = "ColumnStacked" } - elseif ($Barchart) {$Params["chartType"] = "BarStacked" } - elseif ($PieChart) {$Params["chartType"] = "PieExploded3D" } - elseif ($LineChart) {$Params["chartType"] = "Line" } - - Add-ExcelChart -Worksheet $ws @params + if ($ColumnChart) {$Params["chartType"] = "ColumnStacked" } + elseif ($Barchart) {$Params["chartType"] = "BarStacked" } + elseif ($PieChart) {$Params["chartType"] = "PieExploded3D" } + elseif ($LineChart) {$Params["chartType"] = "Line" } - } + Add-ExcelChart -Worksheet $ws @params + + } foreach ($ct in $ConditionalText) { try { @@ -1017,7 +1017,7 @@ function Add-WorkSheet { Adds a workshet to an existing workbook. .Description If the named worksheet already exists, the -clearsheet parameter decides whether it should be deleted and a new one returned, - or if not specified the existing sheet will be returned. + or if not specified the existing sheet will be returned. #> [cmdletBinding()] [OutputType([OfficeOpenXml.ExcelWorksheet])] @@ -1098,10 +1098,10 @@ function Add-WorkSheet { function Add-PivotTable { <# .Synopsis - Adds a Pivot table (and optional pivot chart) to a workbook + Adds a Pivot table (and optional pivot chart) to a workbook .Description - If the pivot table already exists, the source data will be updated. -#> + If the pivot table already exists, the source data will be updated. +#> param ( #Name for the new Pivot table - this will be the name of a sheet in the workbook [Parameter(Mandatory = $true)] @@ -1235,8 +1235,8 @@ function Add-ExcelChart { #An object representing the worksheet where the chart should be added. [OfficeOpenXml.ExcelWorksheet]$Worksheet, [String]$Title = "Chart Title", - #$Header, Not used but referenced previously - #The Type of chart (Area, Line, Pie etc) + #$Header, Not used but referenced previously + #The Type of chart (Area, Line, Pie etc) [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = "ColumnStacked", $XRange, $YRange, @@ -1255,21 +1255,21 @@ function Add-ExcelChart { $SeriesHeader, [Switch]$TitleBold, [Int]$TitleSize , - [String]$XAxisTitleText, + [String]$XAxisTitleText, [Switch]$XAxisTitleBold, $XAxisTitleSize , [string]$XAxisNumberformat, - $XMajorUnit, - $XMinorUnit, + $XMajorUnit, + $XMinorUnit, $XMaxValue, $XMinValue, [OfficeOpenXml.Drawing.Chart.eAxisPosition]$XAxisPosition , - [String]$YAxisTitleText, + [String]$YAxisTitleText, [Switch]$YAxisTitleBold, $YAxisTitleSize, [string]$YAxisNumberformat, - $YMajorUnit, - $YMinorUnit, + $YMajorUnit, + $YMinorUnit, $YMaxValue, $YMinValue, [OfficeOpenXml.Drawing.Chart.eAxisPosition]$YAxisPosition ) @@ -1280,7 +1280,7 @@ function Add-ExcelChart { $chart.Title.Text = $Title if ($TitleBold) {$chart.Title.Font.Bold = $true} if ($TitleSize) {$chart.Title.Font.Size = $TitleSize} - } + } if ($NoLegend) { $chart.Legend.Remove() } else { if ($LegendPostion) {$Chart.Legend.Position = $LegendPostion} @@ -1293,11 +1293,11 @@ function Add-ExcelChart { 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 -ne $null) {$chart.XAxis.MinValue = $XMinValue} - if ($XMaxValue -ne $null) {$chart.XAxis.MaxValue = $XMaxValue} + if ($XAxisPosition) {$chart.XAxis.AxisPosition = $XAxisPosition} + if ($XMajorUnit) {$chart.XAxis.MajorUnit = $XMajorUnit} + if ($XMinorUnit) {$chart.XAxis.MinorUnit = $XMinorUnit} + if ($null -ne $XMinValue) {$chart.XAxis.MinValue = $XMinValue} + if ($null -ne $XMaxValue) {$chart.XAxis.MaxValue = $XMaxValue} if ($XAxisNumberformat) {$chart.XAxis.Format = $XAxisNumberformat} if ($YAxisTitleText) { @@ -1306,14 +1306,14 @@ function Add-ExcelChart { 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-ne $null) {$chart.YAxis.MinValue = $YMinValue} - if ($YMaxValue-ne $null) {$chart.YAxis.MaxValue = $YMaxValue} + if ($YMajorUnit) {$chart.YAxis.MajorUnit = $YMajorUnit} + if ($YMinorUnit) {$chart.YAxis.MinorUnit = $YMinorUnit} + if ($null -ne $YMinValue){$chart.YAxis.MinValue = $YMinValue} + if ($null -ne $YMaxValue){$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 + if ($null -ne $chart.Datalabel) { + $chart.Datalabel.ShowCategory = [boolean]$ShowCategory + $chart.Datalabel.ShowPercent = [boolean]$ShowPercent } $chart.SetPosition($Row, $RowOffsetPixels, $Column, $ColumnOffsetPixels) diff --git a/Set-Row.ps1 b/Set-Row.ps1 index d378763..a05cc26 100644 --- a/Set-Row.ps1 +++ b/Set-Row.ps1 @@ -129,7 +129,7 @@ if ($VerticalAlignment) { $worksheet.row( $Row ).Style.VerticalAlignment = $VerticalAlignment } if ($Height) { $worksheet.row( $Row ).Height = $Height } if ($FontColor) { $worksheet.row( $Row ).Style.Font.Color.SetColor( $FontColor ) } - if ($BorderAround) { $worksheet.row( $Row ).Style.Border.BorderAround( $BorderAround ) } + if ($BorderAround) { $worksheet.row( $Row ).Style.Border.BorderAround( $BorderAround ) } if ($BackgroundColor) { $worksheet.row( $Row ).Style.Fill.PatternType = $BackgroundPattern $worksheet.row( $Row ).Style.Fill.BackgroundColor.SetColor($BackgroundColor ) diff --git a/SetFormat.ps1 b/SetFormat.ps1 index 12b6962..7d83579 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]) { @@ -113,29 +113,29 @@ if ($HorizontalAlignment) {$Address.Style.HorizontalAlignment = $HorizontalAlignment } if ($VerticalAlignment) {$Address.Style.VerticalAlignment = $VerticalAlignment } if ($Value) {$Address.Value = $Value } - if ($Formula) {$Address.Formula = $Formula } - if ($BorderAround) {$Address.Style.Border.BorderAround($BorderAround, $BorderColor)} + if ($Formula) {$Address.Formula = $Formula } + if ($BorderAround) {$Address.Style.Border.BorderAround($BorderAround, $BorderColor)} - if ($BorderBottom) { + if ($BorderBottom) { $Address.Style.Border.Bottom.Style=$BorderBottom $Address.Style.Border.Bottom.Color.SetColor($BorderColor) } - if ($BorderTop) { + if ($BorderTop) { $Address.Style.Border.Top.Style=$BorderTop $Address.Style.Border.Top.Color.SetColor($BorderColor) } - if ($BorderLeft) { + if ($BorderLeft) { $Address.Style.Border.Left.Style=$BorderLeft $Address.Style.Border.Left.Color.SetColor($BorderColor) } - if ($BorderRight) { + if ($BorderRight) { $Address.Style.Border.Right.Style=$BorderRight $Address.Style.Border.Right.Color.SetColor($BorderColor) } - + if ($BackgroundColor) { $Address.Style.Fill.PatternType = $BackgroundPattern $Address.Style.Fill.BackgroundColor.SetColor($BackgroundColor) @@ -155,7 +155,7 @@ if ($Autosize) { if ($Address -is [OfficeOpenXml.ExcelColumn]) {$Address.AutoFit() } elseif ($Address -is [OfficeOpenXml.ExcelRange] ) { - $Address.AutoFitColumns() + $Address.AutoFitColumns() } else {Write-Warning -Message ("Can autofit a column or a range but not a {0} object" -f ($Address.GetType().name)) } diff --git a/ToDo.md b/ToDo.md index d2a73f0..d3f8bff 100644 --- a/ToDo.md +++ b/ToDo.md @@ -7,3 +7,5 @@ [ ] Copy parameter help from function Add-ExcelChart into New-ExcelChart.ps1 - [ ] Examples and tests for new "Quick charts" in Export Excel - [ ] Charting.ps1,GetXYRange.ps1, InferData.PS1 move to deprecated. (replace examples) +- [ ] Refactor Set-Row and Set-Column to use set-format and add conditional format support. +- [ ] Examples and tests for set-Row and Set-column; review test coverage and examples for Set-Format adn Add-Conditional formatting \ No newline at end of file From d08fe73a79550990660bc2a94aa587e99ce4c6b3 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Tue, 17 Jul 2018 09:46:47 +0100 Subject: [PATCH 19/21] Fixed bug with MoveAfter, param improvement on add-worksheet, tests++ --- Export-Excel.ps1 | 38 ++++++++++++++++++++------------ README.md | 3 ++- ToDo.md | 2 +- __tests__/Export-Excel.Tests.ps1 | 6 ++--- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index ded2d94..cbafa5f 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -545,9 +545,9 @@ } Else { $pkg = Open-ExcelPackage -Path $Path -Create -KillExcel:$KillExcel} - $params = @{} + $params = @{WorkSheetname=$worksheetName} if ($NoClobber) {Write-Warning -Message "-NoClobber parameter is no longer used" } - foreach ($p in @("WorkSheetname", "ClearSheet", "MoveToStart", "MoveToEnd", "MoveBefore", "MoveAfter")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}} + foreach ($p in @("ClearSheet", "MoveToStart", "MoveToEnd", "MoveBefore", "MoveAfter")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}} $ws = $pkg | Add-WorkSheet @params foreach ($format in $ConditionalFormat ) { @@ -1029,7 +1029,7 @@ function Add-WorkSheet { [Parameter(Mandatory = $true, ParameterSetName = "WorkBook")] [OfficeOpenXml.ExcelWorkbook]$ExcelWorkbook, #The name of the worksheet 'Sheet1' by default. - [string]$WorkSheetname = 'Sheet1', + [string]$WorkSheetname , #If the worksheet already exists, by default it will returned, unless -ClearSheet is specified in which case it will be deleted and re-created. [switch]$ClearSheet, #If specified, the worksheet will be moved to the start of the workbook. @@ -1049,21 +1049,30 @@ function Add-WorkSheet { #Ignored but retained for backwards compatibility. [Switch] $NoClobber ) + #if we were given a workbook use it, if we were given a package, use its workbook + if ($ExcelPackage -and -not $ExcelWorkbook) {$ExcelWorkbook = $ExcelPackage.Workbook} - if ($ExcelPackage -and -not $ExcelWorkbook) {$ExcelWorkbook = $ExcelPackage.Workbook} + # If worksheetName was given, try to use that worksheet. If it wasn't, and we are copying an existing sheet, try to use the sheet name + # if we are not copying a sheet or the name is in use, use the name "SheetX" where X is the number of the new sheet + if (-not $WorkSheetname -and $CopySource -and -not $ExcelWorkbook[$CopySource.Name]) {$WorkSheetname = $CopySource.Name} + elseif (-not $WorkSheetname) {$WorkSheetname = "Sheet" + (1 + $ExcelWorkbook.Worksheets.Count)} + else {$ws = $ExcelWorkbook.Worksheets[$WorkSheetname]} - $ws = $ExcelWorkbook.Worksheets[$WorkSheetname] - if ( $ws -and $ClearSheet) { $ExcelWorkbook.Worksheets.Delete($WorkSheetname) ; $ws = $null } - if (!$ws -and $CopySource) { - Write-Verbose -Message "Copying into worksheet '$WorkSheetname'." - $ws = $ExcelWorkbook.Worksheets.Add($WorkSheetname, $CopySource) + #If -clearsheet was specified and the named sheet exists, delete it + if ($ws -and $ClearSheet) { $ExcelWorkbook.Worksheets.Delete($WorkSheetname) ; $ws = $null } + + #copy or create new sheet as needed + if (-not $ws -and $CopySource) { + Write-Verbose -Message "Copying into worksheet '$WorkSheetname'." + $ws = $ExcelWorkbook.Worksheets.Add($WorkSheetname, $CopySource) } - elseif (!$ws) { - Write-Verbose -Message "Adding worksheet '$WorkSheetname'." - $ws = $ExcelWorkbook.Worksheets.Add($WorkSheetname) + elseif (-not $ws) { + $ws = $ExcelWorkbook.Worksheets.Add($WorkSheetname) + Write-Verbose -Message "Adding worksheet '$WorkSheetname'." } else {Write-Verbose -Message "Worksheet '$WorkSheetname' already existed."} - if ($MoveToStart) {$ExcelWorkbook.Worksheets.MoveToStart($worksheetName) } + #region Move sheet if needed + if ($MoveToStart) {$ExcelWorkbook.Worksheets.MoveToStart($worksheetName) } elseif ($MoveToEnd ) {$ExcelWorkbook.Worksheets.MoveToEnd($worksheetName) } elseif ($MoveBefore ) { if ($ExcelWorkbook.Worksheets[$MoveBefore]) { @@ -1075,7 +1084,7 @@ function Add-WorkSheet { else {Write-Warning "Can't find worksheet '$MoveBefore'; worsheet '$WorkSheetname' will not be moved."} } elseif ($MoveAfter ) { - if ($MoveAfter = "*") { + if ($MoveAfter -eq "*") { if ($WorkSheetname -lt $ExcelWorkbook.Worksheets[1].Name) {$ExcelWorkbook.Worksheets.MoveToStart($worksheetName)} else { $i = 1 @@ -1093,6 +1102,7 @@ function Add-WorkSheet { } else {Write-Warning "Can't find worksheet '$MoveAfter'; worsheet '$WorkSheetname' will not be moved."} } + #endregion return $ws } function Add-PivotTable { diff --git a/README.md b/README.md index 3c51951..ecca703 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ 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 14th July 18 +# What's new to 17th July 18 - Moved chart creation 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 that -Header does nothing, so it isn't Add-Excel chart and there is a message that does nothing in New-ExcelChartDefinition . - Added -BarChart -ColumnChart -LineChart -PieChart parameters to Export-Excel for quick charts without giving a full chart definition. - Added parameters for managing chart Axes and legend @@ -47,6 +47,7 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi - Added more of the Parameters from Export-Excel to Send-SQLDataToExcel, send just calls export-excel with these parameters... - Added support for passing a System.Data.DataTable directly to Send-SQLDataToExcel - Fixed a bug in Merge-MultipleSheets where if the key was "name", columns like "displayName" would not be processed correctly, nor would names like "something_ROW". Added tests for Compare, Merge and Join Worksheet +- Add-Worksheet , fixed a regression with move-after, changed way default worksheet name is decided, so if none is specified, and an existing worksheet is copied (see June additions) and the name doesn't already exist, the orginal sheet name will be kept. If no name is given an a blank sheet is created, then it will be named sheetX where X is the number of the sheet (so if you have sheets FOO and BAR the new sheet will be Sheet3). # New in June 18 - New commands - Diff , Merge and Join diff --git a/ToDo.md b/ToDo.md index d3f8bff..0ff3da1 100644 --- a/ToDo.md +++ b/ToDo.md @@ -8,4 +8,4 @@ - [ ] Examples and tests for new "Quick charts" in Export Excel - [ ] Charting.ps1,GetXYRange.ps1, InferData.PS1 move to deprecated. (replace examples) - [ ] Refactor Set-Row and Set-Column to use set-format and add conditional format support. -- [ ] Examples and tests for set-Row and Set-column; review test coverage and examples for Set-Format adn Add-Conditional formatting \ No newline at end of file +- [ ] Examples and tests for set-Row and Set-column; review test coverage and examples for Set-Format and Add-Conditional formatting \ No newline at end of file diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 index a53a6af..7cfab04 100644 --- a/__tests__/Export-Excel.Tests.ps1 +++ b/__tests__/Export-Excel.Tests.ps1 @@ -63,10 +63,10 @@ Describe ExportExcel { $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 + Remove-item -Path $path -ErrorAction SilentlyContinue $processes = Get-Process $propertyNames = $Processes[0].psobject.properties.where( {$_.MemberType -eq 'Property'}).name $rowcount = $Processes.Count @@ -454,7 +454,7 @@ Describe ExportExcel { #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 "Sheet1" -MoveAfter "Processes" # 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 From c9ad5079660414bccb11f77c1f6e92e517d2fe18 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Wed, 18 Jul 2018 16:32:10 +0100 Subject: [PATCH 20/21] All new Copy-ExcelWorksheet (+tests); fix sort in merge-Multiple --- Copy-ExcelWorkSheet.ps1 | 108 +++++++++++++++++++++--- Merge-worksheet.ps1 | 3 +- __tests__/Copy-ExcelWorksheet.Tests.ps1 | 86 +++++++++++++++++++ __tests__/Export-Excel.Tests.ps1 | 2 +- 4 files changed, 187 insertions(+), 12 deletions(-) create mode 100644 __tests__/Copy-ExcelWorksheet.Tests.ps1 diff --git a/Copy-ExcelWorkSheet.ps1 b/Copy-ExcelWorkSheet.ps1 index 87503e4..26b5f75 100644 --- a/Copy-ExcelWorkSheet.ps1 +++ b/Copy-ExcelWorkSheet.ps1 @@ -1,22 +1,110 @@ function Copy-ExcelWorkSheet { [CmdletBinding()] + <# + .SYNOPSIS + Copies a worksheet between workbooks or within the same workbook. + .DESCRIPTION + Copy-ExcelWorkSheet takes Source and Destination workbook parameters; each can be the path to an XLSx file, an ExcelPackage object or an ExcelWorkbook object. + The Source worksheet is specified by name or number (starting from 1), and the destination worksheet can be explicitly named, + or will follow the name of the source if no name is specified. + .EXAMPLE + C:\> Copy-ExcelWorkSheet -SourceWorkbook Test1.xlsx -DestinationWorkbook Test2.xlsx + This is the simplest version of the command: no source worksheet is specified so Copy-ExcelWorksheet uses the first sheet in the workbook + No Destination sheet is specified so the new worksheet will be the same as the one which is being copied. + .EXAMPLE + C:\> Copy-ExcelWorkSheet -SourceWorkbook Server1.xlsx -sourceWorksheet "Settings" -DestinationWorkbook Settings.xlsx -DestinationWorkSheet "Server1" + Here the Settings page from Server1's workbook is copied to the 'Server1" page of a "Settings" workbook. + .EXAMPLE + C:\> $excel = Open-ExcelPackage .\test.xlsx + C:\> Copy-ExcelWorkSheet -SourceWorkbook $excel -SourceWorkSheet "first" -DestinationWorkbook $excel -Show -DestinationWorkSheet Duplicate + This opens the workbook test.xlsx and copies the worksheet named "first" to a new worksheet named "Duplicate", + because -Show is specified the file is saved and opened in Excel + .EXAMPLE + C:\> $excel = Open-ExcelPackage .\test.xlsx + C:\> Copy-ExcelWorkSheet -SourceWorkbook $excel -SourceWorkSheet 1 -DestinationWorkbook $excel -DestinationWorkSheet Duplicate + C:\> Close-ExcelPackage $excel + This is almost the same as the previous example, except source sheet is specified by position rather than name and + because -Show is not specified, so other steps can be carried using the package object, at the end the file is saved by Close-ExcelPackage + + #> param( + #An ExcelWorkbook or ExcelPackage object or the path to an XLSx file where the data is found. [Parameter(Mandatory=$true)] $SourceWorkbook, - [Parameter(Mandatory=$true)] - $SourceWorkSheet, + #Name or number (starting from 1) of the worksheet in the source workbook (defaults to 1). + $SourceWorkSheet = 1 , + #An ExcelWorkbook or ExcelPackage object or the path to an XLSx file where the data should be copied. [Parameter(Mandatory=$true)] $DestinationWorkbook, + #Name of the worksheet in the destination workbook; by default the same as the source worksheet's name. If the sheet exists it will be deleted and re-copied. $DestinationWorkSheet, + #if the destination is an excel package or a path, launch excel and open the file on completion. [Switch]$Show ) - - Write-Verbose "Copying $($SourceWorkSheet) from $($SourceWorkbook) to $($DestinationWorkSheet) in $($DestinationWorkbook)" - - if(!$DestinationWorkSheet) { - $DestinationWorkSheet = $SourceWorkSheet + #Special case - give the same path for source and destination worksheet + if ($SourceWorkbook -is [System.String] -and $SourceWorkbook -eq $DestinationWorkbook) { + if (-not $DestinationWorkSheet) {Write-Warning -Message "You must specify a destination worksheet name if copying within the same workbook."; return} + else { + Write-Verbose -Message "Copying " + $excel = Open-ExcelPackage -Path $SourceWorkbook + if (-not $excel.Workbook.Worksheets[$Sourceworksheet]) { + Write-Warning -Message "Could not find Worksheet $sourceWorksheet in $sourceWorkbook" + Close-ExcelPackage -ExcelPackage $excel -NoSave + return + } + elseif ($excel.Workbook.Worksheets[$Sourceworksheet].name -eq $DestinationWorkSheet) { + Write-Warning -Message "The destination worksheet name is the same as the source. " + Close-ExcelPackage -ExcelPackage $excel -NoSave + return + } + else { + $null = Add-WorkSheet -ExcelPackage $Excel -WorkSheetname $DestinationWorkSheet -CopySource ($excel.Workbook.Worksheets[$SourceWorkSheet]) + Close-ExcelPackage -ExcelPackage $excel -Show:$Show + return + } + } + } + else { + if ($SourceWorkbook -is [OfficeOpenXml.ExcelWorkbook]) {$sourcews=$SourceWorkbook.Worksheets[$SourceWorkSheet]} + elseif ($SourceWorkbook -is [OfficeOpenXml.ExcelPackage] ) {$sourcews=$SourceWorkbook.Workbook.Worksheets[$SourceWorkSheet]} + else { + $SourceWorkbook = (Resolve-Path $SourceWorkbook).ProviderPath + try { + Write-Verbose "Opening worksheet '$Worksheetname' in Excel workbook '$SourceWorkbook'." + $Stream = New-Object -TypeName System.IO.FileStream -ArgumentList $SourceWorkbook, 'Open', 'Read' ,'ReadWrite' + $Package1 = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream + $sourceWs = $Package1.Workbook.Worksheets[$SourceWorkSheet] + } + catch {Write-Warning -Message "Could not open $SourceWorkbook" ; return} + } + if (-not $sourceWs) {Write-Warning -Message "Could not find worksheet '$Sourceworksheet' in the source workbook." ; return} + else { + try { + if ($DestinationWorkbook -is [OfficeOpenXml.ExcelWorkbook]) { + $wb = $DestinationWorkbook + } + elseif ($DestinationWorkbook -is [OfficeOpenXml.ExcelPackage] ) { + $wb = $DestinationWorkbook.workbook + if ($show) {$package2 =$DestinationWorkbook} + } + else { + $package2 = Open-ExcelPackage -Create -Path $DestinationWorkbook + $wb = $package2.Workbook + } + if (-not $DestinationWorkSheet) {$DestinationWorkSheet = $SourceWs.Name} + if ($wb.Worksheets[$DestinationWorkSheet]) { + Write-Verbose "Destination workbook already has a sheet named '$DestinationWorkSheet', deleting it." + $wb.Worksheets.Delete($DestinationWorkSheet) + } + Write-Verbose "Copying $($SourceWorkSheet) from $($SourceWorkbook) to $($DestinationWorkSheet) in $($DestinationWorkbook)" + $null = Add-WorkSheet -ExcelWorkbook $wb -WorkSheetname $DestinationWorkSheet -CopySource $sourceWs + if ($package1) {Close-ExcelPackage -ExcelPackage $Package1 -NoSave } + if ($package2) {Close-ExcelPackage -ExcelPackage $Package2 -Show:$show } + if ($show -and $DestinationWorkbook -is [OfficeOpenXml.ExcelWorkbook]) { + Write-Warning -Message "-Show only works if the Destination workbook is given as a file path or an ExcelPackage object." + } + } + catch {Write-Warning -Message "Could not write to sheet '$DestinationWorkSheet' in the destination workbook" ; return} + } } - - Import-Excel -Path $SourceWorkbook -WorkSheetname $SourceWorkSheet | - Export-Excel -Path $DestinationWorkbook -WorkSheetname $DestinationWorkSheet -Show:$Show } \ No newline at end of file diff --git a/Merge-worksheet.ps1 b/Merge-worksheet.ps1 index 3046bfb..54cf472 100644 --- a/Merge-worksheet.ps1 +++ b/Merge-worksheet.ps1 @@ -406,8 +406,9 @@ Function Merge-MultipleSheets { #if the process didn't return data then abandon now. if (-not $merged) {Write-Warning -Message "The merge operation did not return any data."; return } + $orderByProperties = $merged[0].psobject.properties.where({$_.name -match "row$"}).name Write-Progress -Activity "Merging sheets" -CurrentOperation "Creating output sheet '$OutputSheetName' in $OutputFile" - $excel = $merged | Sort-Object "_row" | Update-FirstObjectProperties | + $excel = $merged | Sort-Object -Property $orderByProperties | Update-FirstObjectProperties | Export-Excel -Path $OutputFile -WorkSheetname $OutputSheetName -ClearSheet -BoldTopRow -AutoFilter -PassThru $sheet = $excel.Workbook.Worksheets[$OutputSheetName] diff --git a/__tests__/Copy-ExcelWorksheet.Tests.ps1 b/__tests__/Copy-ExcelWorksheet.Tests.ps1 new file mode 100644 index 0000000..2a9f612 --- /dev/null +++ b/__tests__/Copy-ExcelWorksheet.Tests.ps1 @@ -0,0 +1,86 @@ +$path1 = "$env:TEMP\Test1.xlsx" +$path2 = "$env:TEMP\Test2.xlsx" +Remove-item -Path $path1, $path2 # -ErrorAction SilentlyContinue + +$ProcRange = Get-Process | Export-Excel $path1 -DisplayPropertySet -WorkSheetname Processes -ReturnRange + +if ((Get-Culture).NumberFormat.CurrencySymbol -eq "£") {$OtherCurrencySymbol = "$"} +else {$OtherCurrencySymbol = "£"} +[PSCustOmobject][Ordered]@{ + Date = Get-Date + Formula1 = '=SUM(F2:G2)' + String1 = 'My String' + Float = [math]::pi + IPAddress = '10.10.25.5' + StrLeadZero = '07670' + StrComma = '0,26' + StrEngThousand = '1,234.56' + StrEuroThousand = '1.555,83' + StrDot = '1.2' + StrNegInt = '-31' + StrTrailingNeg = '31-' + StrParens = '(123)' + strLocalCurrency = ('{0}123.45' -f (Get-Culture).NumberFormat.CurrencySymbol ) + strOtherCurrency = ('{0}123.45' -f $OtherCurrencySymbol ) + StrE164Phone = '+32 (444) 444 4444' + StrAltPhone1 = '+32 4 4444 444' + StrAltPhone2 = '+3244444444' + StrLeadSpace = ' 123' + StrTrailSpace = '123 ' + Link1 = [uri]"https://github.com/dfinke/ImportExcel" + Link2 = "https://github.com/dfinke/ImportExcel" # Links are not copied correctly, hopefully this will be fixed at some future date +} | Export-Excel -NoNumberConversion IPAddress, StrLeadZero, StrAltPhone2 -WorkSheetname MixedTypes -Path $path2 +Describe "Copy-Worksheet" { + Context "Simplest copy"{ + BeforeAll { + Copy-ExcelWorkSheet -SourceWorkbook $path1 -DestinationWorkbook $path2 + $excel = Open-ExcelPackage -Path $path2 + $ws = $excel.Workbook.Worksheets["Processes"] + } + it "inserted a worksheet " { + $Excel.Workbook.Worksheets.count | Should be 2 + $ws | Should not benullorEmpty + $ws.Dimension.Address | should be $ProcRange + } + } + Context "Mixed types using a package object"{ + BeforeAll { + Copy-ExcelWorkSheet -SourceWorkbook $excel -DestinationWorkbook $excel -DestinationWorkSheet "CopyOfMixedTypes" + Close-ExcelPackage -ExcelPackage $excel + $excel = Open-ExcelPackage -Path $path2 + $ws = $Excel.Workbook.Worksheets[3] + } + it "inserted a worksheet with the expected name, number of rows and number of columns " { + $Excel.Workbook.Worksheets.count | Should be 3 + $ws | Should not benullorEmpty + $ws.Name | Should be "CopyOfMixedTypes" + $ws.Dimension.Columns | Should be 22 + $ws.Dimension.Rows | Should be 2 + $ws.Cells[2, 1].Value.Gettype().name | Should be 'DateTime' + $ws.Cells[2, 2].Formula | Should be '=SUM(F2:G2)' + $ws.Cells[2, 5].Value.GetType().name | Should be 'String' + $ws.Cells[2, 6].Value.GetType().name | Should be 'String' + $ws.Cells[2, 18].Value.GetType().name | Should be 'String' + ($ws.Cells[2, 11].Value -is [valuetype] ) | Should be $true + ($ws.Cells[2, 12].Value -is [valuetype] ) | Should be $true + ($ws.Cells[2, 13].Value -is [valuetype] ) | Should be $true + $ws.Cells[2, 11].Value | Should beLessThan 0 + $ws.Cells[2, 12].Value | Should beLessThan 0 + $ws.Cells[2, 13].Value | Should beLessThan 0 + if ((Get-Culture).NumberFormat.NumberGroupSeparator -EQ ",") { + ($ws.Cells[2, 8].Value -is [valuetype] ) | Should be $true + $ws.Cells[2, 9].Value.GetType().name | Should be 'String' + } + elseif ((Get-Culture).NumberFormat.NumberGroupSeparator -EQ ".") { + ($ws.Cells[2, 9].Value -is [valuetype] ) | Should be $true + $ws.Cells[2, 8].Value.GetType().name | Should be 'String' + } + ($ws.Cells[2, 14].Value -is [valuetype] ) | Should be $true + $ws.Cells[2, 15].Value.GetType().name | Should be 'String' + $ws.Cells[2, 16].Value.GetType().name | Should be 'String' + $ws.Cells[2, 17].Value.GetType().name | Should be 'String' + ($ws.Cells[2, 19].Value -is [valuetype] ) | Should be $true + ($ws.Cells[2, 20].Value -is [valuetype] ) | Should be $true + } + } +} \ No newline at end of file diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 index 7cfab04..fff8719 100644 --- a/__tests__/Export-Excel.Tests.ps1 +++ b/__tests__/Export-Excel.Tests.ps1 @@ -63,7 +63,7 @@ Describe ExportExcel { $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 From 245bbbd17b2d4a9fa3eedd72ba19610c70fd910e Mon Sep 17 00:00:00 2001 From: jhoneill Date: Wed, 18 Jul 2018 19:53:02 +0100 Subject: [PATCH 21/21] Tweak to Add-Conditional formatting --- AddConditionalFormatting.ps1 | 55 +++++++++++++++++++++--------------- Export-Excel.ps1 | 21 +++++++------- README.md | 8 ++++-- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/AddConditionalFormatting.ps1 b/AddConditionalFormatting.ps1 index b362dc1..5757fe5 100644 --- a/AddConditionalFormatting.ps1 +++ b/AddConditionalFormatting.ps1 @@ -63,6 +63,14 @@ [Parameter(Mandatory = $true, ParameterSetName = "FiveIconSet")] [Parameter(Mandatory = $true, ParameterSetName = "FiveIconSetAddress")] [OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting5IconsSetType]$FiveIconsSet, + #Use the icon set in reverse order + [Parameter(ParameterSetName = "ThreeIconSet")] + [Parameter(ParameterSetName = "ThreeIconSetAddress")] + [Parameter(ParameterSetName = "FourIconSet")] + [Parameter(ParameterSetName = "FourIconSetAddress")] + [Parameter(ParameterSetName = "FiveIconSet")] + [Parameter(ParameterSetName = "FiveIconSetAddress")] + [switch]$Reverse, #A value for the condition (e.g. "2000" if the test is 'lessthan 2000') [string]$ConditionValue, #A second value for the conditions like "between x and Y" @@ -82,36 +90,39 @@ #Underline matching items [switch]$Underline, #Strikethrough text of matching items - [switch]$StrikeThru + [switch]$StrikeThru, + #If specified pass the rule back to the caller to allow additional customization. + [switch]$Passthru ) #Allow conditional formatting to work like Set-Format (with single ADDRESS parameter), split it to get worksheet and range of cells. If ($Address -and -not $WorkSheet -and -not $Range) { $WorkSheet = $Address.Worksheet[0] $Range = $Address.Address } - If ($ThreeIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)} - elseif ($FourIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFourIconSet( $Range , $FourIconsSet) } - elseif ($FiveIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFiveIconSet( $Range , $IconType) } - elseif ($DataBarColor) {$rule = $WorkSheet.ConditionalFormatting.AddDatabar( $Range , $DataBarColor) } - else { $rule = ($WorkSheet.ConditionalFormatting)."Add$RuleType"($Range)} - - if ($ConditionValue -and $RuleType -match "Top|Botom") {$rule.Rank = $ConditionValue } - if ($ConditionValue -and $RuleType -match "StdDev") {$rule.StdDev = $ConditionValue } - if ($ConditionValue -and $RuleType -match "Than|Equal|Expression") {$rule.Formula = $ConditionValue } - if ($ConditionValue -and $RuleType -match "Text|With") {$rule.Text = $ConditionValue } - if ($ConditionValue -and + If ($ThreeIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)} + elseif ($FourIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFourIconSet( $Range , $FourIconsSet) } + elseif ($FiveIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFiveIconSet( $Range , $IconType) } + elseif ($DataBarColor) {$rule = $WorkSheet.ConditionalFormatting.AddDatabar( $Range , $DataBarColor) } + else {$rule = ($WorkSheet.ConditionalFormatting)."Add$RuleType"($Range)} + if ($reverse) {$rule.reverse = $true} + if ($ConditionValue -and $RuleType -match "Top|Botom") {$rule.Rank = $ConditionValue } + if ($ConditionValue -and $RuleType -match "StdDev") {$rule.StdDev = $ConditionValue } + if ($ConditionValue -and $RuleType -match "Than|Equal|Expression") {$rule.Formula = $ConditionValue } + if ($ConditionValue -and $RuleType -match "Text|With") {$rule.Text = $ConditionValue } + if ($ConditionValue -and $ConditionValue2 -and $RuleType -match "Between") { - $rule.Formula = $ConditionValue + $rule.Formula = $ConditionValue $rule.Formula2 = $ConditionValue2 } - if ($NumberFormat) {$rule.Style.NumberFormat.Format = $NumberFormat } - if ($Underline) {$rule.Style.Font.Underline = [OfficeOpenXml.Style.ExcelUnderLineType]::Single } - if ($Bold) {$rule.Style.Font.Bold = $true} - if ($Italic) {$rule.Style.Font.Italic = $true} - if ($StrikeThru) {$rule.Style.Font.Strike = $true} - if ($ForeGroundColor) {$rule.Style.Font.Color.color = $ForeGroundColor } - if ($BackgroundColor) {$rule.Style.Fill.BackgroundColor.color = $BackgroundColor } - if ($BackgroundPattern) {$rule.Style.Fill.PatternType = $BackgroundPattern } - if ($PatternColor) {$rule.Style.Fill.PatternColor.color = $PatternColor } + if ($NumberFormat) {$rule.Style.NumberFormat.Format = $NumberFormat } + if ($Underline) {$rule.Style.Font.Underline = [OfficeOpenXml.Style.ExcelUnderLineType]::Single } + if ($Bold) {$rule.Style.Font.Bold = $true } + if ($Italic) {$rule.Style.Font.Italic = $true } + if ($StrikeThru) {$rule.Style.Font.Strike = $true } + if ($ForeGroundColor) {$rule.Style.Font.Color.color = $ForeGroundColor } + if ($BackgroundColor) {$rule.Style.Fill.BackgroundColor.color = $BackgroundColor } + if ($BackgroundPattern) {$rule.Style.Fill.PatternType = $BackgroundPattern } + if ($PatternColor) {$rule.Style.Fill.PatternColor.color = $PatternColor } + if ($Passthru) {$rule} } \ No newline at end of file diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index cbafa5f..bc99d13 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -551,12 +551,14 @@ $ws = $pkg | Add-WorkSheet @params foreach ($format in $ConditionalFormat ) { - $target = "Add$($format.Formatter)" - $rule = ($ws.ConditionalFormatting).PSObject.Methods[$target].Invoke($format.Range, $format.IconType) - $rule.Reverse = $format.Reverse + switch ($format.formatter) { + "ThreeIconSet" {Add-ConditionalFormatting -WorkSheet $ws -ThreeIconsSet $format.IconType -range $format.range -reverse:$format.reverse } + "FourIconSet" {Add-ConditionalFormatting -WorkSheet $ws -FourIconsSet $format.IconType -range $format.range -reverse:$format.reverse } + "FiveIconSet" {Add-ConditionalFormatting -WorkSheet $ws -FiveIconsSet $format.IconType -range $format.range -reverse:$format.reverse } + } } - if ($append -and $ws.Dimension) { + if ($Append -and $ws.Dimension) { #if there is a title or anything else above the header row, append needs to be combined wih a suitable startrow parameter $headerRange = $ws.Dimension.Address -replace "\d+$", $StartRow #using a slightly odd syntax otherwise header ends up as a 2D array @@ -864,22 +866,21 @@ Column = ($lastCol +1) ; Width = 1200 } - if ($NoHeader) {$params["NoHeader"] = $true} + if ($NoHeader) {$params["NoHeader"] = $true} else {$Params["SeriesHeader"] = $ws.Cells[$startRow, $YCol].Value} if ($ColumnChart) {$Params["chartType"] = "ColumnStacked" } elseif ($Barchart) {$Params["chartType"] = "BarStacked" } elseif ($PieChart) {$Params["chartType"] = "PieExploded3D" } - elseif ($LineChart) {$Params["chartType"] = "Line" } + elseif ($LineChart) {$Params["chartType"] = "Line" } Add-ExcelChart -Worksheet $ws @params - } foreach ($ct in $ConditionalText) { try { - $cfParams = @{RuleType = $ct.ConditionalType; ConditionValue = $ct.text ; - BackgroundColor = $ct.BackgroundColor; BackgroundPattern = $ct.PatternType ; - ForeGroundColor = $ct.ConditionalTextColor + $cfParams = @{RuleType = $ct.ConditionalType; ConditionValue = $ct.text ; + BackgroundColor = $ct.BackgroundColor; BackgroundPattern = $ct.PatternType ; + ForeGroundColor = $ct.ConditionalTextColor } if ($ct.Range) {$cfParams.range = $ct.range} else { $cfParams.Range = $ws.Dimension.Address } Add-ConditionalFormatting -WorkSheet $ws @cfParams diff --git a/README.md b/README.md index ecca703..62d209d 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,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 17th July 18 +# What's new to 18th July 18 - Moved chart creation 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 that -Header does nothing, so it isn't Add-Excel chart and there is a message that does nothing in New-ExcelChartDefinition . - Added -BarChart -ColumnChart -LineChart -PieChart parameters to Export-Excel for quick charts without giving a full chart definition. - Added parameters for managing chart Axes and legend - Added some chart tests to Export-Excel.tests.ps1. (but tests & examples for quick charts , axes or legends still on the to do list ) - 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 "Revese" parameter to Add-ConditionalFormatting ; and added -PassThru to make it easier to modify details of conditional formating rules after creation (#396) +- Refactored ConditionalFormatting code in Export excel to use Add-ConditionalFormatting. +- Rewrote Copy-ExcelWorksheet to use copy functionality rather than import | export (395) +- Found sorts could be inconsitent in Merge-MultipleWorksheet, so now sort on more columns. - 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) - Removed (2) calls to Get-ExcelColumnName (Removed and then restored function itself) - 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 @@ -47,7 +51,7 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi - Added more of the Parameters from Export-Excel to Send-SQLDataToExcel, send just calls export-excel with these parameters... - Added support for passing a System.Data.DataTable directly to Send-SQLDataToExcel - Fixed a bug in Merge-MultipleSheets where if the key was "name", columns like "displayName" would not be processed correctly, nor would names like "something_ROW". Added tests for Compare, Merge and Join Worksheet -- Add-Worksheet , fixed a regression with move-after, changed way default worksheet name is decided, so if none is specified, and an existing worksheet is copied (see June additions) and the name doesn't already exist, the orginal sheet name will be kept. If no name is given an a blank sheet is created, then it will be named sheetX where X is the number of the sheet (so if you have sheets FOO and BAR the new sheet will be Sheet3). +- Add-Worksheet , fixed a regression with move-after (#392), changed way default worksheet name is decided, so if none is specified, and an existing worksheet is copied (see June additions) and the name doesn't already exist, the orginal sheet name will be kept. (#393) If no name is given an a blank sheet is created, then it will be named sheetX where X is the number of the sheet (so if you have sheets FOO and BAR the new sheet will be Sheet3). # New in June 18 - New commands - Diff , Merge and Join