Compare commits

..

164 Commits

Author SHA1 Message Date
dfinke
5e7221062d Updated 2018-06-08 17:15:28 -04:00
dfinke
bf63c27d0a bumped version 2018-06-08 17:15:22 -04:00
dfinke
3ce9018d3c updated 2018-06-08 16:55:08 -04:00
Doug Finke
b026e1b23c Merge pull request #337 from jhoneill/master
Perf for import, Merge, Minor tidying up
2018-06-08 16:53:36 -04:00
dfinke
1b8be2c605 adde databar to Examples 2018-06-08 12:08:27 -04:00
dfinke
a018df09eb Fix databar example 2018-06-08 12:08:16 -04:00
dfinke
9b306458e4 increased how long the import should take 2018-06-08 11:17:22 -04:00
dfinke
ab6bc327e8 Renamed test directory 2018-06-08 11:06:07 -04:00
dfinke
45c96cd37a Added PSVersion. Point to the new directory for tests 2018-06-08 11:05:59 -04:00
jhoneill
e47e1d99c1 Added EndRow, StartColumn, EndColumn to Import-Excel
Start row already existed. 
Aliases allow you to specify -no header -top 10 -bottom 20 -left 6 -right 8 
Start row can be below end right row, and start (left) column can be to the right of the end column - this allows read in reverse order, but does generate a warning.
2018-06-08 10:43:57 +01:00
jhoneill
f2be21f955 Added Merge-MultipleSheets to argument completers 2018-05-30 00:35:26 +01:00
dfinke
711d253d1a added build badge 2018-05-29 18:39:57 -04:00
jhoneill
2a62dc9b45 Making Merge-Worksheet, and Merge-MultipleWorksheet ready to release 2018-05-29 17:36:15 +01:00
jhoneill
15f1839d29 Added Merge Multiple worksheet 2018-05-27 20:44:02 +01:00
jhoneill
e0f3171bba Revert "Added Multiple Merge to Merge-Worksheet.ps1"
This reverts commit dc9bff8240.
2018-05-27 20:37:40 +01:00
jhoneill
dc9bff8240 Added Multiple Merge to Merge-Worksheet.ps1 2018-05-27 20:30:07 +01:00
jhoneill
cd52f3c704 Added Merge-worksheet 2018-05-27 19:43:41 +01:00
dfinke
7a9fbaedb1 update 2018-05-27 13:46:14 -04:00
dfinke
4df916c9f5 Read a larger sheet 2018-05-27 13:44:29 -04:00
dfinke
36387c8f1a Up how long to import takes 2018-05-27 13:37:22 -04:00
dfinke
a9ae83b586 update .gitignore 2018-05-27 13:35:15 -04:00
dfinke
64f55c8ba4 chk # of files 2018-05-27 13:32:14 -04:00
dfinke
817ed117a8 test dir 2018-05-27 13:30:17 -04:00
dfinke
db98f8482d test exists 2018-05-27 13:29:00 -04:00
dfinke
767d75d0c5 try 2018-05-27 13:27:15 -04:00
dfinke
cd9a5c0122 try ls 2018-05-27 13:21:05 -04:00
dfinke
0266aab415 quote it 2018-05-27 13:18:34 -04:00
dfinke
8b51ac4159 update name 2018-05-27 13:16:03 -04:00
dfinke
a10d59cbec add ipmo 2018-05-27 13:14:07 -04:00
dfinke
4bf1020431 test for export-excel 2018-05-27 13:13:01 -04:00
dfinke
8906d7dbf4 chk file exists 2018-05-27 13:09:24 -04:00
dfinke
ba06c4620f moved out of BeforeAll 2018-05-27 13:04:37 -04:00
dfinke
2683ec985f save file with BOM 2018-05-27 12:56:46 -04:00
dfinke
e09f2b5563 Force install of pester 2018-05-27 12:54:44 -04:00
dfinke
8e69b66e82 Checks version of pester on appveyor 2018-05-27 12:53:26 -04:00
dfinke
b436b61a8f First stesp to wire up appveyor 2018-05-27 12:45:57 -04:00
jhoneill
39a68e71c4 Tidying of case, parameter clarity, removal of aliasess.
Added timeout to send-SqlDataToExcel
Added Merge WorkSheet
Fixed bugs in Compare-Worksheet
2018-05-26 22:24:36 +01:00
dfinke
893b2f3214 tightened up the example 2018-05-25 16:32:33 -04:00
dfinke
3a4b0267e7 tweaked 2018-05-19 13:23:00 -04:00
dfinke
2728d21ffb Generates random data 2018-05-19 13:22:15 -04:00
jhoneill
3f28fa5ebe Merge remote-tracking branch 'upstream/master' 2018-05-14 14:24:49 +01:00
jhoneill
807990c4ba Fixed an error in Compare-Worksheet when only 1 row is different 2018-05-14 14:21:49 +01:00
jhoneill
bf8e8ed6bf Added Merge-Workshet. Made perf improvements to Import-Excel 2018-05-14 11:38:23 +01:00
jhoneill
6a53d3ddc9 Minor tidying. Making case consistent, and various things analyzer friendly; added Timeout to Send-SQL..., 2018-05-13 17:35:31 +01:00
Doug Finke
9c628c49be Merge pull request #328 from stahler/patch-1
Some minor spelling changes to the awesome help.
2018-05-09 22:01:34 -04:00
Wes Stahler
152627298a Some minor spelling changes to the awesome help. 2018-05-09 20:50:56 -04:00
jhoneill
b8cc7f163f Merge remote-tracking branch 'upstream/master' 2018-05-04 14:59:09 +01:00
jhoneill
3f1ea599e3 Merge remote-tracking branch 'dfinke/master' 2018-05-04 14:55:36 +01:00
dfinke
5d2520d8e3 Updated 2018-05-03 14:07:05 -04:00
dfinke
d99dd3bbae Bump version and update install module 2018-05-03 14:02:01 -04:00
jhoneill
1e0720f57b fat fingers ! 2018-05-03 14:01:29 -04:00
jhoneill
61fc2b24b7 Color completion wasn't working and reworked gridview for compare 2018-05-03 14:01:29 -04:00
jhoneill
21108f5136 fat fingers ! 2018-05-03 17:12:20 +01:00
jhoneill
6dd928097c One last bug in compare, and fixed bug #310 in Set-Format 2018-05-03 11:30:19 -04:00
jhoneill
8f2dd982c5 Lots of changes to the compare-worksheet module 2018-05-03 11:30:19 -04:00
jhoneill
904145ebae Default parameter set was missing 2018-05-03 11:30:19 -04:00
jhoneill
47a61f5eb3 Added paramters to Compare worksheet
Now has -Gridview, and supports startrow, headernames and NoHeader (as
per import Excel) and ensures the headers don't clash.
2018-05-03 11:30:19 -04:00
jhoneill
f703addeb1 Added Compare worksheet
Added Compate-worksheet function (in its own PS1) Updated
ColorCompletion.ps1 to hold argument completers for set row, and set
column, and removed  duplicates from formatting.ps1
Fixed case of Path param in Open-ExcelPackage
Added comments about date format (m/d/yy is trapped and translated to
local date)
2018-05-03 11:30:19 -04:00
jhoneill
a022f0ae1b Color completion wasn't working and reworked gridview for compare 2018-05-03 15:46:30 +01:00
dfinke
cff83eb692 added 2018-05-02 15:39:37 -04:00
dfinke
f3197fe076 Added 2018-05-02 15:28:50 -04:00
jhoneill
a50363e55f One last bug in compare, and fixed bug #310 in Set-Format 2018-05-02 17:03:13 +01:00
dfinke
7c2943baa1 Added link to gallery 2018-05-02 10:12:06 -04:00
jhoneill
1e115d5ede Lots of changes to the compare-worksheet module 2018-05-02 12:38:13 +01:00
dfinke
ffbc6b46dc tweaked 2018-05-01 17:10:42 -04:00
dfinke
d082ca207c Changed border color 2018-05-01 17:09:52 -04:00
jhoneill
31573ee803 Default parameter set was missing 2018-04-27 15:57:12 +01:00
jhoneill
9632664c2c Added paramters to Compare worksheet
Now has -Gridview, and supports startrow, headernames and NoHeader (as
per import Excel) and ensures the headers don't clash.
2018-04-27 10:33:48 +01:00
jhoneill
da7a70752c Added Compare worksheet
Added Compate-worksheet function (in its own PS1) Updated
ColorCompletion.ps1 to hold argument completers for set row, and set
column, and removed  duplicates from formatting.ps1
Fixed case of Path param in Open-ExcelPackage
Added comments about date format (m/d/yy is trapped and translated to
local date)
2018-04-26 16:19:59 +01:00
jhoneill
f8884a64fd Merge remote-tracking branch 'refs/remotes/dfinke/master' 2018-04-23 22:13:19 +01:00
dfinke
58ab93a6eb updated readme 2018-04-22 14:25:56 -04:00
dfinke
94d86927ba Expand aliases 2018-04-22 14:19:58 -04:00
dfinke
d31a262f37 Remove commented code 2018-04-22 14:19:47 -04:00
dfinke
2e8c69ea6b added tests 2018-04-22 14:16:17 -04:00
Doug Finke
9261b49b56 Merge pull request #318 from nzubair/EmptyStringToNull
Replace empty string with NULL when generating SQL inserts. Fixes #288
2018-04-22 14:16:09 -04:00
Nasir Zubair
bc80134560 Add help and change HeaderRow parameter with StartRow including aliases, for consistency with Import-Excel function. 2018-04-20 23:31:17 -04:00
Nasir Zubair
6dfa5b1aac Add ConvertEmptyStringsToNull parameter, along with logic to replace an empty string with NULL if the parameter is present. 2018-04-20 22:59:39 -04:00
dfinke
6f921e1a3d updated 2018-04-16 15:23:12 -04:00
dfinke
feb5868952 bumped version 2018-04-16 15:23:12 -04:00
Doug Finke
f48e4ad26c Merge pull request #316 from ili101/PSPlot
Fixes `PSPlot` OutputType
2018-04-16 15:17:57 -04:00
Ili Metuky
33d86cb3c9 Fixed the bug "Unable to find type [PSPlot]"
Also added a test for it in ImportExcel.Tests.ps1

The previous fix was broke again by commit:
bd40cfe829 (diff-f50d26003c0baf98f417a44fc1cac92b)
2018-04-16 20:19:10 +03:00
ili101
4753568a39 Merge pull request #1 from dfinke/master
Update
2018-04-16 20:01:59 +03:00
Doug Finke
af31bab499 Merge pull request #313 from PashoQ/master
Updated Install.ps1 to install module for PowerShell V4
2018-04-16 12:07:42 -04:00
Pavlo Yalandin
3d76bec6c4 Updated Install.ps1 to install all needed scripts as part of module for PowerShell v4 2018-04-11 11:52:32 +03:00
dfinke
5dd73789a3 Updated 2018-04-10 19:04:55 -04:00
dfinke
85a78dad7e Updated readme 2018-04-10 19:02:56 -04:00
dfinke
13652bc4ed bumped version 2018-04-10 19:02:56 -04:00
Doug Finke
0540d221e0 Merge pull request #312 from JustinGrote/feature-ReZipParameter
Add ReZip Parameter for MSOnline compatibility (fixes #240)
2018-04-10 18:55:18 -04:00
JustinGrote
243ba0bb3c Add ReZip Parameter for MSOnline compatability 2018-04-09 14:35:28 -07:00
Doug Finke
b5177de50d updated 2018-04-07 19:34:47 -04:00
dfinke
695c986b78 Add image 2018-03-31 08:47:52 -04:00
dfinke
c6dc928e11 add new pic 2018-03-31 08:46:14 -04:00
dfinke
066ab8f348 update border format 2018-03-31 08:46:00 -04:00
dfinke
7dad54f6e9 added custom report example 2018-03-31 08:43:29 -04:00
dfinke
81fc0742f0 Added several new params 2018-03-31 08:42:43 -04:00
dfinke
f33afef2f0 updated readme 2018-03-31 08:32:48 -04:00
dfinke
97275a99de Updating readme 2018-03-31 08:26:05 -04:00
dfinke
9e01d7fc0b Added Example for using PivotFilter 2018-03-31 08:26:00 -04:00
dfinke
593c586a24 Added -PivotFilter param and code 2018-03-31 08:25:41 -04:00
dfinke
4b23d8193b bump version 2018-03-31 08:25:17 -04:00
dfinke
4408a04619 added Pivot Table Filter pic 2018-03-31 08:23:28 -04:00
dfinke
34457d05da Fixes from James merged 2018-03-14 20:11:40 -04:00
Doug Finke
d5bf0a44b4 Merge pull request #296 from jhoneill/master
SQL Server related bug fixes in Send-SQLDataToExcel.ps1
2018-03-14 20:07:29 -04:00
jhoneill
cbe5c8e347 SQL Server related bug fixes
Second connection to the same server may begin in a closed state,
previously assumed all connections begin as "open". Fixed bugs with
ChangeDatabase parameter which would prevent it working
2018-03-10 17:43:48 +00:00
dfinke
848177c358 fixed spelling #285 2018-02-10 20:14:02 -05:00
dfinke
80224da067 updated 2018-01-14 09:35:09 -05:00
dfinke
a5b9ddc257 Fix 276 and 262 2018-01-12 19:32:19 -05:00
dfinke
2f70cd88e8 Allow xlsm files to be read 2018-01-09 19:39:13 -05:00
dfinke
5969bba169 updated and answers issue #273 2018-01-02 16:37:44 -05:00
dfinke
7de56c803c bump version 2018-01-02 16:08:38 -05:00
dfinke
5e4220bd09 Supports excluding Row Grand Totals 2018-01-02 16:08:00 -05:00
dfinke
e8a3d3f350 Added -Force to New-Alias 2018-01-02 11:32:04 -05:00
dfinke
458a08dab0 Add example to set the background color of a cloumn 2017-12-21 09:33:14 -05:00
dfinke
c598bbc2a5 updated 2017-12-02 18:06:40 -05:00
dfinke
dc4b66fffe Two things, checks for $Chart.DataLabel and if the directory for the xlsx path does not exist, it creates it 2017-12-02 12:38:24 -05:00
dfinke
9b57853881 fixed 2017-11-29 18:19:51 -05:00
dfinke
a4169a42f1 Added YouTube link 2017-11-24 14:26:11 -05:00
dfinke
0862fcdc8c Changed description 2017-11-24 14:14:07 -05:00
dfinke
28ddd7de13 Added ReturnRange param 2017-11-24 14:13:51 -05:00
dfinke
21d9c56854 updated to image example 2017-11-24 14:13:30 -05:00
dfinke
0f9bf07d30 Bumped version. Added url for how to videos 2017-11-24 10:16:13 -05:00
dfinke
1df63e3206 added xlrange to image example 2017-11-24 10:07:39 -05:00
dfinke
360c497bee Added new ps1 files 2017-11-24 10:07:23 -05:00
dfinke
182e4313b3 bump version 2017-11-23 12:31:10 -05:00
dfinke
df3702a09c Updated readme 2017-11-23 12:30:51 -05:00
dfinke
4616112aee Commented out last line 2017-11-23 12:30:35 -05:00
Doug Finke
a6f0d621f8 Merge pull request #250 from jhoneill/master
Minor fixes, plus set-row. set column and Send-SQLData
2017-11-23 12:24:30 -05:00
jhoneill
0489ac7d4f Added Convert-xlRangeToImage
New functionlity to allow a range to be exported to a PNG, JPG or BMP
2017-11-17 11:24:46 +00:00
jhoneill
9969a94309 Minor Fixes, Set-Row, Set-Column, Send-SQLDataToExcel
Export-Excel :
* 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
2017-11-15 18:04:14 +00:00
jhoneill
6d106fcc33 Merge remote-tracking branch 'refs/remotes/dfinke/master' 2017-10-31 12:58:31 +00:00
dfinke
98d7c04b3f updated 2017-10-30 19:06:33 -04:00
Doug Finke
23b1608671 Merge pull request #243 from dfinke/jhoneill-master
Simple tweaks to James O'Neill PR
2017-10-30 18:58:04 -04:00
dfinke
e008f3cc21 Updated readme 2017-10-30 18:56:44 -04:00
dfinke
9b7e068beb bumped version 2017-10-30 18:54:04 -04:00
dfinke
624a4e4b82 Updated list of ps1 files for copying and importing 2017-10-30 18:39:04 -04:00
dfinke
93868d4cd3 New files for formatting 2017-10-30 18:38:47 -04:00
dfinke
53b35f9285 Tweaked paramsets 2017-10-30 18:38:25 -04:00
dfinke
edc8b29859 Merge branch 'master' of https://github.com/jhoneill/ImportExcel into jhoneill-master 2017-10-29 11:20:38 -04:00
jhoneill
efadf83b6b Fixed a misplaced commas
Param block in close was re-ordered leaving a parameter in the middle
missing a comma and the parameter at the end had a comma it should not
have done.
2017-10-24 09:17:32 +01:00
jhoneill
890906ff10 Fixes to formatting , new Export charts tool (requires Excel.exe) help updates and Export excel support for "No data" use
Export-Excel.ps1
1. No code seems to act on the "NoClobber" parameter, I had previously
added a "ClearSheet" parameter which removes the worksheet and any past
data (before if there were fewer rows or columns in the new data, the
old would not be over-written)
2. I have made the whole of the process{} block in export-Excel subject
to IF ($target data)  .... this way things which are processed in the
end{} block can be done in a seperate call to Export-Excel without the
need to load data.  Because of this there should be no need for the Open
and Close -ExcelPackage functions going forward, but I've left them in
place for now
3. Fixed a bug when setting the target range it was previously setting
the end of the range to be the number of columns which failed if the
data was inserted at a column other than one.
4. I've moved the top left corner of a pivot chart so it sits closer to
the data.
5. I've change the check for inserting a pivot table so if
-InsertPivotChart is specified it implies -insertPivotTable.

Formatting.
6. The previous check in was not up-to date and contained some issues
with conditional formatting which I had fixed in the past.
7. Fixed a peformance bug in set-format where a foreach intended to
allow the same format to be applied to multiple ranges would format a
large blocks of cells one-by-one if it was the only range passed. .
8. Improved online help for the formatting commands.

Export charts
9. My current project is importing a lot of data into Excel and needs to
take Excel charts out as JPG files. I've thrown in the Export-Charts
script as "something for the pot",  even though the connection with the
rest of work is loose,
2017-10-24 09:02:14 +01:00
jhoneill
bd40cfe829 Append, and formatting
Changed Export-Excel.ps1

#1 @ Line 197 Made new parameter sets . Default, and table already
existed and use path. Added DefaultPackage and TablePackage
A New parameter  "Package" allows an ExcelPackage object returned by
-passThru to be passed in
~Line 400 code to use package or path depending on path passed.

(also added Open-ExcelPackage  to get the object without exporting and
Close-ExcelPackage to close it nicely - these are in their own file)

#2.  @ Line 256 added new parameter excludeProperty to remove unwanted
properties without needing to go through select-object
~Line 459 added logic to exclude the properties specified in the new
parameter

#3 .  @ Line 262 Added new parameter Append
~Line 420 code to read the existing headers and move the insertion point
below the current data
(normal behaviour is to check if headers exist when adding data in the
process block, which makes this change wonderfully easy)
~Line 510 changed basis for identifying named ranges and changed scope
for rangeName so it can be used on other sheets

#4.  ~Line 550. Remove any existing Pivot table before trying to
[re]create it.

Added formatting.ps1 which applies conditional and normal formats -
requires an ExcelPackage to be open.

Added Open-ExcelPackage.ps1 (which contains a close function as well to
get the the object and save it )  open allows the sheet to be loaded
into a package object without needing to export .

Updated .psm1 to add the formating and open/close ps1 files.
2017-10-21 18:14:02 +01:00
dfinke
08254b0fe1 Added 2017-10-20 17:07:29 -04:00
dfinke
1834a4967f Corrected version 2017-10-20 17:04:15 -04:00
Doug Finke
954ed8d736 Merge pull request #233 from DarkLite1/master
Fixed zero exported as String instead of numerical
2017-10-17 09:49:22 -04:00
DarkLite1
4823424ae3 Fixed zero exported as String instead of numerical 2017-10-16 13:59:22 +02:00
dfinke
e07fad442a Update 2017-10-13 16:38:57 -04:00
dfinke
a175125990 Bump version 2017-10-13 16:38:50 -04:00
Doug Finke
ca92d468cf Merge pull request #232 from dfinke/MultilplePivotTablesWithCmdlets
Multilple pivot tables with cmdlets
2017-10-13 16:19:14 -04:00
dfinke
8d6f5521af updated 2017-10-13 15:50:25 -04:00
dfinke
ceb986b408 added 2017-10-13 09:17:51 -04:00
dfinke
48607e403e Name of pivot table comes from caller 2017-10-12 12:06:16 -04:00
dfinke
57c02a466d works with multiple sheets 2017-10-12 11:33:49 -04:00
dfinke
1004d8a9ea Updated 2017-10-12 10:55:18 -04:00
dfinke
f5acf88a17 Experiment multiple pivot tables 2017-10-11 19:23:45 -04:00
dfinke
85151f8375 formatted 2017-10-11 18:11:47 -04:00
dfinke
304a96e955 updated 2017-10-07 10:29:07 -04:00
Doug Finke
3c027bafdd Update ToDo.md 2017-10-07 10:28:08 -04:00
dfinke
ac04f0025a added 2017-10-07 10:27:13 -04:00
dfinke
fd2c5dd042 updated 2017-10-07 10:26:11 -04:00
dfinke
51447f9732 Updated 2017-10-04 18:26:08 -04:00
dfinke
6966d0d4ef bumped version 2017-10-04 18:26:03 -04:00
Doug Finke
2dc50250fc Merge pull request #230 from ili101/master
Fix Bug. AutoFilter with TableName corrupted file
2017-10-04 18:21:48 -04:00
ili101
8ad38b544e Fix Bug, Unable to find type [PSPlot] in ISE
When loading cmdlet info from ISE "Commands" panel you get the error
"Unable to find type [PSPlot]"
https://github.com/dfinke/ImportExcel/issues/131
2017-10-05 00:15:57 +03:00
ili101
06124e12e7 Fix Bug. AutoFilter with TableName corrupted file
Fix Bug, AutoFilter with TableName create corrupted Excel file.
https://github.com/dfinke/ImportExcel/issues/65
You now cannot set AutoFilter and TableName at the same time.
"-Now" will not auto add AutoFilter if TableName set.
2017-10-05 00:02:06 +03:00
58 changed files with 3626 additions and 527 deletions

1
.gitignore vendored
View File

@@ -55,4 +55,3 @@ test.xlsx
testCCFMT.ps1 testCCFMT.ps1
testHide.ps1 testHide.ps1
ImportExcel.zip ImportExcel.zip
*.xlsx

View File

@@ -0,0 +1,118 @@
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
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.
#>
Param (
#The worksheet where the format is to be applied
[Parameter(Mandatory = $true, ParameterSetName = "NamedRule")]
[Parameter(Mandatory = $true, ParameterSetName = "DataBar")]
[Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSet")]
[Parameter(Mandatory = $true, ParameterSetName = "FourIconSet")]
[Parameter(Mandatory = $true, ParameterSetName = "FiveIconSet")]
[OfficeOpenXml.ExcelWorksheet]$WorkSheet ,
#The area of the worksheet where the format is to be applied
[Parameter(Mandatory = $true, ParameterSetName = "NamedRule")]
[Parameter(Mandatory = $true, ParameterSetName = "DataBar")]
[Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSet")]
[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
[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
[Parameter(Mandatory = $true, ParameterSetName = "NamedRule", Position = 3)]
[Parameter(Mandatory = $true, ParameterSetName = "NamedRuleAddress", Position = 3)]
[OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType]$RuleType ,
#Text colour for matching objects
[Alias("ForeGroundColour")]
[System.Drawing.Color]$ForeGroundColor,
#colour for databar type charts
[Parameter(Mandatory = $true, ParameterSetName = "DataBar")]
[Parameter(Mandatory = $true, ParameterSetName = "DataBarAddress")]
[Alias("DataBarColour")]
[System.Drawing.Color]$DataBarColor,
#One of the three-icon set types (e.g. Traffic Lights)
[Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSet")]
[Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSetAddress")]
[OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting3IconsSetType]$ThreeIconsSet,
#A four-icon set name
[Parameter(Mandatory = $true, ParameterSetName = "FourIconSet")]
[Parameter(Mandatory = $true, ParameterSetName = "FourIconSetAddress")]
[OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting4IconsSetType]$FourIconsSet,
#A five-icon set name
[Parameter(Mandatory = $true, ParameterSetName = "FiveIconSet")]
[Parameter(Mandatory = $true, ParameterSetName = "FiveIconSetAddress")]
[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
[string]$ConditionValue2,
#Background colour for matching items
[System.Drawing.Color]$BackgroundColor,
#Background pattern for matching items
[OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid,
#Secondary colour when a background pattern requires it
[System.Drawing.Color]$PatternColor,
#Sets the numeric format for matching items
$NumberFormat,
#Put matching items in bold face
[switch]$Bold,
#Put matching items in italic
[switch]$Italic,
#Underline matching items
[switch]$Underline,
#Strikethrough text of matching items
[switch]$StrikeThru
)
#Allow add 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 ($rule -eq "Databar" -and -not $databarColor) {Write-Warning -Message "-DatabarColor must be specified for the Databar rule type" }
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
$ConditionValue2 -and $RuleType -match "Between") {
$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 }
}

34
ColorCompletion.ps1 Normal file
View File

@@ -0,0 +1,34 @@
Function ColorCompletion {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
[System.Drawing.KnownColor].GetFields() | Where-Object {$_.IsStatic -and $_.name -like "$wordToComplete*" } |
Sort-Object name | ForEach-Object {New-CompletionResult $_.name $_.name
}
}
if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue) {
Register-ArgumentCompleter -CommandName Export-Excel -ParameterName TitleBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName DataBarColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName AllDataBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName TabColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-Worksheet -ParameterName AddBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-Worksheet -ParameterName ChangeBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-Worksheet ` -ParameterName DeleteBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-MulipleSheets -ParameterName KeyFontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-MulipleSheets -ParameterName AddBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-MulipleSheets -ParameterName ChangeBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-MulipleSheets ` -ParameterName DeleteBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-MulipleSheets -ParameterName KeyFontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Column -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Column -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Column -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Row -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Row -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Row -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion
}

View File

@@ -0,0 +1,74 @@
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}
}
<#
del demo*.xlsx
$workSheetname = 'Processes'
$Path = "$pwd\demo.xlsx"
$myData = Get-Process | Select-Object -Property Name,WS,CPU,Description,company,startTime
$excelPackage = $myData | Export-Excel -KillExcel -Path $Path -WorkSheetname $workSheetname -ClearSheet -AutoSize -AutoFilter -BoldTopRow -FreezeTopRow -PassThru
$workSheet = $excelPackage.Workbook.Worksheets[$workSheetname]
$range = $workSheet.Dimension.Address
Set-Format -WorkSheet $workSheet -Range "b:b" -NumberFormat "#,###" -AutoFit
Set-Format -WorkSheet $workSheet -Range "C:C" -NumberFormat "#,##0.00" -AutoFit
Set-Format -WorkSheet $workSheet -Range "F:F" -NumberFormat "dd MMMM HH:mm:ss" -AutoFit
Add-ConditionalFormatting -WorkSheet $workSheet -Range "c2:c1000" -DataBarColor Blue
Add-ConditionalFormatting -WorkSheet $workSheet -Range "b2:B1000" -RuleType GreaterThan -ConditionValue '104857600' -ForeGroundColor "Red" -Bold
Export-Excel -ExcelPackage $excelPackage -WorkSheetname $workSheetname
Convert-XlRangeToImage -Path $Path -workSheetname $workSheetname -range $range -destination "$pwd\temp.png" -show
#>
#Convert-XlRangeToImage -Path $Path -workSheetname $workSheetname -range $range -destination "$pwd\temp.png" -show

View File

@@ -1,4 +1,93 @@
function ConvertFrom-ExcelToSQLInsert { function ConvertFrom-ExcelToSQLInsert {
<#
.SYNOPSIS
Generate SQL insert statements from Excel spreadsheet.
.DESCRIPTION
Generate SQL insert statements from Excel spreadsheet.
.PARAMETER TableName
Name of the target database table.
.PARAMETER Path
Path to an existing .XLSX file
This parameter is passed to Import-Excel as is.
.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
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
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
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
Import only rows and columns that contain data, empty rows and empty columns are not imported.
.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
Generate SQL insert statements from Movies.xlsx file, leaving blank cells as empty strings:
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Sheet1 |
----------------------------------------------------------
| A B C |
|1 Movie Name Year Rating |
|2 The Bodyguard 1992 9 |
|3 The Matrix 1999 8 |
|4 Skyfall 2012 9 |
|5 The Avengers 2012 |
----------------------------------------------------------
PS C:\> Import-Excel -TableName "Movies" -Path 'C:\Movies.xlsx'
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Bodyguard', '1992', '9');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Matrix', '1999', '8');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('Skyfall', '2012', '9');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Avengers', '2012', '');
.EXAMPLE
Generate SQL insert statements from Movies.xlsx file, specify NULL instead of an empty string.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Sheet1 |
----------------------------------------------------------
| A B C |
|1 Movie Name Year Rating |
|2 The Bodyguard 1992 9 |
|3 The Matrix 1999 8 |
|4 Skyfall 2012 9 |
|5 The Avengers 2012 |
----------------------------------------------------------
PS C:\> ConvertFrom-ExcelToSQLInsert -TableName "Movies" -Path "C:\Movies.xlsx" -ConvertEmptyStringsToNull
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Bodyguard', '1992', '9');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Matrix', '1999', '8');
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( param(
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
$TableName, $TableName,
@@ -8,39 +97,34 @@ function ConvertFrom-ExcelToSQLInsert {
$Path, $Path,
[Alias("Sheet")] [Alias("Sheet")]
$WorkSheetname = 1, $WorkSheetname = 1,
[int]$HeaderRow = 1, [Alias('HeaderRow', 'TopRow')]
[ValidateRange(1, 9999)]
[Int]$StartRow,
[string[]]$Header, [string[]]$Header,
[switch]$NoHeader, [switch]$NoHeader,
[switch]$DataOnly [switch]$DataOnly,
[switch]$ConvertEmptyStringsToNull
) )
$null = $PSBoundParameters.Remove('TableName') $null = $PSBoundParameters.Remove('TableName')
$null = $PSBoundParameters.Remove('ConvertEmptyStringsToNull')
$params = @{} + $PSBoundParameters $params = @{} + $PSBoundParameters
ConvertFrom-ExcelData @params { ConvertFrom-ExcelData @params {
param($propertyNames, $record) param($propertyNames, $record)
$ColumnNames = "'" + ($PropertyNames -join "', '") + "'" $ColumnNames = "'" + ($PropertyNames -join "', '") + "'"
$values = foreach ($propertyName in $PropertyNames) { $record.$propertyName } $values = foreach ($propertyName in $PropertyNames) {
$targetValues = "'" + ($values -join "', '") + "'" if ($ConvertEmptyStringsToNull.IsPresent -and [string]::IsNullOrEmpty($record.$propertyName)) {
'NULL'
}
else {
"'" + $record.$propertyName + "'"
}
}
$targetValues = ($values -join ", ")
"INSERT INTO {0} ({1}) Values({2});" -f $TableName, $ColumnNames, $targetValues "INSERT INTO {0} ({1}) Values({2});" -f $TableName, $ColumnNames, $targetValues
} }
# $data = Import-Excel @params
# $PropertyNames = $data[0].psobject.Properties |
# Where-Object {$_.membertype -match 'property'} |
# Select-Object -ExpandProperty name
# $ColumnNames = "'" + ($PropertyNames -join "', '") + "'"
# foreach ($record in $data) {
# $values = $(foreach ($propertyName in $PropertyNames) {
# $record.$propertyName
# })
# $targetValues = "'" + ($values -join "', '") + "'"
# "INSERT INTO {0} ({1}) Values({2});" -f $TableName, $ColumnNames, $targetValues
# }
} }

View File

@@ -0,0 +1,34 @@
Import-Module .\ImportExcel.psd1 -Force
$xlFile = ".\testSQL.xlsx"
Describe "ConvertFrom-ExcelToSQLInsert" {
BeforeEach {
$([PSCustomObject]@{
Name="John"
Age=$null
}) | Export-Excel $xlFile
}
AfterAll {
Remove-Item $xlFile -Recurse -Force -ErrorAction Ignore
}
It "Should be empty double single quotes" {
$expected="INSERT INTO Sheet1 ('Name', 'Age') Values('John', '');"
$actual = ConvertFrom-ExcelToSQLInsert -Path $xlFile Sheet1
$actual | should be $expected
}
It "Should have NULL" {
$expected="INSERT INTO Sheet1 ('Name', 'Age') Values('John', NULL);"
$actual = ConvertFrom-ExcelToSQLInsert -Path $xlFile Sheet1 -ConvertEmptyStringsToNull
$actual | should be $expected
}
}

7
DoTests.ps1 Normal file
View File

@@ -0,0 +1,7 @@
$PSVersionTable.PSVersion
if ((Get-Module -ListAvailable pester) -eq $null) {
Install-Module -Name Pester -Repository PSGallery -Force
}
Invoke-Pester -Script $PSScriptRoot\__tests__

BIN
Examples/Charts/Tools.xlsx Normal file

Binary file not shown.

View File

@@ -0,0 +1,21 @@
Remove-Item -Path .\test.xlsx -ErrorAction Ignore
$excel = Get-Process |
Select-Object -Property Name,Company,Handles,CPU,PM,NPM,WS |
Export-Excel -Path .\test.xlsx -ClearSheet -WorkSheetname "Processes" -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 -Address $sheet.Column($c) -AutoFit }
Export-Excel -ExcelPackage $excel -WorkSheetname "Processes" -IncludePivotChart -ChartType ColumnClustered -NoLegend -PivotRows company -PivotData @{'Name'='Count'} -Show

Binary file not shown.

View File

@@ -0,0 +1,72 @@
Import-Module ..\..\ImportExcel.psd1 -Force
$f = ".\dashboard.xlsx"
Remove-Item $f -ErrorAction Ignore
$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
"@ | ConvertFrom-Csv
$data | Export-Excel $f -AutoSize
$excel = Open-ExcelPackage $f
$sheet1 = $excel.Workbook.Worksheets["sheet1"]
$sheet1.View.ShowGridLines = $false
$sheet1.View.ShowHeaders = $false
Set-Format -Address $sheet1.Cells["C:C"] -NumberFormat "$#,##0" -WrapText -HorizontalAlignment Center
Set-Format -Address $sheet1.Cells["D:D"] -NumberFormat "#.#0%" -WrapText -HorizontalAlignment Center
Set-Format -Address $sheet1.Cells["E:E"] -NumberFormat "$#,##0" -WrapText -HorizontalAlignment Center
Set-Format -Address $sheet1.Cells["F:F"] -NumberFormat "#.#0%" -WrapText -HorizontalAlignment Center
Set-Format -Address $sheet1.Cells["G:H"] -WrapText -HorizontalAlignment Center
## Insert Rows/Columns
$sheet1.InsertRow(1, 1)
foreach ($col in Write-Output 2 4 6 8 10 12 14) {
$sheet1.InsertColumn($col, 1)
$sheet1.Column($col).width = .75
}
Set-Format -Address $sheet1.Cells["E:E"] -Width 12
Set-Format -Address $sheet1.Cells["I:I"] -Width 12
$BorderBottom = "Thick"
$BorderColor = "Black"
Set-Format -Address $sheet1.Cells["A2"] -BorderBottom $BorderBottom -BorderColor $BorderColor
Set-Format -Address $sheet1.Cells["C2"] -BorderBottom $BorderBottom -BorderColor $BorderColor
Set-Format -Address $sheet1.Cells["E2:G2"] -BorderBottom $BorderBottom -BorderColor $BorderColor
Set-Format -Address $sheet1.Cells["I2:K2"] -BorderBottom $BorderBottom -BorderColor $BorderColor
Set-Format -Address $sheet1.Cells["M2:O2"] -BorderBottom $BorderBottom -BorderColor $BorderColor
Set-Format -Address $sheet1.Cells["A2:C8"] -FontColor GrayText
$HorizontalAlignment = "Center"
Set-Format -Address $sheet1.Cells["F1"] -HorizontalAlignment $HorizontalAlignment -Bold -Value Revenue
Set-Format -Address $sheet1.Cells["J1"] -HorizontalAlignment $HorizontalAlignment -Bold -Value Margin
Set-Format -Address $sheet1.Cells["N1"] -HorizontalAlignment $HorizontalAlignment -Bold -Value Passenger
Set-Format -Address $sheet1.Cells["E2"] -Value '($)'
Set-Format -Address $sheet1.Cells["G2"] -Value '%'
Set-Format -Address $sheet1.Cells["I2"] -Value '($)'
Set-Format -Address $sheet1.Cells["K2"] -Value '%'
Set-Format -Address $sheet1.Cells["C10"] -HorizontalAlignment Right -Bold -Value "Grand Total Calculation"
Set-Format -Address $sheet1.Cells["E10"] -Formula "=Sum(E3:E8)" -Bold
Set-Format -Address $sheet1.Cells["I10"] -Formula "=Sum(I3:I8)" -Bold
Set-Format -Address $sheet1.Cells["M10"] -Formula "=Sum(M3:M8)" -Bold
Set-Format -Address $sheet1.Cells["O10"] -Formula "=Sum(O3:O8)" -Bold
Close-ExcelPackage $excel -Show

Binary file not shown.

BIN
Examples/Fibonacci/fib.xlsx Normal file

Binary file not shown.

View File

@@ -0,0 +1,38 @@
if(!(gcm ig -ErrorAction SilentlyContinue)) {
"Use ``Install-Module NameIT`` to get the needed module from the gallery to support running this script"
return
}
$sign=@{sign=echo + -}
$location=@{location=echo Atlanta Newark Washington Chicago Philadelphia Houston Phoneix}
$(1..6 | % {
$from=$to=""
while($from -eq $to) {
$from=ig "[location]" -CustomData $location
$to=ig "[location]" -CustomData $location
}
[double]$a=ig "########"
[double]$b=ig ".####"
[double]$c=ig "#######"
[double]$d=ig "[sign].##" -CustomData $sign
[double]$e=ig "###"
[double]$f=ig "[sign]##" -CustomData $sign
#"{0},{1},{2},{3},{4},{5},{6},{7}" -f $from, $to, $a, $b, $c, $d, $e, $f
[PSCustomObject][Ordered]@{
From=$from
To=$to
RDollars=$a
RPercent=$b
MDollars=$c
MPercent=$d
Revenue=$e
Margin=$f
}
} | ConvertTo-Csv -NoTypeInformation) -replace '"','' # | Export-Excel

View File

@@ -1,9 +1,9 @@
ColumnChart -Title "Central Limit Theorem" ($( ColumnChart -Title "Central Limit Theorem" -NoLegend ($(
for ($i = 1; $i -le 500; $i++) { for ($i = 1; $i -le 500; $i++) {
$s=0 $s = 0
for ($j = 1; $j -le 100; $j++){ for ($j = 1; $j -le 100; $j++) {
$s+=Get-Random -Minimum 0 -Maximum 2 $s += Get-Random -Minimum 0 -Maximum 2
}
$s
} }
$s ) | Sort-Object | Group-Object | Select-Object Count, Name)
}
) | Sort | Group | select Count, Name)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,21 @@
Import-Module ..\..\ImportExcel.psd1 -Force
$xlFile=".\testPivot.xlsx"
Remove-Item $xlFile -ErrorAction Ignore
$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
"@ | ConvertFrom-Csv
$data |
Export-Excel $xlFile -Show `
-AutoSize -AutoFilter `
-IncludePivotTable `
-PivotRows Product `
-PivotData @{"Units"="sum"} -PivotFilter Region, Area

View File

@@ -0,0 +1,88 @@
 Import-Module -name ImportExcel -Force -Verbose
$sql = @"
SELECT rootfile.baseName , rootfile.extension , Image.fileWidth AS width , image.fileHeight AS height ,
metadata.dateDay , metadata.dateMonth , metadata.dateYear , Image.captureTime AS dateTaken,
metadata.hasGPS , metadata.GPSLatitude , metadata.GPSLongitude ,
metadata.focalLength , metadata.flashFired , metadata.ISOSpeedRating AS ISOSpeed,
metadata.Aperture AS apertureValue , metadata.ShutterSpeed AS shutterSpeedValue,
Image.bitdepth , image.colorLabels ,
Camera.Value AS cameraModel , LensRef.value AS lensModel
FROM Adobe_images image
JOIN AgLibraryFile rootFile ON rootfile.id_local = image.rootFile
JOIN AgharvestedExifMetadata metadata ON image.id_local = metadata.image
LEFT JOIN AgInternedExifLens LensRef ON LensRef.id_Local = metadata.lensRef
LEFT JOIN AgInternedExifCameraModel Camera ON Camera.id_local = metadata.cameraModelRef
"@
``
#Sql Statement gets 20 columns of data from Adobe lightroom database
#Define a pivot table and chart for total pictures with each lens.
$pt = @{"LensPivot" = @{ "PivotTableName" = "LensPivot";
"SourceWorkSheet" = "Sheet1" ;
"PivotRows" = "LensModel" ;
"PivotData" = @{"basename" = "Count"} ;
"IncludePivotChart" = $true ;
"NoLegend" = $true ;
"ShowPercent" = $true ;
"ChartType" = "Pie" ;
"ChartTitle" = "Split by Lens" }
}
#we want to add 3 columns, translate Apperture value and Shutter speed value into familar f/ and seconds notation, and use these and ISO to calculate EV level
$Avalue = {"=IF(P$ROW>6.63,TEXT(ROUND(Sqrt(Power(2,O$ROW)),1),`"`"`"f/`"`"0.0`")," +
"TEXT(ROUND(Sqrt(Power(2,O$ROW)),1),`"`"`"f/`"`"0.0`"))"}
$Svalue = {"=IF(P$ROW>2,TEXT(ROUND(POWER(2,P$ROW),0),`"`"`"1/`"`"0`"`"sec`"`"`"),"+
"IF(P$ROW>3.32,TEXT(ROUND(1/POWER(2,P$ROW),2),`"0.0`"`"Sec`"`"`"),"+
"TEXT(ROUND(1/POWER(2,P$ROW),2),`"0`"`"Sec`"`"`")))"}
$evValue = {"=ROUND(P$Row+O$Row-(LOG(N$Row/100,2)),0)" }
#remove and recreate the file
Remove-Item -Path "~\Documents\temp.xlsx" -ErrorAction SilentlyContinue
#Open a connection to the ODBC source "LR" (which points to the SQLLite DB for Lightroom), run the SQL query, and drop into Excel - in sheet1, autosizing columns.
$e = Send-SQLDataToExcel -Path "~\Documents\temp.xlsx" -WorkSheetname "Sheet1" -Connection "DSN=LR" -SQL $sql -AutoSize -Passthru
#Add columns, then format them and hide the ones which aren't of interest.
Set-Column -Worksheet $e.workbook.Worksheets["sheet1"] -Column 21 -Value $Avalue -Heading "Apperture"
Set-Column -Worksheet $e.workbook.Worksheets["sheet1"] -Column 22 -Value $Svalue -Heading "Shutter"
Set-Column -Worksheet $e.workbook.Worksheets["sheet1"] -Column 23 -Value $Evvalue -Heading "Ev"
Set-Format -Address $e.workbook.Worksheets["sheet1" ].Column(21) -HorizontalAlignment Left -AutoFit
Set-Format -Address $e.workbook.Worksheets["sheet1" ].Column(22) -HorizontalAlignment Right -AutoFit
@(5,6,7,13,15,16,17,18) | ForEach-Object {
Set-Format -Address $e.workbook.Worksheets["sheet1" ].Column($_) -Hidden
}
#Center the column labels.
Set-Format -Address $e.workbook.Worksheets["sheet1" ].Row(1) -HorizontalAlignment Center
#Format the data as a nice Table, Create the pivot table & chart defined above, show the file in Excel in excel after saving.
Export-Excel -ExcelPackage $e -WorkSheetname "sheet1" -TableName "Table" -PivotTableDefinition $pt -Show
############################################################
Remove-Item .\demo3.xlsx
#Database query to get race wins, Poles and fastest lapes for the 25 best drivers; we already have a connection to the DB in $dbSessions
$session = $DbSessions["f1"]
$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
"@
#Run the query and put the results in workshet "Winners", autosize the columns and hold on to the ExcelPackage object
$Excel = Send-SQLDataToExcel -SQL $sql -Session $session -path .\demo3.xlsx -WorkSheetname "Winners" -AutoSize -Passthru
#Create and format columns for the ratio of Wins to poles and fast laps.
Set-Column -ExcelPackage $Excel -WorkSheetname "Winners" -column 6 -Heading "WinsToPoles" -Value {"=D$row/C$row"}
Set-Column -ExcelPackage $Excel -WorkSheetname "Winners" -column 7 -Heading "WinsToFast" -Value {"=E$row/C$row"}
6..7 | ForEach-Object {
Set-Format -Address $Excel.Workbook.Worksheets["Winners"].column($_) -NumberFormat "0.0%" -AutoFit }
#Define a chart to show the relationship of lest on an XY Grid, create the ranges required in the, add the chart and show the file in Excel in excel after saving.
$chart = New-ExcelChart -NoLegend -ChartType XYScatter -XRange WinsToFast -YRange WinsToPoles -ShowCategory -Column 7 -Width 2000 -Height 700
Export-Excel -ExcelPackage $Excel -WorkSheetname "Winners" -AutoNameRange -ExcelChartDefinition $chart -Show

View File

@@ -0,0 +1,22 @@
ipmo C:\Users\mcp\Documents\GitHub\ImportExcel\ImportExcel.psd1 -Force
Get-SQL -Session f1 -Excel -Connection C:\Users\mcp\OneDrive\Public\F1\f1Results.xlsx -showtables -Verbose
del .\demo3.xlsx
$session = $DbSessions["f1"]
$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"
$Excel = Send-SQLDataToExcel -SQL $sql -Session $session -path .\demo3.xlsx -WorkSheetname "Winners" -AutoSize -AutoNameRange -BoldTopRow -FreezeTopRow -Passthru
$ws = $Excel.Workbook.Worksheets["Winners"]
Set-Row -Worksheet $ws -Heading "Average" -Value {"=Average($columnName`2:$columnName$endrow)"} -NumberFormat "0.0" -Bold
Set-Column -Worksheet $ws -Heading "WinsToPoles" -Value {"=D$row/C$row"} -Column 6 -AutoSize -AutoNameRange
Set-Column -Worksheet $ws -Heading "WinsToFast" -Value {"=E$row/C$row"} -Column 7 -AutoSize -AutoNameRange
Set-Format -WorkSheet $ws -Range "F2:G50" -NumberFormat "0.0%"
$chart = New-ExcelChart -NoLegend -ChartType XYScatter -XRange WinsToFast -YRange WinsToPoles -Column 7 -Width 2000 -Height 700 -Title "Poles vs fastlaps"
Export-Excel -ExcelPackage $Excel -WorkSheetname "Winners" -ExcelChartDefinition $chart -Show

View File

@@ -0,0 +1,9 @@

$p = ps | select Company, Handles | Export-Excel c:\temp\testBackgroundColor.xlsx -ClearSheet -KillExcel -PassThru
$ws = $p.Workbook.WorkSheets[1]
$totalRows = $ws.Dimension.Rows
Set-Format -Address $ws.Cells["B2:B$($totalRows)"] -BackgroundColor LightBlue
Export-Excel -ExcelPackage $p -show

View File

@@ -0,0 +1,30 @@
# To ship, is to choose
#ipmo .\ImportExcel.psd1 -Force
$pt=[ordered]@{}
$pt.ServiceInfo=@{
SourceWorkSheet='Services'
PivotRows = "Status"
PivotData= @{'Status'='count'}
IncludePivotChart=$true
ChartType='BarClustered3D'
}
$pt.ProcessInfo=@{
SourceWorkSheet='Processes'
PivotRows = "Company"
PivotData= @{'Company'='count'}
IncludePivotChart=$true
ChartType='PieExploded3D'
}
$gsv=Get-Service | Select-Object status, Name, displayName, starttype
$ps=Get-Process | Select-Object Name,Company, Handles
$file = "c:\temp\testPT.xlsx"
rm $file -ErrorAction Ignore
$gsv| Export-Excel -Path $file -AutoSize -WorkSheetname Services
$ps | Export-Excel -Path $file -AutoSize -WorkSheetname Processes -PivotTableDefinition $pt -Show

View File

@@ -0,0 +1,26 @@
Import-Module ..\ImportExcel.psd1 -Force
$file = "C:\Temp\test.xlsx"
Remove-Item $file -ErrorAction Ignore -Force
$base = @{
SourceWorkSheet = 'gsv'
PivotData = @{'Status' = 'count'}
IncludePivotChart = $true
# ChartType = 'BarClustered3D'
}
$ptd = [ordered]@{}
# $ptd.gpt1 = $base + @{ PivotRows = "ServiceType" }
# $ptd.gpt2 = $base + @{ PivotRows = "Status" }
# $ptd.gpt3 = $base + @{ PivotRows = "StartType" }
# $ptd.gpt4 = $base + @{ PivotRows = "CanStop" }
$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

View File

@@ -0,0 +1,23 @@
ipmo .\ImportExcel.psd1 -Force
. .\ConvertExcelToImageFile.ps1
$xlFileName = "C:\Temp\testPNG.xlsx"
rm C:\Temp\testPNG.xlsx -ErrorAction Ignore
$range = @"
Region,Item,Cost
North,Pear,1
South,Apple,2
East,Grapes,3
West,Berry,4
North,Pear,1
South,Apple,2
East,Grapes,3
West,Berry,4
"@ | ConvertFrom-Csv |
Export-Excel $xlFileName -ReturnRange `
-ConditionalText (New-ConditionalText Apple), (New-ConditionalText Berry -ConditionalTextColor White -BackgroundColor Purple)
Convert-XlRangeToImage -Path $xlFileName -workSheetname sheet1 -range $range -Show

View File

@@ -1,64 +1,94 @@
# Contributed by https://github.com/W1M0R #Requires -Modules Pester
#Requires -Modules Assert
Import-Module ImportExcel -Force $here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
function New-TestWorkbook { Import-Module $here -Force
$testWorkbook = "$($PSScriptRoot)\test.xlsx"
Remove-Item $testWorkbook -ErrorAction Ignore $WarningPreference = 'SilentlyContinue'
$testWorkbook $ProgressPreference = 'SilentlyContinue'
Function Test-isNumeric {
Param (
[Parameter(ValueFromPipeline)]$x
)
Return $x -is [byte] -or $x -is [int16] -or $x -is [int32] -or $x -is [int64] `
-or $x -is [sbyte] -or $x -is [uint16] -or $x -is [uint32] -or $x -is [uint64] `
-or $x -is [float] -or $x -is [double] -or $x -is [decimal]
} }
function Remove-TestWorkbook { $fakeData = [PSCustOmobject]@{
New-TestWorkbook | Out-Null Property_1_Date = (Get-Date).ToString('d') # US '10/16/2017' BE '16/10/2107'
Property_2_Formula = '=SUM(G2:H2)'
Property_3_String = 'My String'
Property_4_String = 'a'
Property_5_IPAddress = '10.10.25.5'
Property_6_Number = '0'
Property_7_Number = '5'
Property_8_Number = '007'
Property_9_Number = (33).ToString('F2') # US '33.00' BE '33,00'
Property_10_Number = (5/3).ToString('F2') # US '1.67' BE '1,67'
Property_11_Number = (15999998/3).ToString('N2') # US '5,333,332.67' BE '5.333.332,67'
Property_12_Number = '1.555,83'
Property_13_PhoneNr = '+32 44'
Property_14_PhoneNr = '+32 4 4444 444'
Property_15_PhoneNr = '+3244444444'
} }
function New-TestDataCsv { $Path = 'Test.xlsx'
@"
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
01200,Drill,20,8,160
00120,Crowbar,7,23.48,164.36
true,Bla,7,82,12
false,Bla,7,82,12
2009-05-01 14:57:32.8,Yay,1,3,2
"@ | ConvertFrom-Csv
}
Describe "Export-Excel" { Describe 'Export-Excel' {
in $TestDrive {
Describe 'Number conversion' {
Context 'numerical values expected' {
#region Create test file
$fakeData | Export-Excel -Path $Path
$csvData = New-TestDataCsv $Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
$workbook = New-TestWorkbook $Excel = New-Object OfficeOpenXml.ExcelPackage $Path
$Worksheet = $Excel.Workbook.WorkSheets[1]
#endregion
Context "Importing CSV data from a here string" { it 'zero' {
It "All properties are type [string]" { $fakeData.Property_6_Number | Should -BeExactly '0'
$csvData | % { $Worksheet.Cells[2, 6].Text | Should -BeExactly $fakeData.Property_6_Number
$_.PSObject.Properties | % { $Worksheet.Cells[2, 6].Value | Test-isNumeric | Should -Be $true
$_.Value -is [string] | Should Be $true }
It 'regular number' {
$fakeData.Property_7_Number | Should -BeExactly '5'
$Worksheet.Cells[2, 7].Text | Should -BeExactly $fakeData.Property_7_Number
$Worksheet.Cells[2, 7].Value | Test-isNumeric | Should -Be $true
}
It 'number starting with zero' {
$fakeData.Property_8_Number | Should -BeExactly '007'
$Worksheet.Cells[2, 8].Text | Should -BeExactly '7'
$Worksheet.Cells[2, 8].Value | Test-isNumeric | Should -Be $true
}
It 'decimal number' {
# US '33.00' BE '33,00'
$fakeData.Property_9_Number | Should -BeExactly (33).ToString('F2')
$Worksheet.Cells[2, 9].Text | Should -BeExactly '33'
$Worksheet.Cells[2, 9].Value | Test-isNumeric | Should -Be $true
# US '1.67' BE '1,67'
$fakeData.Property_10_Number | Should -BeExactly (5/3).ToString('F2')
$Worksheet.Cells[2, 10].Text | Should -BeExactly $fakeData.Property_10_Number
$Worksheet.Cells[2, 10].Value | Test-isNumeric | Should -Be $true
}
It 'thousand seperator and decimal number' {
# US '5,333,332.67' BE '5.333.332,67'
# Excel BE '5333332,67'
$fakeData.Property_11_Number | Should -BeExactly (15999998/3).ToString('N2')
$Worksheet.Cells[2, 11].Text | Should -BeExactly $fakeData.Property_11_Number
$Worksheet.Cells[2, 11].Value | Test-isNumeric | Should -Be $true
} }
} }
} }
It "Leading zeroes are preserved" {
$csvData[4] | Select-Object -ExpandProperty ID | Should Be "00120"
}
} }
Context "Piping CSV data to Export-Excel" {
$xlPkg = $csvData | Export-Excel $workbook -PassThru
$ws = $xlPkg.Workbook.WorkSheets[1]
It "Exports numeric strings as numbers" {
$csvData[2] | Select-Object -ExpandProperty ID | Should Be "12003"
$ws.Cells["A4"].Value -is [double] | Should Be $true
$ws.Cells["A4"].Value | Should Be 12003
}
$xlPkg.Save()
$xlPkg.Dispose()
}
Remove-TestWorkbook
} }

View File

@@ -1,17 +1,87 @@
Function Export-Excel { Function Export-Excel {
<# <#
.SYNOPSIS .SYNOPSIS
Export data to an Excel worksheet. Export data to an Excel worksheet.
.DESCRIPTION .DESCRIPTION
Export data to an Excel file and where possible try to convert numbers so Excel recognizes them as numbers instead of text. After all. Excel is a spreadsheet program used for number manipulation and calculations. In case the number conversion is not desired, use the parameter '-NoNumberConversion *'. Export data to an Excel file and where possible try to convert numbers so Excel recognizes them as numbers instead of text. After all. Excel is a spreadsheet program used for number manipulation and calculations. In case the number conversion is not desired, use the parameter '-NoNumberConversion *'.
.PARAMETER Path
Path to a new or existing .XLSX file
.PARAMETER ExcelPackage
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 WorkSheetName
The name of a sheet within the workbook - "Sheet1" by default
.PARAMETER ClearSheet
If specified Export-Excel will remove any existing worksheet with the selected name. The Default behaviour is to overwrite cells in this sheet as needed (but leaving non-overwritten ones in place)
.PARAMETER Append
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 ExcludeProperty
Specifies properties which may exist in the target data but should not be placed on the worksheet
.PARAMETER Title
Text of a title to be placed in Cell A1
.PARAMETER TitleBold
Sets the title in boldface type
.PARAMETER TitleSize
Sets the point size for the title
.PARAMETER TitleBackgroundColor
Sets the cell background to solid and the chose colour 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 PivotRows
Name(s) columns from the spreadhseet which will provide the row name(s) in the pivot table
.PARAMETER PivotColumns
Name(s) columns from the spreadhseet which will provide the Column name(s) in the pivot table
.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.
.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 ConditionalText .PARAMETER ConditionalText
Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered. Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered.
.PARAMETER NoNumberConversion .PARAMETER NoNumberConversion
By default we convert all values to numbers if possible, but this isn't always desirable. NoNumberConversion allows you to add exceptions for the conversion. Wildcards (like '*') are allowed. By default we convert all values to numbers if possible, but this isn't always desirable. NoNumberConversion allows you to add exceptions for the conversion. Wildcards (like '*') are allowed.
.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 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 ExcelChartDefinition
A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts
.PARAMETER HideSheet
Name(s) of Sheet(s) to hide in the workbook
.PARAMETER KillExcel
Closes Excel - prevents errors writing to the file because Excel has it open
.PARAMETER AutoNameRange
Makes each column a named range
.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 .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 select column from within Excel.
@@ -51,6 +121,8 @@ Function Export-Excel {
.PARAMETER Show .PARAMETER Show
Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first.
.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 .EXAMPLE
Get-Process | Export-Excel .\Test.xlsx -show Get-Process | Export-Excel .\Test.xlsx -show
@@ -190,29 +262,93 @@ Function Export-Excel {
.EXAMPLE .EXAMPLE
Get-Service | Export-Excel 'c:\temp\test.xlsx' -Show -IncludePivotTable -PivotRows status -PivotData @{status='count'} Get-Service | Export-Excel 'c:\temp\test.xlsx' -Show -IncludePivotTable -PivotRows status -PivotData @{status='count'}
.EXAMPLE
$pt = [ordered]@{}
$pt.pt1=@{ SourceWorkSheet = 'Sheet1';
PivotRows = 'Status'
PivotData = @{'Status'='count'}
IncludePivotChart = $true
ChartType = 'BarClustered3D'
}
$pt.pt2=@{ SourceWorkSheet = 'Sheet2';
PivotRows = 'Company'
PivotData = @{'Company'='count'}
IncludePivotChart = $true
ChartType = 'PieExploded3D'
}
Remove-Item -Path .\test.xlsx
Get-Service | Select-Object -Property Status,Name,DisplayName,StartType | Export-Excel -Path .\test.xlsx -AutoSize
Get-Process | Select-Object -Property Name,Company,Handles,CPU,VM | Export-Excel -Path .\test.xlsx -AutoSize -WorkSheetname 'sheet2'
Export-Excel -Path .\test.xlsx -PivotTableDefinition $pt -Show
This example defines two pivot tables. Then it puts Service data on Sheet1 with one call to Export-Excel and Process Data on sheet2 with a second call to Export-Excel
The thrid and final call adds the two pivot tables and opens the spreadsheet in Excel
.EXAMPLE
Remove-Item -Path .\test.xlsx
$excel = Get-Service | Select-Object -Property Status,Name,DisplayName,StartType | Export-Excel -Path .\test.xlsx -PassThru
$excel.Workbook.Worksheets["Sheet1"].Row(1).style.font.bold = $true
$excel.Workbook.Worksheets["Sheet1"].Column(3 ).width = 29
$excel.Workbook.Worksheets["Sheet1"].Column(3 ).Style.wraptext = $true
$excel.Save()
$excel.Dispose()
Start-Process .\test.xlsx
This example uses -passthrough - put service information into sheet1 of the work book and saves the excelPackageObject in $Excel
It then uses the package object to apply formatting, and then saves the workbook and disposes of the object before loading the document in Excel.
.EXAMPLE
Remove-Item -Path .\test.xlsx -ErrorAction Ignore
$excel = Get-Process | Select-Object -Property Name,Company,Handles,CPU,PM,NPM,WS | Export-Excel -Path .\test.xlsx -ClearSheet -WorkSheetname "Processes" -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 -Address $sheet.Column($c) -AutoFit }
Export-Excel -ExcelPackage $excel -WorkSheetname "Processes" -IncludePivotChart -ChartType ColumnClustered -NoLegend -PivotRows company -PivotData @{'Name'='Count'} -Show
This a more sophisticated version of the previous example showing different ways of using Set-Format, and also adding conditional formatting.
In the final command a Pivot chart is added and the workbook is opened in Excel.
.LINK .LINK
https://github.com/dfinke/ImportExcel https://github.com/dfinke/ImportExcel
#> #>
[CmdLetBinding()] [CmdletBinding(DefaultParameterSetName = 'Default')]
Param( Param(
$Path, [Parameter(ParameterSetName = "Default", Position = 0)]
[Parameter(ValueFromPipeline=$true)] [Parameter(ParameterSetName = "Table" , Position = 0)]
[String]$Path,
[Parameter(Mandatory = $true, ParameterSetName = "PackageDefault")]
[Parameter(Mandatory = $true, ParameterSetName = "PackageTable")]
[OfficeOpenXml.ExcelPackage]$ExcelPackage,
[Parameter(ValueFromPipeline = $true)]
$TargetData, $TargetData,
[String]$Password,
[String]$WorkSheetname = 'Sheet1', [String]$WorkSheetname = 'Sheet1',
[switch]$ClearSheet,
[switch]$Append,
[String]$Title, [String]$Title,
[OfficeOpenXml.Style.ExcelFillStyle]$TitleFillPattern = 'None', [OfficeOpenXml.Style.ExcelFillStyle]$TitleFillPattern = 'None',
[Switch]$TitleBold, [Switch]$TitleBold,
[Int]$TitleSize = 22, [Int]$TitleSize = 22,
[System.Drawing.Color]$TitleBackgroundColor, [System.Drawing.Color]$TitleBackgroundColor,
[Switch]$IncludePivotTable,
[String[]]$PivotRows, [String[]]$PivotRows,
[String[]]$PivotColumns, [String[]]$PivotColumns,
$PivotData, $PivotData,
[String[]]$PivotFilter,
[Switch]$PivotDataToColumn, [Switch]$PivotDataToColumn,
[String]$Password, [Hashtable]$PivotTableDefinition,
[OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie',
[Switch]$IncludePivotTable,
[Switch]$IncludePivotChart, [Switch]$IncludePivotChart,
[OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie',
[Switch]$NoLegend, [Switch]$NoLegend,
[Switch]$ShowCategory, [Switch]$ShowCategory,
[Switch]$ShowPercent, [Switch]$ShowPercent,
@@ -223,25 +359,31 @@ Function Export-Excel {
[Switch]$FreezeFirstColumn, [Switch]$FreezeFirstColumn,
[Switch]$FreezeTopRowFirstColumn, [Switch]$FreezeTopRowFirstColumn,
[Int[]]$FreezePane, [Int[]]$FreezePane,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'PackageDefault')]
[Switch]$AutoFilter, [Switch]$AutoFilter,
[Switch]$BoldTopRow, [Switch]$BoldTopRow,
[Switch]$NoHeader, [Switch]$NoHeader,
[String]$RangeName, [String]$RangeName,
[ValidateScript({ [ValidateScript( {
if ($_.Contains(' ')) { if ($_.Contains(' ')) {
throw 'Tablename has spaces.' throw 'Tablename has spaces.'
} }
elseif (-not $_) { elseif (-not $_) {
throw 'Tablename is null or empty.' throw 'Tablename is null or empty.'
} }
elseif ($_[0] -notmatch '[a-z]') { elseif ($_[0] -notmatch '[a-z]') {
throw 'Tablename start with invalid character.' throw 'Tablename starts with an invalid character.'
} }
else { else {
$true $true
} }
})] })]
[Parameter(ParameterSetName = 'Table' , Mandatory = $true)]
[Parameter(ParameterSetName = 'PackageTable' , Mandatory = $true)]
[String]$TableName, [String]$TableName,
[Parameter(ParameterSetName = 'Table')]
[Parameter(ParameterSetName = 'PackageTable')]
[OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6', [OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6',
[Object[]]$ExcelChartDefinition, [Object[]]$ExcelChartDefinition,
[String[]]$HideSheet, [String[]]$HideSheet,
@@ -251,14 +393,28 @@ Function Export-Excel {
[Int]$StartColumn = 1, [Int]$StartColumn = 1,
[Switch]$PassThru, [Switch]$PassThru,
[String]$Numberformat = 'General', [String]$Numberformat = 'General',
[string[]]$ExcludeProperty,
[String[]]$NoNumberConversion, [String[]]$NoNumberConversion,
[Object[]]$ConditionalFormat, [Object[]]$ConditionalFormat,
[Object[]]$ConditionalText, [Object[]]$ConditionalText,
[ScriptBlock]$CellStyleSB, [ScriptBlock]$CellStyleSB,
[Switch]$Now [Parameter(ParameterSetName = 'Now')]
# [Parameter(ParameterSetName = 'TableNow')]
[Switch]$Now,
[Switch]$ReturnRange,
[Switch]$NoTotalsInPivot,
[Switch]$ReZip
) )
Begin { Begin {
function Find-WorkSheet {
param (
$WorkSheetName
)
$pkg.Workbook.Worksheets | Where-Object {$_.name -match $WorkSheetName}
}
Function Add-CellValue { Function Add-CellValue {
<# <#
.SYNOPSIS .SYNOPSIS
@@ -290,14 +446,14 @@ Function Export-Excel {
{$_ -is [DateTime]} { {$_ -is [DateTime]} {
#region Save a date with an international valid format #region Save a date with an international valid format
$TargetCell.Value = $_ $TargetCell.Value = $_
$TargetCell.Style.Numberformat.Format = 'm/d/yy h:mm' $TargetCell.Style.Numberformat.Format = 'm/d/yy h:mm' # This is not a custom format, but a preset recognized as date and localized.
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as date" Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as date"
break break
#endregion #endregion
} }
{(($NoNumberConversion) -and ($NoNumberConversion -contains $Name)) -or {(($NoNumberConversion) -and ($NoNumberConversion -contains $Name)) -or
($NoNumberConversion -eq '*')} { ($NoNumberConversion -eq '*')} {
#regioon Save a value without converting to number #regioon Save a value without converting to number
$TargetCell.Value = $_ $TargetCell.Value = $_
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' unconverted" Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' unconverted"
@@ -307,7 +463,7 @@ Function Export-Excel {
Default { Default {
#region Save a value as a number if possible #region Save a value as a number if possible
if ($Number = ConvertTo-Number $_) { if (($Number = ConvertTo-Number $_) -ne $null) {
$TargetCell.Value = $Number $TargetCell.Value = $Number
$targetCell.Style.Numberformat.Format = $Numberformat $targetCell.Style.Numberformat.Format = $Numberformat
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' as number converted from '$_' with format '$Numberformat'" Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' as number converted from '$_' with format '$Numberformat'"
@@ -342,7 +498,7 @@ Function Export-Excel {
if ($TitleBackgroundColor -AND ($TitleFillPattern -ne 'None')) { if ($TitleBackgroundColor -AND ($TitleFillPattern -ne 'None')) {
$ws.Cells[$Row, $StartColumn].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor) $ws.Cells[$Row, $StartColumn].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor)
} }
else { elseif ($TitleBackgroundColor) {
Write-Warning "Title Background Color ignored. You must set the TitleFillPattern parameter to a value other than 'None'. Try 'Solid'." Write-Warning "Title Background Color ignored. You must set the TitleFillPattern parameter to a value other than 'None'. Try 'Solid'."
} }
} }
@@ -359,8 +515,8 @@ Function Export-Excel {
$R = $null $R = $null
if ([Double]::TryParse([String]$Value,[System.Globalization.NumberStyles]::Any, if ([Double]::TryParse([String]$Value, [System.Globalization.NumberStyles]::Any,
[System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$R)) { [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$R)) {
$R $R
} }
} }
@@ -374,50 +530,76 @@ Function Export-Excel {
Get-Process excel -ErrorAction Ignore | Stop-Process Get-Process excel -ErrorAction Ignore | Stop-Process
while (Get-Process excel -ErrorAction Ignore) {} while (Get-Process excel -ErrorAction Ignore) {}
} }
Try { Try {
$script:Header = $null $script:Header = $null
if ($append -and $clearSheet) {throw "You can't use -Append AND -ClearSheet."}
if ($KillExcel) { if ($KillExcel) {
Stop-ExcelProcess Stop-ExcelProcess
} }
if ($Now) { if ($PSBoundParameters.Keys.Count -eq 0 -Or $Now) {
$Path = [System.IO.Path]::GetTempFileName() -replace '\.tmp','.xlsx' $Path = [System.IO.Path]::GetTempFileName() -replace '\.tmp', '.xlsx'
$Show = $true $Show = $true
$AutoSize = $true $AutoSize = $true
$AutoFilter = $true if (!$TableName) {
$AutoFilter = $true
}
} }
$Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) if ($ExcelPackage) {
$pkg = $ExcelPackage
$Path = $pkg.File
}
Else {
$Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
if (Test-Path $Path) { $targetPath = Split-Path $Path
Write-Debug "Path '$Path' already exists" if (!(Test-Path $targetPath)) {
Write-Debug "Base path $($targetPath) does not exist, creating"
$null = mkdir $targetPath -ErrorAction Ignore
}
elseif (Test-Path $Path) {
Write-Debug "Path '$Path' already exists"
}
$pkg = New-Object OfficeOpenXml.ExcelPackage $Path
} }
$pkg = New-Object OfficeOpenXml.ExcelPackage $Path [OfficeOpenXml.ExcelWorksheet]$ws = $pkg | Add-WorkSheet -WorkSheetname $WorkSheetname -NoClobber:$NoClobber -ClearSheet:$ClearSheet #Add worksheet doesn't take any action for -noClobber
$ws = $pkg | Add-WorkSheet -WorkSheetname $WorkSheetname -NoClobber:$NoClobber
foreach ($format in $ConditionalFormat ) { foreach ($format in $ConditionalFormat ) {
$target = "Add$($format.Formatter)" $target = "Add$($format.Formatter)"
$rule = ($ws.ConditionalFormatting).PSObject.Methods[$target].Invoke($format.Range, $format.IconType) $rule = ($ws.ConditionalFormatting).PSObject.Methods[$target].Invoke($format.Range, $format.IconType)
$rule.Reverse = $format.Reverse $rule.Reverse = $format.Reverse
} }
$Row = $StartRow if ($append) {
$headerRange = $ws.Dimension.Address -replace "\d+$", "1"
if ($Title) { #if there is a title or anything else above the header row, specifying StartRow will skip it.
Add-Title if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1", "$StartRow"}
#$script:Header = $ws.Cells[$headerrange].Value
$Row += 1 #using a slightly odd syntax otherwise header ends up as a 2D array
$ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ }
$row = $ws.Dimension.Rows
Write-Debug -Message ("Appending: headers are " + ($script:Header -join ", ") + "Start row $row")
} }
elseif ($Title) {
#Can only add a title if not appending
$Row = $StartRow
Add-Title
$Row ++ ; $startRow ++
}
else {
$Row = $StartRow
}
$ColumnIndex = $StartColumn
$firstTimeThru = $true $firstTimeThru = $true
$isDataTypeValueType = $false $isDataTypeValueType = $false
$pattern = 'string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort' $pattern = 'string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort'
} }
Catch { Catch {
if ($AlreadyExists) { if ($AlreadyExists) {
#Is this set anywhere ?
throw "Failed exporting worksheet '$WorkSheetname' to '$Path': The worksheet '$WorkSheetname' already exists." throw "Failed exporting worksheet '$WorkSheetname' to '$Path': The worksheet '$WorkSheetname' already exists."
} }
else { else {
@@ -427,69 +609,76 @@ Function Export-Excel {
} }
Process { Process {
Try { if ($TargetData) {
if ($firstTimeThru) { Try {
$firstTimeThru = $false if ($firstTimeThru) {
$isDataTypeValueType = $TargetData.GetType().name -match $pattern $firstTimeThru = $false
Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" $isDataTypeValueType = $TargetData.GetType().name -match $pattern
} Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'"
if ($isDataTypeValueType) {
$ColumnIndex = $StartColumn
Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData
$ColumnIndex += 1
$Row += 1
}
else {
#region Add headers
if (-not $script:Header) {
$ColumnIndex = $StartColumn
$script:Header = $TargetData.PSObject.Properties.Name
if ($NoHeader) {
# Don't push the headers to the spread sheet
$Row -= 1
}
else {
foreach ($Name in $script:Header) {
$ws.Cells[$Row, $ColumnIndex].Value = $Name
Write-Verbose "Cell '$Row`:$ColumnIndex' add header '$Name'"
$ColumnIndex += 1
}
}
} }
#endregion
$Row += 1 if ($isDataTypeValueType) {
$ColumnIndex = $StartColumn $ColumnIndex = $StartColumn
foreach ($Name in $script:Header) { Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData
#region Add non header values
Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData.$Name
$ColumnIndex += 1 $ColumnIndex += 1
$Row += 1
}
else {
#region Add headers
if (-not $script:Header) {
$ColumnIndex = $StartColumn
$script:Header = $TargetData.PSObject.Properties.Name | Where-Object {$_ -notin $ExcludeProperty}
if ($NoHeader) {
# Don't push the headers to the spread sheet
$Row -= 1
}
else {
foreach ($Name in $script:Header) {
$ws.Cells[$Row, $ColumnIndex].Value = $Name
Write-Verbose "Cell '$Row`:$ColumnIndex' add header '$Name'"
$ColumnIndex += 1
}
}
}
#endregion #endregion
$Row += 1
$ColumnIndex = $StartColumn
foreach ($Name in $script:Header) {
#region Add non header values
Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData.$Name
$ColumnIndex += 1
#endregion
}
} }
} }
} Catch {
Catch { throw "Failed exporting worksheet '$WorkSheetname' to '$Path': $_"
throw "Failed exporting worksheet '$WorkSheetname' to '$Path': $_" }
} }
} }
End { End {
Try { Try {
if ($AutoNameRange) { if ($AutoNameRange) {
$totalRows = $ws.Dimension.Rows if (-not $script:header) {
$headerRange = $ws.Dimension.Address -replace "\d+$", "1"
#if there is a title or anything else above the header row, specifying StartRow will skip it.
if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1", "$StartRow"}
#using a slightly odd syntax otherwise header ends up as a 2D array
$ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ }
}
$totalRows = $ws.Dimension.End.Row
$totalColumns = $ws.Dimension.Columns $totalColumns = $ws.Dimension.Columns
foreach ($c in 0..($totalColumns - 1)) {
foreach($c in 0..($totalColumns-1)) {
$targetRangeName = "$($script:Header[$c])" $targetRangeName = "$($script:Header[$c])"
$targetColumn = $c + $StartColumn
$targetColumn = $c+1 $theCell = $ws.Cells[($startrow + 1), $targetColumn, $totalRows , $targetColumn ]
$theCell = $ws.Cells[2,$targetColumn,$totalRows,$targetColumn ]
$ws.Names.Add($targetRangeName, $theCell) | Out-Null $ws.Names.Add($targetRangeName, $theCell) | Out-Null
if ([OfficeOpenXml.FormulaParsing.ExcelUtilities.ExcelAddressUtil]::IsValidAddress($targetRangeName)) { if ([OfficeOpenXml.FormulaParsing.ExcelUtilities.ExcelAddressUtil]::IsValidAddress($targetRangeName)) {
@@ -499,7 +688,7 @@ Function Export-Excel {
} }
if ($Title) { if ($Title) {
$startAddress = "A2" $startAddress = $ws.Dimension.Start.address -replace "$($ws.Dimension.Start.row)`$", "$($ws.Dimension.Start.row + 1)"
} }
else { else {
$startAddress = $ws.Dimension.Start.Address $startAddress = $ws.Dimension.Start.Address
@@ -515,27 +704,113 @@ Function Export-Excel {
if (-not [String]::IsNullOrEmpty($TableName)) { if (-not [String]::IsNullOrEmpty($TableName)) {
$csr = $StartRow $csr = $StartRow
if ($Title) {
$csr += 1
}
$csc = $StartColumn $csc = $StartColumn
$cer = $ws.Dimension.End.Row $cer = $ws.Dimension.End.Row
$cec = $script:Header.Count $cec = $ws.Dimension.End.Column # was $script:Header.Count
$targetRange = $ws.Cells[$csr, $csc, $cer,$cec] $targetRange = $ws.Cells[$csr, $csc, $cer, $cec]
#if we're appending data the table may already exist: but excel doesn't like the result if I put
# if ($ws.Tables[$TableName]) {$ws.Tables.Delete($TableName) }
$tbl = $ws.Tables.Add($targetRange, $TableName) $tbl = $ws.Tables.Add($targetRange, $TableName)
$tbl.TableStyle = $TableStyle $tbl.TableStyle = $TableStyle
} }
if ($IncludePivotTable) { $PivotTableStartCell = "A1"
if($PivotFilter) {$PivotTableStartCell = "A3"}
if ($PivotTableDefinition) {
foreach ($item in $PivotTableDefinition.GetEnumerator()) {
$targetName = $item.Key
$pivotTableName = $targetName #+ 'PivotTable'
#Make sure the Pivot table sheet doesn't already exist
try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {}
$wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber
$pivotTableDataName = $targetName + 'PivotTableData'
if (!$item.Value.SourceWorkSheet) {
$pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $ws.Cells[$dataRange], $pivotTableDataName)
}
else {
$workSheet = Find-WorkSheet $item.Value.SourceWorkSheet
if ($workSheet) {
$targetStartAddress = $workSheet.Dimension.Start.Address
$targetDataRange = "{0}:{1}" -f $targetStartAddress, $workSheet.Dimension.End.Address
$pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $workSheet.Cells[$targetDataRange], $pivotTableDataName)
}
}
switch ($item.Value.Keys) {
"PivotRows" {
foreach ($Row in $item.Value.PivotRows) {
$null = $pivotTable.RowFields.Add($pivotTable.Fields[$Row])
}
}
"PivotColumns" {
foreach ($Column in $item.Value.PivotColumns) {
$null = $pivotTable.ColumnFields.Add($pivotTable.Fields[$Column])
}
}
"PivotData" {
$pivotData = $item.Value.PivotData
if ($PivotData -is [HashTable] -or $PivotData -is [System.Collections.Specialized.OrderedDictionary]) {
$PivotData.Keys | ForEach-Object {
$df = $pivotTable.DataFields.Add($pivotTable.Fields[$_])
$df.Function = $PivotData.$_
}
}
else {
foreach ($Item in $PivotData) {
$df = $pivotTable.DataFields.Add($pivotTable.Fields[$Item])
$df.Function = 'Count'
}
}
if ($PivotDataToColumn) {
$pivotTable.DataOnRows = $false
}
}
"IncludePivotChart" {
$ChartType = "Pie"
if ($item.Value.ChartType) {
$ChartType = $item.Value.ChartType
}
$chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable)
$chart.SetPosition(0, 0, 4, 0) #Changed position to top row, next to a chart which doesn't pivot on columns
$chart.SetSize(600, 400)
if ($chart.DataLabel) {
$chart.DataLabel.ShowCategory = [boolean]$item.value.ShowCategory
$chart.DataLabel.ShowPercent = [boolean]$item.value.ShowPercent
}
if ([boolean]$item.value.NoLegend) {$chart.Legend.Remove()}
if ($item.value.ChartTitle) {$chart.Title.Text = $item.value.chartTitle}
}
}
if($item.Value.NoTotalsInPivot) {
$pivotTable.RowGrandTotals = $false
}
}
}
if ($IncludePivotTable -or $IncludePivotChart) {
#changed so -includePivotChart Implies -includePivotTable.
$pivotTableName = $WorkSheetname + 'PivotTable' $pivotTableName = $WorkSheetname + 'PivotTable'
#Make sure the Pivot table sheet doesn't already exist
try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {}
$wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber $wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber
$wsPivot.View.TabSelected = $true $wsPivot.View.TabSelected = $true
$pivotTableDataName=$WorkSheetname + 'PivotTableData' $pivotTableDataName = $WorkSheetname + 'PivotTableData'
$pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells['A1'], $ws.Cells[$dataRange], $pivotTableDataName) $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $ws.Cells[$dataRange], $pivotTableDataName)
if ($PivotRows) { if ($PivotRows) {
foreach ($Row in $PivotRows) { foreach ($Row in $PivotRows) {
@@ -567,48 +842,60 @@ Function Export-Excel {
} }
} }
if($NoTotalsInPivot) {
$pivotTable.RowGrandTotals = $false
}
if ($IncludePivotChart) { if ($IncludePivotChart) {
$chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable) $chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable)
$chart.DataLabel.ShowCategory = $ShowCategory if ($chart.DataLabel) {
$chart.DataLabel.ShowPercent = $ShowPercent $chart.DataLabel.ShowCategory = $ShowCategory
$chart.DataLabel.ShowPercent = $ShowPercent
}
$chart.SetPosition(0, 26, 2, 26) # if Pivot table is rows+data only it will be 2 columns wide if has pivot columns we don't know how wide it will be
if ($NoLegend) { if ($NoLegend) {
$chart.Legend.Remove() $chart.Legend.Remove()
} }
$chart.SetPosition(1, 0, 6, 0)
$chart.SetSize(600, 400)
} }
} }
if($pivotTable -and $PivotFilter) {
foreach($pFilter in $PivotFilter) {
$null = $pivotTable.PageFields.Add($pivotTable.Fields[$pFilter])
}
}
if ($Password) { if ($Password) {
$ws.Protection.SetPassword($Password) $ws.Protection.SetPassword($Password)
} }
if ($AutoFilter) { if ($AutoFilter) {
$ws.Cells[$dataRange].AutoFilter=$true $ws.Cells[$dataRange].AutoFilter = $true
} }
if ($FreezeTopRow) { if ($FreezeTopRow) {
$ws.View.FreezePanes(2,1) $ws.View.FreezePanes(2, 1)
} }
if ($FreezeTopRowFirstColumn) { if ($FreezeTopRowFirstColumn) {
$ws.View.FreezePanes(2,2) $ws.View.FreezePanes(2, 2)
} }
if ($FreezeFirstColumn) { if ($FreezeFirstColumn) {
$ws.View.FreezePanes(1,2) $ws.View.FreezePanes(1, 2)
} }
if ($FreezePane) { if ($FreezePane) {
$freezeRow,$freezeColumn=$FreezePane $freezeRow, $freezeColumn = $FreezePane
if (-not $freezeColumn -or $freezeColumn -eq 0) { if (-not $freezeColumn -or $freezeColumn -eq 0) {
$freezeColumn=1 $freezeColumn = 1
} }
if ($freezeRow -gt 1) { if ($freezeRow -gt 1) {
$ws.View.FreezePanes($freezeRow,$freezeColumn) $ws.View.FreezePanes($freezeRow, $freezeColumn)
} }
} }
if ($BoldTopRow) { if ($BoldTopRow) {
@@ -629,9 +916,9 @@ Function Export-Excel {
$pkg.Workbook.WorkSheets[$Sheet].Hidden = 'Hidden' $pkg.Workbook.WorkSheets[$Sheet].Hidden = 'Hidden'
} }
$chartCount=0 $chartCount = 0
foreach ($chartDef in $ExcelChartDefinition) { foreach ($chartDef in $ExcelChartDefinition) {
$ChartName = 'Chart' + (Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.','' $ChartName = 'Chart' + (Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.', ''
$chart = $ws.Drawings.AddChart($ChartName, $chartDef.ChartType) $chart = $ws.Drawings.AddChart($ChartName, $chartDef.ChartType)
$chart.Title.Text = $chartDef.Title $chart.Title.Text = $chartDef.Title
@@ -640,26 +927,27 @@ Function Export-Excel {
} }
if ($chart.Datalabel -ne $null) { if ($chart.Datalabel -ne $null) {
$chart.Datalabel.ShowCategory = $chartDef.ShowCategory $chart.Datalabel.ShowCategory = $chartDef.ShowCategory
$chart.Datalabel.ShowPercent = $chartDef.ShowPercent $chart.Datalabel.ShowPercent = $chartDef.ShowPercent
} }
$chart.SetPosition($chartDef.Row, $chartDef.RowOffsetPixels,$chartDef.Column, $chartDef.ColumnOffsetPixels) $chart.SetPosition($chartDef.Row, $chartDef.RowOffsetPixels, $chartDef.Column, $chartDef.ColumnOffsetPixels)
$chart.SetSize($chartDef.Width, $chartDef.Height) $chart.SetSize($chartDef.Width, $chartDef.Height)
$chartDefCount = @($chartDef.YRange).Count $chartDefCount = @($chartDef.YRange).Count
if ($chartDefCount -eq 1) { if ($chartDefCount -eq 1) {
$Series=$chart.Series.Add($chartDef.YRange, $chartDef.XRange) $Series = $chart.Series.Add($chartDef.YRange, $chartDef.XRange)
$SeriesHeader=$chartDef.SeriesHeader $SeriesHeader = $chartDef.SeriesHeader
if (-not $SeriesHeader) { if (-not $SeriesHeader) {
$SeriesHeader = 'Series 1' $SeriesHeader = 'Series 1'
} }
$Series.Header = $SeriesHeader $Series.Header = $SeriesHeader
} else { }
for($idx = 0; $idx -lt $chartDefCount; $idx += 1) { else {
$Series=$chart.Series.Add($chartDef.YRange[$idx], $chartDef.XRange) for ($idx = 0; $idx -lt $chartDefCount; $idx += 1) {
$Series = $chart.Series.Add($chartDef.YRange[$idx], $chartDef.XRange)
if ($chartDef.SeriesHeader.Count -gt 0) { if ($chartDef.SeriesHeader.Count -gt 0) {
$SeriesHeader = $chartDef.SeriesHeader[$idx] $SeriesHeader = $chartDef.SeriesHeader[$idx]
@@ -679,12 +967,12 @@ Function Export-Excel {
foreach ($targetConditionalText in $ConditionalText) { foreach ($targetConditionalText in $ConditionalText) {
$target = "Add$($targetConditionalText.ConditionalType)" $target = "Add$($targetConditionalText.ConditionalType)"
$Range=$targetConditionalText.Range $Range = $targetConditionalText.Range
if (-not $Range) { if (-not $Range) {
$Range = $ws.Dimension.Address $Range = $ws.Dimension.Address
} }
$rule=($ws.Cells[$Range].ConditionalFormatting).PSObject.Methods[$target].Invoke() $rule = ($ws.Cells[$Range].ConditionalFormatting).PSObject.Methods[$target].Invoke()
if ($targetConditionalText.Text) { if ($targetConditionalText.Text) {
if ($targetConditionalText.ConditionalType -match 'equal|notequal|lessthan|lessthanorequal|greaterthan|greaterthanorequal') { if ($targetConditionalText.ConditionalType -match 'equal|notequal|lessthan|lessthanorequal|greaterthan|greaterthanorequal') {
@@ -711,7 +999,30 @@ Function Export-Excel {
$pkg $pkg
} }
else { else {
if ($ReturnRange) {
$ws.Dimension.Address
}
$pkg.Save() $pkg.Save()
if ($ReZip) {
write-verbose "Re-Zipping $($pkg.file) using .NET ZIP library"
$zipAssembly = "System.IO.Compression.Filesystem"
try {
Add-Type -assembly $zipAssembly -ErrorAction stop
} catch {
write-error "The -ReZip parameter requires .NET Framework 4.5 or later to be installed. Recommend to install Powershell v4+"
continue
}
$TempZipPath = Join-Path -path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName())
[io.compression.zipfile]::ExtractToDirectory($pkg.File,$TempZipPath) | Out-Null
Remove-Item $pkg.File -Force
[io.compression.zipfile]::CreateFromDirectory($TempZipPath,$pkg.File) | Out-Null
}
$pkg.Dispose() $pkg.Dispose()
if ($Show) { if ($Show) {
@@ -724,3 +1035,27 @@ Function Export-Excel {
} }
} }
} }
function New-PivotTableDefinition {
param(
[Parameter(Mandatory)]
[Alias("PivtoTableName")]#Previous typo - use alias to avoid breaking scripts
$PivotTableName,
$SourceWorkSheet,
$PivotRows,
[hashtable]$PivotData,
$PivotColumns,
[Switch]$IncludePivotChart,
[OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie',
[Switch]$NoLegend,
[Switch]$ShowCategory,
[Switch]$ShowPercent,
[String]$ChartTitle,
[Switch]$NoTotalsInPivot
)
$parameters = @{} + $PSBoundParameters
$parameters.Remove('PivotTableName')
@{$PivotTableName = $parameters}
}

View File

@@ -19,7 +19,7 @@ function Export-ExcelSheet {
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path $xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path
$workbook = $xl.Workbook $workbook = $xl.Workbook
$targetSheets = $workbook.Worksheets | Where {$_.Name -Match $SheetName} $targetSheets = $workbook.Worksheets | Where-Object {$_.Name -Match $SheetName}
$params = @{} + $PSBoundParameters $params = @{} + $PSBoundParameters
$params.Remove("OutputPath") $params.Remove("OutputPath")

51
Export-charts.ps1 Normal file
View File

@@ -0,0 +1,51 @@
<#
.Synopsis
Exports the charts in an Excel spreadSheet
.Example
Export-Charts .\test,xlsx
Exports the charts in test.xlsx to JPEG files in the current directory.
.Example
Export-Charts -path .\test,xlsx -destination [System.Environment+SpecialFolder]::MyDocuments -outputType PNG -passthrough
Exports the charts to PNG files in MyDocuments , and returns file objects representing the newly created files
#>
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.
[switch]$Passthru,
#Format to write - JPG by default
[ValidateSet("JPG","PNG","GIF")]
$OutputType = "JPG",
#Folder to write image files to (defaults to same one as the Excel file is in)
$Destination
)
#if no output folder was specified, set destination to the folder where the Excel file came from
if (-not $Destination) {$Destination = Split-Path -Path $Path -Parent }
#Call up Excel and tell it to open the file.
try { $excelApp = New-Object -ComObject "Excel.Application" }
catch { Write-Warning "Could not start Excel application - which usually means it is not installed." ; return }
try { $excelWorkBook = $excelApp.Workbooks.Open($Path) }
catch { Write-Warning -Message "Could not Open $Path." ; return }
#For each worksheet, for each chart, jump to the chart, create a filename of "WorksheetName_ChartTitle.jpg", and export the file.
foreach ($excelWorkSheet in $excelWorkBook.Worksheets) {
#note somewhat unusual way of telling excel we want all the charts.
foreach ($excelchart in $excelWorkSheet.ChartObjects([System.Type]::Missing)) {
#if you don't go to the chart the image will be zero size !
$excelApp.Goto($excelchart.TopLeftCell,$true)
$imagePath = Join-Path -Path $Destination -ChildPath ($excelWorkSheet.Name + "_" + ($excelchart.Chart.ChartTitle.Text -split "\s\d\d:\d\d,")[0] + ".$OutputType")
if ( $excelchart.Chart.Export($imagePath, $OutputType, $false) ) { # Export returs true/false for success/failure
if ($Passthru) {Get-Item -Path $imagePath } # when succesful return a file object (-Passthru) or print a verbose message, write warning for any failures
else {Write-Verbose -Message "Exported $imagePath"}
}
else {Write-Warning -Message "Failure exporting $imagePath" }
}
}
$excelApp.DisplayAlerts = $false
$excelWorkBook.Close($false,$null,$null)
$excelApp.Quit()

104
GetExcelTable.ps1 Normal file
View File

@@ -0,0 +1,104 @@
Function Get-ExcelTableName {
Param (
$Path,
$WorksheetName
)
$Path = (Resolve-Path $Path).ProviderPath
$Stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path, 'Open', 'Read', 'ReadWrite'
$Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream
if ($WorksheetName) {
$Worksheet = $Excel.Workbook.Worksheets[$WorkSheetName]
} else {
$Worksheet = $Excel.Workbook.Worksheets | Select-Object -First 1
}
foreach($TableName in $Worksheet.Tables.Name) {
[PSCustomObject][Ordered]@{
WorksheetName=$Worksheet.Name
TableName=$TableName
}
}
$Stream.Close()
$Stream.Dispose()
$Excel.Dispose()
$Excel = $null
}
Function Get-ExcelTable {
Param (
$Path,
$TableName,
$WorksheetName
)
$Path = (Resolve-Path $Path).ProviderPath
$Stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path, 'Open', 'Read', 'ReadWrite'
$Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream
if ($WorksheetName) {
$Worksheet = $Excel.Workbook.Worksheets[$WorkSheetName]
} else {
$Worksheet = $Excel.Workbook.Worksheets | Select-Object -First 1
}
if($TableName) {
$Table = $Worksheet.Tables[$TableName]
} else {
$Table = $Worksheet.Tables | Select-Object -First 1
}
$rowCount = $Table.Address.Rows
$colCount = $Table.Address.Columns
$digits = "0123456789".ToCharArray()
$start, $end=$Table.Address.Address.Split(':')
$pos=$start.IndexOfAny($digits)
[int]$startCol=ConvertFrom-ExcelColumnName $start.Substring(0,$pos)
[int]$startRow=$start.Substring($pos)
$propertyNames = for($col=$startCol; $col -lt ($startCol+$colCount); $col+= 1) {
$Worksheet.Cells[$startRow, $col].value
}
$startRow++
for($row=$startRow; $row -lt ($startRow+$rowCount); $row += 1) {
$nr=[ordered]@{}
$c=0
for($col=$startCol; $col -lt ($startCol+$colCount); $col+= 1) {
$nr.($propertyNames[$c]) = $Worksheet.Cells[$row, $col].value
$c++
}
[pscustomobject]$nr
}
$Stream.Close()
$Stream.Dispose()
$Excel.Dispose()
$Excel = $null
}
function ConvertFrom-ExcelColumnName {
param($columnName)
$sum=0
$columnName.ToCharArray() |
ForEach {
$sum*=26
$sum+=[char]$_.tostring().toupper()-[char]'A'+1
}
$sum
}
cls
ipmo .\ImportExcel.psd1 -Force
#Get-ExcelTableName .\testTable.xlsx | Get-ExcelTable .\testTable.xlsx
Get-ExcelTable .\testTable.xlsx Table3

View File

@@ -2071,3 +2071,14 @@ Context 'special cases' {
} }
} }
} }
Context 'General Tests' {
in $TestDrive {
Describe 'Get Help' {
it 'New-Plot' {
#Get-Help : Unable to find type [PSPlot].
{Help New-Plot} | Should -Not -Throw
}
}
}
}

View File

@@ -4,7 +4,7 @@
RootModule = 'ImportExcel.psm1' RootModule = 'ImportExcel.psm1'
# Version number of this module. # Version number of this module.
ModuleVersion = '4.0.2' ModuleVersion = '5.0.0'
# ID used to uniquely identify this module # ID used to uniquely identify this module
GUID = '60dd4136-feff-401a-ba27-a84458c57ede' GUID = '60dd4136-feff-401a-ba27-a84458c57ede'
@@ -19,7 +19,10 @@ CompanyName = 'Doug Finke'
Copyright = 'c 2015 All rights reserved.' Copyright = 'c 2015 All rights reserved.'
# Description of the functionality provided by this module # Description of the functionality provided by this module
Description = 'PowerShell module to import/export Excel spreadsheets, without Excel' Description = @'
PowerShell module to import/export Excel spreadsheets, without Excel.
Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5uoqS92stXioZw-u-ze_NtvSo0k0K0kq
'@
# Minimum version of the Windows PowerShell engine required by this module # Minimum version of the Windows PowerShell engine required by this module
# PowerShellVersion = '' # PowerShellVersion = ''

View File

@@ -1,274 +1,274 @@
Add-Type -Path "$($PSScriptRoot)\EPPlus.dll" #region import everything we need
Add-Type -Path "$($PSScriptRoot)\EPPlus.dll"
. $PSScriptRoot\Charting.ps1 . $PSScriptRoot\AddConditionalFormatting.ps1
. $PSScriptRoot\ConvertFromExcelData.ps1 . $PSScriptRoot\Charting.ps1
. $PSScriptRoot\ConvertFromExcelToSQLInsert.ps1 . $PSScriptRoot\ColorCompletion.ps1
. $PSScriptRoot\ConvertToExcelXlsx.ps1 . $PSScriptRoot\ConvertExcelToImageFile.ps1
. $PSScriptRoot\Copy-ExcelWorkSheet.ps1 . $PSScriptRoot\Compare-WorkSheet.ps1
. $PSScriptRoot\Export-Excel.ps1 . $PSScriptRoot\ConvertFromExcelData.ps1
. $PSScriptRoot\Export-ExcelSheet.ps1 . $PSScriptRoot\ConvertFromExcelToSQLInsert.ps1
. $PSScriptRoot\Get-ExcelColumnName.ps1 . $PSScriptRoot\ConvertToExcelXlsx.ps1
. $PSScriptRoot\Get-ExcelSheetInfo.ps1 . $PSScriptRoot\Copy-ExcelWorkSheet.ps1
. $PSScriptRoot\Get-ExcelWorkbookInfo.ps1 . $PSScriptRoot\Export-Excel.ps1
. $PSScriptRoot\Get-HtmlTable.ps1 . $PSScriptRoot\Export-ExcelSheet.ps1
. $PSScriptRoot\Get-Range.ps1 . $PSScriptRoot\Get-ExcelColumnName.ps1
. $PSScriptRoot\Get-XYRange.ps1 . $PSScriptRoot\Get-ExcelSheetInfo.ps1
. $PSScriptRoot\Import-Html.ps1 . $PSScriptRoot\Get-ExcelWorkbookInfo.ps1
. $PSScriptRoot\InferData.ps1 . $PSScriptRoot\Get-HtmlTable.ps1
. $PSScriptRoot\Invoke-Sum.ps1 . $PSScriptRoot\Get-Range.ps1
. $PSScriptRoot\New-ConditionalFormattingIconSet.ps1 . $PSScriptRoot\Get-XYRange.ps1
. $PSScriptRoot\New-ConditionalText.ps1 . $PSScriptRoot\Import-Html.ps1
. $PSScriptRoot\New-ExcelChart.ps1 . $PSScriptRoot\InferData.ps1
. $PSScriptRoot\New-PSItem.ps1 . $PSScriptRoot\Invoke-Sum.ps1
. $PSScriptRoot\Pivot.ps1 . $PSScriptRoot\Merge-Worksheet.ps1
. $PSScriptRoot\Set-CellStyle.ps1 . $PSScriptRoot\New-ConditionalFormattingIconSet.ps1
. $PSScriptRoot\TrackingUtils.ps1 . $PSScriptRoot\New-ConditionalText.ps1
. $PSScriptRoot\Update-FirstObjectProperties.ps1 . $PSScriptRoot\New-ExcelChart.ps1
. $PSScriptRoot\New-PSItem.ps1
. $PSScriptRoot\Open-ExcelPackage.ps1
. $PSScriptRoot\Pivot.ps1
. $PSScriptRoot\Send-SQLDataToExcel.ps1
. $PSScriptRoot\Set-CellStyle.ps1
. $PSScriptRoot\Set-Column.ps1
. $PSScriptRoot\Set-Row.ps1
. $PSScriptRoot\SetFormat.ps1
. $PSScriptRoot\TrackingUtils.ps1
. $PSScriptRoot\Update-FirstObjectProperties.ps1
if ($PSVersionTable.PSVersion.Major -ge 5) { New-Alias -Name Use-ExcelData -Value "ConvertFrom-ExcelData" -Force
. $PSScriptRoot\Plot.ps1
Function New-Plot { if ($PSVersionTable.PSVersion.Major -ge 5) {
[OutputType([PSPlot])] . $PSScriptRoot\Plot.ps1
Param()
Function New-Plot {
Param()
[PSPlot]::new()
}
[PSPlot]::new()
} }
else {
} Write-Warning 'PowerShell 5 is required for plot.ps1'
else { Write-Warning 'PowerShell Excel is ready, except for that functionality'
Write-Warning 'PowerShell 5 is required for plot.ps1' }
Write-Warning 'PowerShell Excel is ready, except for that functionality' #endregion
}
Function Import-Excel { Function Import-Excel {
<# <#
.SYNOPSIS .SYNOPSIS
Create custom objects from the rows in an Excel worksheet. Create custom objects from the rows in an Excel worksheet.
.DESCRIPTION .DESCRIPTION
The Import-Excel cmdlet creates custom objects from the rows in an Excel worksheet. Each row represents one object. All of this is possible without installing Microsoft Excel and by using the .NET library <EFBFBD>EPPLus.dll<EFBFBD>. The Import-Excel cmdlet creates custom objects from the rows in an Excel worksheet. Each row represents one object. All of this is possible without installing Microsoft Excel and by using the .NET library EPPLus.dll.
By default, the property names of the objects are retrieved from the column headers. Because an object cannot have a blanc property name, only columns with column headers will be imported. By default, the property names of the objects are retrieved from the column headers. Because an object cannot have a blanc property name, only columns with column headers will be imported.
If the default behavior is not desired and you want to import the complete worksheet <EFBFBD>as is<EFBFBD>, the parameter <EFBFBD>-NoHeader<EFBFBD> can be used. In case you want to provide your own property names, you can use the parameter <EFBFBD>-HeaderName<EFBFBD>. If the default behavior is not desired and you want to import the complete worksheet as is, the parameter -NoHeader can be used. In case you want to provide your own property names, you can use the parameter -HeaderName.
.PARAMETER Path .PARAMETER Path
Specifies the path to the Excel file. Specifies the path to the Excel file.
.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. 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.
.PARAMETER DataOnly .PARAMETER DataOnly
Import only rows and columns that contain data, empty rows and empty columns are not imported. Import only rows and columns that contain data, empty rows and empty columns are not imported.
.PARAMETER HeaderName .PARAMETER HeaderName
Specifies custom property names to use, instead of the values defined in the column headers of the TopRow. 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 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. 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 .PARAMETER NoHeader
Automatically generate property names (P1, P2, P3, ..) instead of the ones defined in the column headers of the TopRow. 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 <EFBFBD>as is<EFBFBD> and are not concerned with the property names. This switch is best used when you want to import the complete worksheet as is and are not concerned with the property names.
.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. 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 <EFBFBD>-NoHeader<EFBFBD> and <EFBFBD>-HeaderName<EFBFBD> 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. 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 Password .PARAMETER EndRow
Accepts a string that will be used to open a password protected Excel file. By default all rows up to the last cell in the sheet will be imported. If specified, import stops at this row.
.PARAMETER Password
Accepts a string that will be used to open a password protected Excel file.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the column names defined in the first row. In case a column doesnt have a column header (usually in row 1 when -StartRow is not used), then the unnamed columns will be skipped and the data in those columns will not be imported.
----------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------
| A B C |
|1 First Name Address |
|2 Chuck Norris California |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors
First Name: Chuck
Address : California
First Name: Jean-Claude
Address : Brussels
Notice that column 'B' is not imported because there's no value in cell 'B1' that can be used as property name for the objects.
.EXAMPLE
Import the complete Excel worksheet as is by using the -NoHeader switch. One object is created for each row. The property names of the objects will be automatically generated (P1, P2, P3, ..).
----------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------
| A B C |
|1 First Name Address |
|2 Chuck Norris California |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -NoHeader
P1: First Name
P2:
P3: Address
P1: Chuck
P2: Norris
P3: California
P1: Jean-Claude
P2: Vandamme
P3: Brussels
Notice that the column header (row 1) is imported as an object too.
.EXAMPLE .EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the column names defined in the first row. In case a column doesn<73>t have a column header (usually in row 1 when <20>-StartRow<6F> is not used), then the unnamed columns will be skipped and the data in those columns will not be imported. Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the names defined in the parameter -HeaderName. The properties are named starting from the most left column (A) to the right. In case no value is present in one of the columns, that property will have an empty value.
---------------------------------------------- ----------------------------------------------------------
| File: Movies.xlsx - Sheet: Actors | | File: Movies.xlsx - Sheet: Movies |
---------------------------------------------- ----------------------------------------------------------
| A B C | | A B C D |
|1 First Name Address | |1 The Bodyguard 1992 9 |
|2 Chuck Norris California | |2 The Matrix 1999 8 |
|3 Jean-Claude Vandamme Brussels | |3 |
---------------------------------------------- |4 Skyfall 2012 9 |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies -HeaderName 'Movie name', 'Year', 'Rating', 'Genre'
First Name: Chuck Movie name: The Bodyguard
Address : California Year : 1992
Rating : 9
Genre :
First Name: Jean-Claude Movie name: The Matrix
Address : Brussels Year : 1999
Rating : 8
Genre :
Notice that column 'B' is not imported because there's no value in cell 'B1' that can be used as property name for the objects. Movie name:
Year :
Rating :
Genre :
Movie name: Skyfall
Year : 2012
Rating : 9
Genre :
Notice that empty rows are imported and that data for the property 'Genre' is not present in the worksheet. As such, the 'Genre' property will be blanc for all objects.
.EXAMPLE .EXAMPLE
Import the complete Excel worksheet <20>as is<69> by using the <20>-NoHeader<65> switch. One object is created for each row. The property names of the objects will be automatically generated (P1, P2, P3, ..). Import data from an Excel worksheet. One object is created for each row. The property names of the objects are automatically generated by using the switch -NoHeader (P1, P@, P#, ..). The switch -DataOnly will speed up the import because empty rows and empty columns are not imported.
---------------------------------------------- ----------------------------------------------------------
| File: Movies.xlsx - Sheet: Actors | | File: Movies.xlsx - Sheet: Movies |
---------------------------------------------- ----------------------------------------------------------
| A B C | | A B C D |
|1 First Name Address | |1 The Bodyguard 1992 9 |
|2 Chuck Norris California | |2 The Matrix 1999 8 |
|3 Jean-Claude Vandamme Brussels | |3 |
---------------------------------------------- |4 Skyfall 2012 9 |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -NoHeader PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies NoHeader -DataOnly
P1: First Name P1: The Bodyguard
P2: P2: 1992
P3: Address P3: 9
P1: Chuck P1: The Matrix
P2: Norris P2: 1999
P3: California P3: 8
P1: Jean-Claude P1: Skyfall
P2: Vandamme P2: 2012
P3: Brussels P3: 9
Notice that the column header (row 1) is imported as an object too. Notice that empty rows and empty columns are not imported.
.EXAMPLE .EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the names defined in the parameter <20>-HeaderName<6D>. The properties are named starting from the most left column (A) to the right. In case no value is present in one of the columns, that property will have an empty value. Import data from an Excel worksheet. One object is created for each row. The property names are provided with the -HeaderName parameter. The import will start from row 2 and empty columns and rows are not imported.
---------------------------------------------------------- ----------------------------------------------------------
| File: Movies.xlsx - Sheet: Movies | | File: Movies.xlsx - Sheet: Actors |
---------------------------------------------------------- ----------------------------------------------------------
| A B C D | | A B C D |
|1 The Bodyguard 1992 9 | |1 Chuck Norris California |
|2 The Matrix 1999 8 | |2 |
|3 | |3 Jean-Claude Vandamme Brussels |
|4 Skyfall 2012 9 | ----------------------------------------------------------
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies -HeaderName 'Movie name', 'Year', 'Rating', 'Genre' PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -DataOnly -HeaderName 'FirstName', 'SecondName', 'City' StartRow 2
Movie name: The Bodyguard FirstName : Jean-Claude
Year : 1992 SecondName: Vandamme
Rating : 9 City : Brussels
Genre :
Movie name: The Matrix Notice that only 1 object is imported with only 3 properties. Column B and row 2 are empty and have been disregarded by using the switch '-DataOnly'. The property names have been named with the values provided with the parameter '-HeaderName'. Row number 1 with Chuck Norris has not been imported, because we started the import from row 2 with the parameter -StartRow 2.
Year : 1999
Rating : 8
Genre :
Movie name: .LINK
Year : https://github.com/dfinke/ImportExcel
Rating :
Genre :
Movie name: Skyfall .NOTES
Year : 2012 #>
Rating : 9
Genre :
Notice that empty rows are imported and that data for the property 'Genre' is not present in the worksheet. As such, the 'Genre' property will be blanc for all objects.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects are automatically generated by using the switch <20>-NoHeader<65> (P1, P@, P#, ..). The switch <20>-DataOnly<6C> will speed up the import because empty rows and empty columns are not imported.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Movies |
----------------------------------------------------------
| A B C D |
|1 The Bodyguard 1992 9 |
|2 The Matrix 1999 8 |
|3 |
|4 Skyfall 2012 9 |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies <20>NoHeader -DataOnly
P1: The Bodyguard
P2: 1992
P3: 9
P1: The Matrix
P2: 1999
P3: 8
P1: Skyfall
P2: 2012
P3: 9
Notice that empty rows and empty columns are not imported.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names are provided with the <20>-HeaderName<6D> parameter. The import will start from row 2 and empty columns and rows are not imported.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------------------
| A B C D |
|1 Chuck Norris California |
|2 |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -DataOnly -HeaderName 'FirstName', 'SecondName', 'City' <20>StartRow 2
FirstName : Jean-Claude
SecondName: Vandamme
City : Brussels
Notice that only 1 object is imported with only 3 properties. Column B and row 2 are empty and have been disregarded by using the switch '-DataOnly'. The property names have been named with the values provided with the parameter '-HeaderName'. Row number 1 with <20>Chuck Norris<69> has not been imported, because we started the import from row 2 with the parameter <20>-StartRow 2<>.
.LINK
https://github.com/dfinke/ImportExcel
.NOTES
#>
[CmdLetBinding(DefaultParameterSetName)] [CmdLetBinding(DefaultParameterSetName)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
Param ( Param (
[Alias('FullName')] [Alias('FullName')]
[Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Position=0, Mandatory)] [Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Position=0, Mandatory)]
[ValidateScript({(Test-Path -Path $_ -PathType Leaf) -and ($_ -match '.xls$|.xlsx$')})] [ValidateScript( {(Test-Path -Path $_ -PathType Leaf) -and ($_ -match '.xls$|.xlsx$|.xlsm$')})]
[String]$Path, [String]$Path,
[Alias('Sheet')] [Alias('Sheet')]
[Parameter(Position=1)] [Parameter(Position=1)]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[String]$WorksheetName, [String]$WorksheetName,
[Parameter(ParameterSetName='B', Mandatory)] [Parameter(ParameterSetName='B', Mandatory)]
[String[]]$HeaderName, [String[]]$HeaderName ,
[Parameter(ParameterSetName='C', Mandatory)] [Parameter(ParameterSetName='C', Mandatory)]
[Switch]$NoHeader, [Switch]$NoHeader ,
[Alias('HeaderRow','TopRow')] [Alias('HeaderRow','TopRow')]
[ValidateRange(1, 9999)] [ValidateRange(1, 9999)]
[Int]$StartRow, [Int]$StartRow = 1,
[Alias('StopRow','BottomRow')]
[Int]$EndRow ,
[Alias('LeftColumn')]
[Int]$StartColumn = 1,
[Alias('RightColumn')]
[Int]$EndColumn ,
[Switch]$DataOnly, [Switch]$DataOnly,
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[String]$Password [String]$Password
) )
Begin { Begin {
Function Add-Property { $sw = [System.Diagnostics.Stopwatch]::StartNew()
<#
.SYNOPSIS
Add the property name and value to the hashtable that will create a new object for each row.
#>
Param (
[Parameter(Mandatory)]
[String]$Name,
$Value
)
Try {
$NewRow.$Name = $Value
Write-Verbose "Import cell '$($Worksheet.Cells[$R, $P.Column].Address)' with property name '$Name' and value '$Value'"
}
Catch {
throw "Failed adding the property name '$Name' with value '$Value': $_"
}
}
Function Get-PropertyNames { Function Get-PropertyNames {
<# <#
.SYNOPSIS .SYNOPSIS
@@ -303,7 +303,7 @@ Function Import-Excel {
} }
foreach ($C in $Columns) { foreach ($C in $Columns) {
$Worksheet.Cells[$StartRow,$C] | where {$_.Value} | Select-Object @{N='Column'; E={$C}}, Value $Worksheet.Cells[$StartRow,$C] | Where-Object {$_.Value} | Select-Object @{N='Column'; E={$C}}, Value
} }
} }
} }
@@ -324,12 +324,12 @@ Function Import-Excel {
if ($Password) { if ($Password) {
$Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage $Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage
Try { Try {
$Excel.Load($Stream,$Password) $Excel.Load($Stream,$Password)
} }
Catch { Catch {
throw "Password '$Password' is not correct." throw "Password '$Password' is not correct."
} }
} }
else { else {
$Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream $Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream
@@ -346,74 +346,62 @@ Function Import-Excel {
$Worksheet = $Excel.Workbook.Worksheets | Select-Object -First 1 $Worksheet = $Excel.Workbook.Worksheets | Select-Object -First 1
} }
#endregion #endregion
Write-Debug $sw.Elapsed.TotalMilliseconds
#region Set the top row #region Get rows and columns
if (((-not ($NoHeader -or $HeaderName)) -and ($StartRow -eq 0))) { #If we are doing dataonly it is quicker to work out which rows to ignore before processing the cells.
$StartRow = 1 if (-not $EndRow ) {$EndRow = $Worksheet.Dimension.End.Row }
} if (-not $EndColumn) {$EndColumn = $Worksheet.Dimension.End.Column }
#endregion $endAddress = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$EndRow]C[$EndColumn]",0,0)
if ($DataOnly) {
if (-not ($AllCells = $Worksheet.Cells | where {($_.Start.Row -ge $StartRow)})) { #If we are using headers startrow will be the headerrow so examine data from startRow + 1,
Write-Warning "Worksheet '$WorksheetName' in workbook '$Path' is empty after StartRow '$StartRow'" if ($NoHeader) {$range = "A" + ($StartRow ) + ":" + $endAddress }
else {$range = "A" + ($StartRow + 1 ) + ":" + $endAddress }
#We're going to look at every cell and build 2 hash tables holding rows & columns which contain data.
#Want to Avoid 'select unique' operations & large Sorts, becuse time time taken increases with square
#of number of items (PS uses heapsort at large size). Instead keep a list of what we have seen,
#using Hash tables: "we've seen it" is all we need, no need to worry about "seen it before" / "Seen it many times"
$colHash = @{}
$rowHash = @{}
foreach ($cell in $Worksheet.Cells[$range]) {
if ($cell.Value -ne $null) {$colHash[$cell.Start.Column]=1; $rowHash[$cell.Start.row]=1 }
}
$rows = ( $StartRow..$EndRow ).Where({$rowHash[$_]})
$columns = ($StartColumn..$EndColumn).Where({$colHash[$_]})
} }
else { else {
#region Get rows and columns $Columns = $StartColumn..$EndColumn ; if ($StartColumn -gt $EndColumn) {Write-Warning -Message "Selecting columns $StartColumn to $EndColumn might give odd results."}
if ($DataOnly) { if ($NoHeader) {$Rows = ( $StartRow)..$EndRow ; if ($StartRow -gt $EndRow) {Write-Warning -Message "Selecting rows $StartRow to $EndRow might give odd results."} }
$CellsWithValues = $AllCells | where {$_.Value} else {$Rows = (1 + $StartRow)..$EndRow ; if ($StartRow -ge $EndRow) {Write-Warning -Message "Selecting $StartRow as the header with data in $(1+$StartRow) to $EndRow might give odd results."}}
$Columns = $CellsWithValues.Start.Column | Sort-Object -Unique
$Rows = $CellsWithValues.Start.Row | Sort-Object -Unique
}
else {
$LastColumn = $AllCells.Start.Column | Sort-Object -Unique | Select-Object -Last 1
$Columns = 1..$LastColumn
$LastRow = $AllCells.Start.Row | Sort-Object -Unique | Select-Object -Last 1
$Rows = $StartRow..$LastRow | where {($_ -ge $StartRow) -and ($_ -gt 0)}
}
#endregion
#region Create property names
if ((-not $Columns) -or (-not ($PropertyNames = Get-PropertyNames -Columns $Columns -StartRow $StartRow))) {
throw "No column headers found on top row '$StartRow'. If column headers in the worksheet are not a requirement then please use the '-NoHeader' or '-HeaderName' parameter."
}
if ($Duplicates = $PropertyNames | Group-Object Value | where Count -GE 2) {
throw "Duplicate column headers found on row '$StartRow' in columns '$($Duplicates.Group.Column)'. Column headers must be unique, if this is not a requirement please use the '-NoHeader' or '-HeaderName' parameter."
}
#endregion
#region Filter out rows with data in columns that don't have a column header
if ($DataOnly -and (-not $NoHeader)) {
$Rows = $CellsWithValues.Start | where {$PropertyNames.Column -contains $_.Column} |
Sort-Object Row -Unique | Select-Object -ExpandProperty Row
}
#endregion
#region Filter out the top row when it contains column headers
if (-not ($NoHeader -or $HeaderName)) {
$Rows = $Rows | where {$_ -gt $StartRow}
}
#endregion
if (-not $Rows) {
Write-Warning "Worksheet '$WorksheetName' in workbook '$Path' contains no data in the rows after top row '$StartRow'"
}
else {
#region Create one object per row
foreach ($R in $Rows) {
Write-Verbose "Import row '$R'"
$NewRow = [Ordered]@{}
foreach ($P in $PropertyNames) {
Add-Property -Name $P.Value -Value $Worksheet.Cells[$R, $P.Column].Value
}
[PSCustomObject]$NewRow
}
#endregion
}
} }
#endregion
#region Create property names
if ((-not $Columns) -or (-not ($PropertyNames = Get-PropertyNames -Columns $Columns -StartRow $StartRow))) {
throw "No column headers found on top row '$StartRow'. If column headers in the worksheet are not a requirement then please use the '-NoHeader' or '-HeaderName' parameter."
}
if ($Duplicates = $PropertyNames | Group-Object Value | Where-Object Count -GE 2) {
throw "Duplicate column headers found on row '$StartRow' in columns '$($Duplicates.Group.Column)'. Column headers must be unique, if this is not a requirement please use the '-NoHeader' or '-HeaderName' parameter."
}
#endregion
Write-Debug $sw.Elapsed.TotalMilliseconds
if (-not $Rows) {
Write-Warning "Worksheet '$WorksheetName' in workbook '$Path' contains no data in the rows after top row '$StartRow'"
}
else {
#region Create one object per row
foreach ($R in $Rows) {
Write-Verbose "Import row '$R'"
$NewRow = [Ordered]@{}
foreach ($P in $PropertyNames) {
$NewRow[$P.Value] = $Worksheet.Cells[$R, $P.Column].Value
Write-Verbose "Import cell '$($Worksheet.Cells[$R, $P.Column].Address)' with property name '$($p.Value)' and value '$($Worksheet.Cells[$R, $P.Column].Value)'."
}
[PSCustomObject]$NewRow
}
#endregion
}
Write-Debug $sw.Elapsed.TotalMilliseconds
} }
Catch { Catch {
throw "Failed importing the Excel workbook '$Path' with worksheet '$Worksheetname': $_" throw "Failed importing the Excel workbook '$Path' with worksheet '$Worksheetname': $_"
@@ -434,11 +422,12 @@ function Add-WorkSheet {
[OfficeOpenXml.ExcelPackage] $ExcelPackage, [OfficeOpenXml.ExcelPackage] $ExcelPackage,
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
[string] $WorkSheetname, [string] $WorkSheetname,
[switch] $ClearSheet,
[Switch] $NoClobber [Switch] $NoClobber
) )
$ws = $ExcelPackage.Workbook.Worksheets[$WorkSheetname] $ws = $ExcelPackage.Workbook.Worksheets[$WorkSheetname]
if($ClearSheet -and $ws) {$ExcelPackage.Workbook.Worksheets.Delete($WorkSheetname) ; $ws = $null }
if(!$ws) { if(!$ws) {
Write-Verbose "Add worksheet '$WorkSheetname'" Write-Verbose "Add worksheet '$WorkSheetname'"
$ws=$ExcelPackage.Workbook.Worksheets.Add($WorkSheetname) $ws=$ExcelPackage.Workbook.Worksheets.Add($WorkSheetname)
@@ -488,7 +477,7 @@ function ConvertFrom-ExcelSheet {
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream $xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream
$workbook = $xl.Workbook $workbook = $xl.Workbook
$targetSheets = $workbook.Worksheets | Where {$_.Name -like $SheetName} $targetSheets = $workbook.Worksheets | Where-Object {$_.Name -like $SheetName}
$params = @{} + $PSBoundParameters $params = @{} + $PSBoundParameters
$params.Remove("OutputPath") $params.Remove("OutputPath")
@@ -511,6 +500,7 @@ function ConvertFrom-ExcelSheet {
} }
function Export-MultipleExcelSheets { function Export-MultipleExcelSheets {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
param( param(
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
$Path, $Path,

View File

@@ -20,14 +20,19 @@ Begin {
Write-Verbose "$ModuleName module installation started" Write-Verbose "$ModuleName module installation started"
$Files = @( $Files = @(
'AddConditionalFormatting.ps1',
'Charting.ps1', 'Charting.ps1',
'ColorCompletion.ps1',
'ConvertFromExcelData.ps1', 'ConvertFromExcelData.ps1',
'ConvertFromExcelToSQLInsert.ps1', 'ConvertFromExcelToSQLInsert.ps1',
'ConvertExcelToImageFile.ps1',
'ConvertToExcelXlsx.ps1', 'ConvertToExcelXlsx.ps1',
'Copy-ExcelWorkSheet.ps1', 'Copy-ExcelWorkSheet.ps1',
'EPPlus.dll', 'EPPlus.dll',
'Export-charts.ps1',
'Export-Excel.ps1', 'Export-Excel.ps1',
'Export-ExcelSheet.ps1', 'Export-ExcelSheet.ps1',
'formatting.ps1',
'Get-ExcelColumnName.ps1', 'Get-ExcelColumnName.ps1',
'Get-ExcelSheetInfo.ps1', 'Get-ExcelSheetInfo.ps1',
'Get-ExcelWorkbookInfo.ps1', 'Get-ExcelWorkbookInfo.ps1',
@@ -43,9 +48,14 @@ Begin {
'New-ConditionalText.ps1', 'New-ConditionalText.ps1',
'New-ExcelChart.ps1', 'New-ExcelChart.ps1',
'New-PSItem.ps1', 'New-PSItem.ps1',
'Open-ExcelPackage.ps1',
'Pivot.ps1', 'Pivot.ps1',
'Plot.ps1', 'plot.ps1',
'Send-SqlDataToExcel.ps1',
'Set-CellStyle.ps1', 'Set-CellStyle.ps1',
'Set-Column.ps1',
'Set-Row.ps1',
'SetFormat.ps1',
'TrackingUtils.ps1', 'TrackingUtils.ps1',
'Update-FirstObjectProperties.ps1' 'Update-FirstObjectProperties.ps1'
) )

View File

@@ -22,13 +22,19 @@ Begin {
'*.dll', '*.dll',
'*.psd1', '*.psd1',
'*.psm1', '*.psm1',
'AddConditionalFormatting.ps1',
'Charting.ps1', 'Charting.ps1',
'ColorCompletion.ps1',
'Compare-Worksheet.ps1',
'ConvertFromExcelData.ps1', 'ConvertFromExcelData.ps1',
'ConvertFromExcelToSQLInsert.ps1', 'ConvertFromExcelToSQLInsert.ps1',
'ConvertExcelToImageFile.ps1',
'ConvertToExcelXlsx.ps1', 'ConvertToExcelXlsx.ps1',
'Copy-ExcelWorkSheet.ps1', 'Copy-ExcelWorkSheet.ps1',
'Export-Charts.ps1',
'Export-Excel.ps1', 'Export-Excel.ps1',
'Export-ExcelSheet.ps1', 'Export-ExcelSheet.ps1',
'formatting.ps1',
'Get-ExcelColumnName.ps1', 'Get-ExcelColumnName.ps1',
'Get-ExcelSheetInfo.ps1', 'Get-ExcelSheetInfo.ps1',
'Get-ExcelWorkbookInfo.ps1', 'Get-ExcelWorkbookInfo.ps1',
@@ -38,13 +44,18 @@ Begin {
'Import-Html.ps1', 'Import-Html.ps1',
'InferData.ps1', 'InferData.ps1',
'Invoke-Sum.ps1', 'Invoke-Sum.ps1',
'New-ConditionalText.ps1',
'New-ConditionalFormattingIconSet.ps1', 'New-ConditionalFormattingIconSet.ps1',
'New-ConditionalText.ps1',
'New-ExcelChart.ps1', 'New-ExcelChart.ps1',
'New-PSItem.ps1', 'New-PSItem.ps1',
'Open-ExcelPackage.ps1',
'Pivot.ps1', 'Pivot.ps1',
'Plot.ps1', 'Plot.ps1',
'Send-SQLDataToExcel.ps1',
'Set-CellStyle.ps1', 'Set-CellStyle.ps1',
'Set-Column.ps1',
'Set-Row.ps1',
'SetFormat.ps1',
'TrackingUtils.ps1', 'TrackingUtils.ps1',
'Update-FirstObjectProperties.ps1' 'Update-FirstObjectProperties.ps1'
) )

471
Merge-worksheet.ps1 Normal file
View File

@@ -0,0 +1,471 @@
Function Merge-Worksheet {
<#
.Synopsis
Merges two worksheets (or other objects) into a single worksheet with differences marked up.
.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
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
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.
.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.
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.
This version uses aliases to shorten the parameters,
(OutputFileName can be "outFile" and the sheet "OutSheet" : DifferenceObject & ReferenceObject can be DiffObject & RefObject)
#>
[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 ,
#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,
#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,
#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,
[parameter(ParameterSetName='D',Mandatory=$true)]
[parameter(ParameterSetName='E',Mandatory=$true)]
[Alias('RefObject')]
$ReferenceObject ,
[parameter(ParameterSetName='D',Mandatory=$true,Position=1)]
[Alias('DiffObject')]
$DifferenceObject ,
[parameter(ParameterSetName='D',Position=2)]
[parameter(ParameterSetName='E',Position=3)]
$DiffPrefix = "=>" ,
#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)]
[Alias('OutSheet')]
$OutputSheetName = "Sheet1",
#Properties to include in the DIFF - supports wildcards, default is "*".
$Property = "*" ,
#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.
[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.
[switch]$HideEqual ,
#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.
[Switch]$Show
)
#region Read Excel data
if ($Referencefile -and $Differencefile) {
#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.
if ($onefile -and ( ($WorkSheetName.count -ne 2) -or $WorkSheetName[0] -eq $WorkSheetName[1] ) ) {
Write-Warning -Message "If both the Reference and difference file are the same then worksheet name must provide 2 different names"
return
}
if ($WorkSheetName.count -eq 2) {$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 }
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
}
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 ;}
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$",""
}
if ($NoHeader) {$firstDataRow = $Startrow } else {$firstDataRow = $Startrow + 1}
}
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 = @()
$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"}
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}) }
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 " + ")
#Build the list of the properties to output, in order.
$diffpart = @()
$refpart = @()
foreach ($p in $proplist.Where({$key -ne $_}) ) {$refPart += $p ; $diffPart += "$DiffPrefix $p" }
#Last reference column will be A if there the only one property (which might be the key), B if there are two properties, C if there are 3 etc
$lastRefCol = [char](64 + $propList.count)
#First difference column will be the next one (we'll trap the case of only having the key later)
$FirstDiffCol = [char](65 + $propList.count)
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
$lastDiffCol = [char](63 + 2 * $propList.count)
}
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
$lastDiffCol = [char](64 + 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
#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 = @{}
$rowNo = $firstDataRow
foreach ($row in $ReferenceObject) {
if ($row._row -eq $null) {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
foreach ($row in $DifferenceObject) {
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
}
else {$Rowhash[$row.$key] = $rowNo }
$rowNo ++
}
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.
$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]@{}
foreach ($result in $_.Group) {
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 ($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'
}
else {
$hash["$DiffPrefix is"] = 'Changed'
}
}
'<=' { $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
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)
{$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" ))})
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
}
elseif ( $HideEqual ) {$ws.row($i+2).hidden = $true }
if ( $expandedDiff[$i].side -eq "<>" ) {
$range = $ws.Dimension -replace "\d+", ($i + 2 )
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $ChangeBackgroundColor
}
elseif ( $expandedDiff[$i].side -eq "<=" ) {
$range = "A" + ($i + 2 ) + ":" + $lastRefCol + ($i + 2 )
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $DeleteBackgroundColor
}
elseif ( $expandedDiff[$i].side -eq "=>" ) {
if ($propList.count -gt 1) {
$range = $FirstDiffCol + ($i + 2 ) + ":" + $lastDiffCol + ($i + 2 )
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $AddBackgroundColor
}
Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -BackgroundColor $AddBackgroundColor
}
}
Close-ExcelPackage -ExcelPackage $xl -Show:$Show
}
}
Function Merge-MulipleSheets {
<#
.Synopsis
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
it 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
Then it calls merge-worsheet with this result and sheet B, comparing 'Name', 'Displayname' and 'Start mode' columns on each side and outputting
_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 "DisplayName" and "Start" are renamed "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,
Sheet C will be processed and that row and nothing will 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 of 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
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
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.
#>
param (
[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,
#Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
[String[]]$Headername,
#Automatically generate property names (P1, P2, P3, ..) instead of the using the values the top row of the sheet.
[switch]$NoHeader,
#Name(s) of worksheets to compare,
$WorkSheetName = "Sheet1",
#File to write output to
[Alias('OutFile')]
$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.
$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.
[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
[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.
[Switch]$Show
)
begin { $filestoProcess = @() }
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
$WorksheetName = $excel.Workbook.Worksheets.Name.where({$_ -like $WorkSheetName})
Close-ExcelPackage -NoSave -ExcelPackage $excel
}
#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}
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) {
Write-Progress -Activity "Merging sheets" -CurrentOperation "Comparing $($filestoProcess[-$nextFileNo]) against $($filestoProcess[0]). "
$merged = Merge-Worksheet @params -ReferenceObject $merged -Differencefile $filestoProcess[-$nextFileNo]
$nextFileNo ++
}
}
#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}
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) {
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.
else { Write-Warning -Message "Need at least two files to process" ; return }
#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]
#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}
if ($filesToProcess.Count -ge 2) {
$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'
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)
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)
}
#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 "|"
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
}
#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 ",") +")" )
$sheet.view.FreezePanes(2, 3)
}
else {$sheet.view.FreezePanes(2, 2) }
#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 ($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
Write-Progress -Activity "Merging sheets" -Completed
}
}

50
Open-ExcelPackage.ps1 Normal file
View File

@@ -0,0 +1,50 @@
Function Open-ExcelPackage {
<#
.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)
close-ExcelPackage $excel -Show
This will open the file at $xlPath, select sheet1 apply formatting to two blocks of the sheet and close the package
#>
[OutputType([OfficeOpenXml.ExcelPackage])]
Param ([Parameter(Mandatory=$true)]$Path,
[switch]$KillExcel)
if($KillExcel) {
Get-Process -Name "excel" -ErrorAction Ignore | Stop-Process
while (Get-Process -Name "excel" -ErrorAction Ignore) {}
}
$Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
if (Test-Path $path) {New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $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 as required
#>
Param (
#File to close
[parameter(Mandatory=$true, ValueFromPipeline=$true)]
[OfficeOpenXml.ExcelPackage]$ExcelPackage,
#Open the file
[switch]$Show,
#Abandon the file without saving
[Switch]$NoSave,
#Save file with a new name (ignored if -NoSaveSpecified)
$SaveAs
)
if ( $NoSave) {$ExcelPackage.Dispose()}
else {
if ($SaveAs) {$ExcelPackage.SaveAs( $SaveAs ) }
Else {$ExcelPackage.Save(); $SaveAs = $ExcelPackage.File.FullName }
$ExcelPackage.Dispose()
if ($show) {Start-Process -FilePath $SaveAs }
}
}

177
README.md
View File

@@ -1,10 +1,19 @@
PowerShell Import-Excel PowerShell Import-Excel
- -
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-objects thanks to the .NET EPPlus DLL (http://epplus.codeplex.com/) which is included in the module. Creating Tables, Pivot Tables, Charts and much more has just become a lot easier. ## Build Status
[![Build status](https://ci.appveyor.com/api/projects/status/21hko6eqtpccrkba?svg=true)](https://ci.appveyor.com/project/dfinke/importexcel)
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) ![](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 Installation
- -
#### [PowerShell V5](https://www.microsoft.com/en-us/download/details.aspx?id=50395) and Later #### [PowerShell V5](https://www.microsoft.com/en-us/download/details.aspx?id=50395) and Later
@@ -27,6 +36,172 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi
``` ```
# What's new # What's new
#### 06/08/2018
Thank you again to [James O'Neill](https://twitter.com/jamesoneill) for the lion share of these updates.
Most notably the performance gains in the `Import-Excel` function.
In addition, I have started to create tests and wired up Appveyor for continuous integration
This release will be bumped to 5.0.0 and will be published officially after more testing.
* added databar to Examples
* Fix databar example
* increased how long the import should take
* Renamed test directory
* Added PSVersion. Point to the new directory for tests
* Added EndRow, StartColumn, EndColumn to Import-Excel
* Added Merge-MultipleSheets to argument completers
* added build badge
* Making Merge-Worksheet, and Merge-MultipleWorksheet ready to release
* Added Merge Multiple worksheet
* Revert "Added Multiple Merge to Merge-Worksheet.ps1"
* Added Multiple Merge to Merge-Worksheet.ps1
* Added Merge-worksheet
* Force install of pester
* Checks version of pester on appveyor
* First step to wire up appveyor
* 13 days ago : Tidying of case, parameter clarity, removal of aliases. Added timeout to send-SqlDataToExcel Added Merge WorkSheet Fixed bugs in Compare-Worksheet
#### 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 #### 10/2/2017
Thanks to [Jeremy Brun](https://github.com/jeremytbrun) Thanks to [Jeremy Brun](https://github.com/jeremytbrun)
Fixed issues related to use of -Title parameter combined with column formatting parameters. Fixed issues related to use of -Title parameter combined with column formatting parameters.

35
RemoveWorksheet.ps1 Normal file
View File

@@ -0,0 +1,35 @@
Function Remove-WorkSheet {
Param (
$Path,
$WorksheetName
)
$Path = (Resolve-Path $Path).ProviderPath
$Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage $Path
$workSheet = $Excel.Workbook.Worksheets[$WorkSheetName]
if($workSheet) {
if($Excel.Workbook.Worksheets.Count -gt 1) {
$Excel.Workbook.Worksheets.Delete($workSheet)
} else {
throw "Cannot delete $WorksheetName. A workbook must contain at least one visible worksheet"
}
} else {
throw "$WorksheetName not found"
}
$Excel.Save()
$Excel.Dispose()
}
cls
ipmo .\ImportExcel.psd1 -Force
$names = Get-ExcelSheetInfo C:\Temp\testDelete.xlsx
$names | % { Remove-WorkSheet C:\Temp\testDelete.xlsx $_.Name}
##Remove-WorkSheet C:\Temp\testDelete.xlsx sheet6

139
Send-SqlDataToExcel.ps1 Normal file
View File

@@ -0,0 +1,139 @@
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
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
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 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
[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)]
[string]$SQL,
#Override the default query time of 30 seconds.
[int]$QueryTimeout,
#File name for the Excel File
$Path,
[String]$WorkSheetname = 'Sheet1',
[Switch]$KillExcel,
#If Specified, open the file created.
[Switch]$Show,
[String]$Title,
[OfficeOpenXml.Style.ExcelFillStyle]$TitleFillPattern = 'None',
[Switch]$TitleBold,
[Int]$TitleSize = 22,
[System.Drawing.Color]$TitleBackgroundColor,
[String]$Password,
[String[]]$PivotRows,
[String[]]$PivotColumns,
$PivotData,
[Switch]$PivotDataToColumn,
[Hashtable]$PivotTableDefinition,
[Switch]$IncludePivotChart,
[OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie',
[Switch]$NoLegend,
[Switch]$ShowCategory,
[Switch]$ShowPercent,
[Switch]$AutoSize,
[Switch]$FreezeTopRow,
[Switch]$FreezeFirstColumn,
[Switch]$FreezeTopRowFirstColumn,
[Int[]]$FreezePane,
[Switch]$AutoFilter,
[Switch]$BoldTopRow,
[Switch]$NoHeader,
[String]$RangeName,
[String]$TableName,
[OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6',
[Object[]]$ExcelChartDefinition,
[Switch]$AutoNameRange,
[Object[]]$ConditionalFormat,
[Object[]]$ConditionalText,
[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]$Passthru
)
if ($KillExcel) {
Get-Process excel -ErrorAction Ignore | Stop-Process
while (Get-Process excel -ErrorAction Ignore) {}
}
#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 ($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 ($DataBase) {$Session.ChangeDatabase($DataBase) }
}
elseif ($Connection) {
$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)
}
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)"
#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.
if ($Connection) {$Session.close() }
}

139
Set-Column.ps1 Normal file
View File

@@ -0,0 +1,139 @@
Function Set-Column {
<#
.SYNOPSIS
Adds a column to the existing data area in an Excel sheet, fills values and sets formatting
.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
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()]
Param (
[Parameter(ParameterSetName="Package",Mandatory=$true)]
[OfficeOpenXml.ExcelPackage]$ExcelPackage,
#Sheet to update
[Parameter(ParameterSetName="Package")]
$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 ,
[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)]
$Value ,
#Optional column heading
$Heading ,
#Number format to apply to cells e.g. "dd/MM/yyyy HH:mm", "£#,##0.00;[Red]-£#,##0.00", "0.00%" , "##/##" , "0.0E+0" etc
[Alias("NFormat")]
$NumberFormat,
#Style of border to draw around the row
[OfficeOpenXml.Style.ExcelBorderStyle]$BorderAround,
#Colour for the text - if none specified it will be left as it it is
[System.Drawing.Color]$FontColor,
#Make text bold
[switch]$Bold,
#Make text italic
[switch]$Italic,
#Underline the text using the underline style in -underline type
[switch]$Underline,
#Should Underline use single or double, normal or accounting mode : default is single normal
[OfficeOpenXml.Style.ExcelUnderLineType]$UnderLineType = [OfficeOpenXml.Style.ExcelUnderLineType]::Single,
#StrikeThrough text
[switch]$StrikeThru,
#Subscript or superscript
[OfficeOpenXml.Style.ExcelVerticalAlignmentFont]$FontShift,
#Font to use - Excel defaults to Calibri
[String]$FontName,
#Point size for the text
[float]$FontSize,
#Change background colour
[System.Drawing.Color]$BackgroundColor,
#Background pattern - solid by default
[OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid ,
#Secondary colour for background pattern
[Alias("PatternColour")]
[System.Drawing.Color]$PatternColor,
#Turn on text wrapping
[switch]$WrapText,
#Position cell contents to left, right or centre ...
[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.
[ValidateRange(-90, 90)]
[int]$TextRotation ,
#Autofit cells to width
[Alias("AutoFit")]
[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
[Switch]$AutoNameRange,
[switch]$PassThru
)
#if we were passed a package object and a worksheet name , get the worksheet.
if ($ExcelPackage) {$Worksheet = $ExcelPackage.Workbook.Worksheets[$Worksheetname] }
#In a script block to build a formula, we may want any of corners or the columnname,
#if column and startrow aren't specified, assume first unused column, and first row
if (-not $StartRow) {$startRow = $Worksheet.Dimension.Start.Row }
$StartColumn = $Worksheet.Dimension.Start.Column
$endColumn = $Worksheet.Dimension.End.Column
$endRow = $Worksheet.Dimension.End.Row
if ($Column -lt 2 ) {$Column = $endColumn + 1 }
$ColumnName = [OfficeOpenXml.ExcelCellAddress]::new(1,$column).Address -replace "1",""
Write-Verbose -Message "Updating Column $ColumnName"
#If there is a heading, insert it and use it as the name for a range (if we're creating one)
if ($Heading) {
$Worksheet.Cells[$StartRow, $Column].Value = $heading
$startRow ++
if ($AutoNameRange) { $Worksheet.Names.Add( $heading, ($Worksheet.Cells[$startrow, $Column, $endRow, $Column]) ) | Out-Null }
}
#Fill in the data
if ($value) { foreach ($row in ($StartRow.. $endRow)) {
if ($Value -is [scriptblock]) { #re-create the script block otherwise variables from this function are out of scope.
$cellData = & ([scriptblock]::create( $Value ))
Write-Verbose -Message $cellData
}
else { $cellData = $Value}
if ($cellData -match "^=") { $Worksheet.Cells[$Row, $Column].Formula = $cellData }
else { $Worksheet.Cells[$Row, $Column].Value = $cellData }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } # This is not a custom format, but a preset recognized as date and localized.
}}
#region Apply formatting
if ($Underline) {
$Worksheet.Column( $Column).Style.Font.UnderLine = $true
$Worksheet.Column( $Column).Style.Font.UnderLineType = $UnderLineType
}
if ($Bold) { $Worksheet.Column( $Column).Style.Font.Bold = $true }
if ($Italic) { $Worksheet.Column( $Column).Style.Font.Italic = $true }
if ($StrikeThru) { $Worksheet.Column( $Column).Style.Font.Strike = $true }
if ($FontShift) { $Worksheet.Column( $Column).Style.Font.VerticalAlign = $FontShift }
if ($NumberFormat) { $Worksheet.Column( $Column).Style.Numberformat.Format = $NumberFormat }
if ($TextRotation) { $Worksheet.Column( $Column).Style.TextRotation = $TextRotation }
if ($WrapText) { $Worksheet.Column( $Column).Style.WrapText = $true }
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 ($BackgroundColor) {
$Worksheet.Column( $Column).Style.Fill.PatternType = $BackgroundPattern
$Worksheet.Column( $Column).Style.Fill.BackgroundColor.SetColor($BackgroundColor )
if ($PatternColor) { $Worksheet.Column( $Column).Style.Fill.PatternColor.SetColor( $PatternColor ) }
}
if ($Autosize) { $Worksheet.Column( $Column).AutoFit() }
elseif ($Width) { $Worksheet.Column( $Column).Width = $Width }
#endregion
#return the new data if -passthru was specified.
if ($passThru) { $Worksheet.Column( $Column)}
}

142
Set-Row.ps1 Normal file
View File

@@ -0,0 +1,142 @@
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)" }
$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)]
[OfficeOpenXml.ExcelPackage]$ExcelPackage,
#the name to update in the package
[Parameter(ParameterSetName="Package")]
$Worksheetname = "Sheet1",
#A worksheet object
[Parameter(ParameterSetName="sheet",Mandatory=$true)]
[OfficeOpenXml.Excelworksheet]
$Worksheet,
#Row to fill right - first row is 1. 0 will be interpreted as first unused row
$Row = 0 ,
#Position in the row to start from
[Int]$StartColumn,
#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)]
$Value,
#Optional Row heading
$Heading ,
#Number format to apply to cells e.g. "dd/MM/yyyy HH:mm", "£#,##0.00;[Red]-£#,##0.00", "0.00%" , "##/##" , "0.0E+0" etc
[Alias("NFormat")]
$NumberFormat,
#Style of border to draw around the row
[OfficeOpenXml.Style.ExcelBorderStyle]$BorderAround,
#Colour for the text - if none specified it will be left as it it is
[System.Drawing.Color]$FontColor,
#Make text bold
[switch]$Bold,
#Make text italic
[switch]$Italic,
#Underline the text using the underline style in -underline type
[switch]$Underline,
#Should Underline use single or double, normal or accounting mode : default is single normal
[OfficeOpenXml.Style.ExcelUnderLineType]$UnderLineType = [OfficeOpenXml.Style.ExcelUnderLineType]::Single,
#StrikeThrough text
[switch]$StrikeThru,
#Subscript or superscript
[OfficeOpenXml.Style.ExcelVerticalAlignmentFont]$FontShift,
#Font to use - Excel defaults to Calibri
[String]$FontName,
#Point size for the text
[float]$FontSize,
#Change background colour
[System.Drawing.Color]$BackgroundColor,
#Background pattern - solid by default
[OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid ,
#Secondary colour for background pattern
[Alias("PatternColour")]
[System.Drawing.Color]$PatternColor,
#Turn on text wrapping
[switch]$WrapText,
#Position cell contents to left, right or centre ...
[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.
[ValidateRange(-90, 90)]
[int]$TextRotation ,
#Set cells to a fixed hieght
[float]$Height,
[switch]$PassThru
)
#if we were passed a package object and a worksheet name , get the worksheet.
if ($ExcelPackage) {$Worksheet = $ExcelPackage.Workbook.worksheets[$Worksheetname] }
#In a script block to build a formula, we may want any of corners or the columnname,
#if row and start column aren't specified assume first unused row, and first column
if (-not $StartColumn) {$StartColumn = $Worksheet.Dimension.Start.Column }
$startRow = $Worksheet.Dimension.Start.Row + 1
$endColumn = $Worksheet.Dimension.End.Column
$endRow = $Worksheet.Dimension.End.Row
if ($Row -lt 2 ) {$Row = $endRow + 1 }
Write-Verbose -Message "Updating Row $Row"
#Add a row label
if ($Heading) {
$Worksheet.Cells[$Row, $StartColumn].Value = $Heading
$StartColumn ++
}
#Fill in the data
if ($value) {foreach ($column in ($StartColumn..$EndColumn)) {
#We might want the column name in a script block
$ColumnName = [OfficeOpenXml.ExcelCellAddress]::new(1,$column).Address -replace "1",""
if ($Value -is [scriptblock] ) {
#re-create the script block otherwise variables from this function are out of scope.
$cellData = & ([scriptblock]::create( $Value ))
Write-Verbose -Message $cellData
}
else{$cellData = $Value}
if ($cellData -match "^=") { $Worksheet.Cells[$Row, $column].Formula = $cellData }
else { $Worksheet.Cells[$Row, $Column].Value = $cellData }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } # This is not a custom format, but a preset recognized as date and localized.
}}
#region Apply formatting
if ($Underline) {
$worksheet.row( $Row ).Style.Font.UnderLine = $true
$worksheet.row( $Row ).Style.Font.UnderLineType = $UnderLineType
}
if ($Bold) { $worksheet.row( $Row ).Style.Font.Bold = $true }
if ($Italic) { $worksheet.row( $Row ).Style.Font.Italic = $true }
if ($StrikeThru) { $worksheet.row( $Row ).Style.Font.Strike = $true }
if ($FontShift) { $worksheet.row( $Row ).Style.Font.VerticalAlign = $FontShift }
if ($NumberFormat) { $worksheet.row( $Row ).Style.Numberformat.Format = $NumberFormat }
if ($TextRotation) { $worksheet.row( $Row ).Style.TextRotation = $TextRotation }
if ($WrapText) { $worksheet.row( $Row ).Style.WrapText = $true }
if ($HorizontalAlignment) { $worksheet.row( $Row ).Style.HorizontalAlignment = $HorizontalAlignment}
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 ($BackgroundColor) {
$worksheet.row( $Row ).Style.Fill.PatternType = $BackgroundPattern
$worksheet.row( $Row ).Style.Fill.BackgroundColor.SetColor($BackgroundColor )
if ($PatternColor) { $worksheet.row( $Row ).Style.Fill.PatternColor.SetColor( $PatternColor ) }
}
#endregion
#return the new data if -passthru was specified.
if ($passThru) {$Worksheet.Row($Row)}
}

191
SetFormat.ps1 Normal file
View File

@@ -0,0 +1,191 @@
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
#>
Param (
#One or more row(s), Column(s) and/or block(s) of cells to format
[Parameter(ValueFromPipeline = $true,ParameterSetName="Address",Mandatory=$True)]
$Address ,
#The worksheet where the format is to be applied
[Parameter(ParameterSetName="SheetAndRange",Mandatory=$True)]
[OfficeOpenXml.ExcelWorksheet]$WorkSheet ,
#The area of the worksheet where the format is to be applied
[Parameter(ParameterSetName="SheetAndRange",Mandatory=$True)]
[OfficeOpenXml.ExcelAddress]$Range,
#Number format to apply to cells e.g. "dd/MM/yyyy HH:mm", "£#,##0.00;[Red]-£#,##0.00", "0.00%" , "##/##" , "0.0E+0" etc
[Alias("NFormat")]
$NumberFormat,
#Style of border to draw around the range
[OfficeOpenXml.Style.ExcelBorderStyle]$BorderAround,
[System.Drawing.Color]$BorderColor=[System.Drawing.Color]::Black,
[OfficeOpenXml.Style.ExcelBorderStyle]$BorderBottom,
[OfficeOpenXml.Style.ExcelBorderStyle]$BorderTop,
[OfficeOpenXml.Style.ExcelBorderStyle]$BorderLeft,
[OfficeOpenXml.Style.ExcelBorderStyle]$BorderRight,
#Colour for the text - if none specified it will be left as it it is
[System.Drawing.Color]$FontColor,
#Value for the cell
$Value,
#Formula for the cell
$Formula,
#Clear Bold, Italic, StrikeThrough and Underline and set colour to black
[switch]$ResetFont,
#Make text bold
[switch]$Bold,
#Make text italic
[switch]$Italic,
#Underline the text using the underline style in -underline type
[switch]$Underline,
#Should Underline use single or double, normal or accounting mode : default is single normal
[OfficeOpenXml.Style.ExcelUnderLineType]$UnderLineType = [OfficeOpenXml.Style.ExcelUnderLineType]::Single,
#StrikeThrough text
[switch]$StrikeThru,
#Subscript or superscript
[OfficeOpenXml.Style.ExcelVerticalAlignmentFont]$FontShift,
#Font to use - Excel defaults to Calibri
[String]$FontName,
#Point size for the text
[float]$FontSize,
#Change background colour
[System.Drawing.Color]$BackgroundColor,
#Background pattern - solid by default
[OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid ,
#Secondary colour for background pattern
[Alias("PatternColour")]
[System.Drawing.Color]$PatternColor,
#Turn on text wrapping
[switch]$WrapText,
#Position cell contents to left, right or centre ...
[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.
[ValidateRange(-90, 90)]
[int]$TextRotation ,
#Autofit cells to width (columns or ranges only)
[Alias("AutoFit")]
[Switch]$AutoSize,
#Set cells to a fixed width (columns or ranges only), ignored if Autosize is specified
[float]$Width,
#Set cells to a fixed hieght (rows or ranges only)
[float]$Height,
#Hide a row or column (not a range)
[switch]$Hidden
)
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]) {
[void]$PSBoundParameters.Remove("Address")
$Address | Set-Format @PSBoundParameters
}
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
}
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 ($BorderBottom) {
$Address.Style.Border.Bottom.Style=$BorderBottom
$Address.Style.Border.Bottom.Color.SetColor($BorderColor)
}
if ($BorderTop) {
$Address.Style.Border.Top.Style=$BorderTop
$Address.Style.Border.Top.Color.SetColor($BorderColor)
}
if ($BorderLeft) {
$Address.Style.Border.Left.Style=$BorderLeft
$Address.Style.Border.Left.Color.SetColor($BorderColor)
}
if ($BorderRight) {
$Address.Style.Border.Right.Style=$BorderRight
$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)
if ($PatternColor) {
$Address.Style.Fill.PatternColor.SetColor( $PatternColor)
}
}
if ($Height) {
if ($Address -is [OfficeOpenXml.ExcelRow] ) {$Address.Height = $Height }
elseif ($Address -is [OfficeOpenXml.ExcelRange] ) {
($Address.Start.Row)..($Address.Start.Row + $Address.Rows) |
ForEach-Object {$Address.WorkSheet.Row($_).Height = $Height }
}
else {Write-Warning -Message ("Can set the height of a row or a range but not a {0} object" -f ($Address.GetType().name)) }
}
if ($Autosize) {
if ($Address -is [OfficeOpenXml.ExcelColumn]) {$Address.AutoFit() }
elseif ($Address -is [OfficeOpenXml.ExcelRange] ) {
$Address.AutoFitColumns()
}
else {Write-Warning -Message ("Can autofit a column or a range but not a {0} object" -f ($Address.GetType().name)) }
}
elseif ($Width) {
if ($Address -is [OfficeOpenXml.ExcelColumn]) {$Address.Width = $Width}
elseif ($Address -is [OfficeOpenXml.ExcelRange] ) {
($Address.Start.Column)..($Address.Start.Column + $Address.Columns - 1) |
ForEach-Object {
#$ws.Column($_).Width = $Width
$Address.Worksheet.Column($_).Width = $Width
}
}
else {Write-Warning -Message ("Can set the width of a column or a range but not a {0} object" -f ($Address.GetType().name)) }
}
if ($Hidden) {
if ($Address -is [OfficeOpenXml.ExcelRow] -or
$Address -is [OfficeOpenXml.ExcelColumn] ) {$Address.Hidden = $True}
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
}
}
}
}

BIN
Testimonials/t1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

1
ToDo.md Normal file
View File

@@ -0,0 +1 @@
- [ ] Create an autocomplete for WorkSheetName param on ImportExcel

Binary file not shown.

View File

@@ -0,0 +1,32 @@
Import-Module $PSScriptRoot\..\..\ImportExcel.psd1
Describe "Tests" {
BeforeAll {
$data = $null
$timer = Measure-Command {
$data = Import-Excel $PSScriptRoot\Simple.xlsx
}
}
It "Should have two items" {
$data.count | Should be 2
}
It "Should have items a and b" {
$data[0].p1 | Should be "a"
$data[1].p1 | Should be "b"
}
It "Should read fast < 1000 milliseconds" {
$timer.TotalMilliseconds | should BeLessThan 1000
}
It "Should read larger xlsx, 4k rows 1 col < 2000 milliseconds" {
$timer = Measure-Command {
$null = Import-Excel $PSScriptRoot\LargerFile.xlsx
}
$timer.TotalMilliseconds | should BeLessThan 2000
}
}

Binary file not shown.

BIN
appveyor.yml Normal file

Binary file not shown.

252
compare-WorkSheet.ps1 Normal file
View File

@@ -0,0 +1,252 @@
Function Compare-WorkSheet {
<#
.Synopsis
Compares two worksheets with the same name in different files.
.Description
This command takes two file names, a worksheet name and a name for a key column.
It reads the worksheet from each file and decides the column names.
It builds as hashtable of the key column values and the rows they appear in
It then uses PowerShell's compare object command to compare the sheets (explicity checking all column names which have not been excluded)
For the difference rows it adds the row number for the key of that row - we have to add the key after doing the comparison,
otherwise rows will be considered as different simply because they have different row numbers
We also add the name of the file in which the difference occurs.
If -BackgroundColor is specified the difference rows will be changed to that background.
.Example
Compare-WorkSheet -Referencefile 'Server56.xlsx' -Differencefile 'Server57.xlsx' -WorkSheetName Products -key IdentifyingNumber -ExcludeProperty Install* | format-table
The two workbooks in this example contain the result of redirecting a subset of properties from Get-WmiObject -Class win32_product to Export-Excel
The command compares the "products" pages in the two workbooks, but we don't want to register a differnce if if the software was installed on a
different date or from a different place, so Excluding Install* removes InstallDate and InstallSource.
This data doesn't have a "name" column" so we specify the "IdentifyingNumber" column as the key.
The results will be presented as a table.
.Example
compare-WorkSheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -GridView
This time two workbooks contain the result of redirecting Get-WmiObject -Class win32_service to Export-Excel
Here the -Differencefile and -Referencefile parameter switches are assumed , and the default setting for -key ("Name") works for services
This will display the differences between the "services" sheets using a grid view
.Example
Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName Services -BackgroundColor lightGreen
This version of the command outputs the differences between the "services" pages and also highlights any different rows in the spreadsheet files.
.Example
Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName Services -BackgroundColor lightGreen -FontColor Red -Show
This builds on the previous example: this time Where two changed rows have the value in the "name" column (the default value for -key),
this version adds highlighting of the changed cells in red; and then opens the Excel file.
.Example
Compare-WorkSheet 'Pester-tests.xlsx' 'Pester-tests.xlsx' -WorkSheetName 'Server1','Server2' -Property "full Description","Executed","Result" -Key "full Description"
This time the reference file and the difference file are the same file and two different sheets are used. Because the tests include the
machine name and time the test was run the command specifies a limited set of columns should be used.
.Example
Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -GridView -ExcludeDifferent
The "General" page has a title and two unlabelled columns with a row forCPU, Memory, Domain, Disk and so on
So the command is instructed to starts at row 2 to skip the title and to name the columns: the first is "label" and the Second "Value";
the label acts as the key. This time we interested the rows which are the same in both sheets,
and the result is displayed using grid view. Note that grid view works best when the number of columns is small.
.Example
Compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -BackgroundColor White -Show -AllDataBackgroundColor LightGray
This version of the previous command lightlights all the cells in lightgray and then sets the changed rows back to white; only
the unchanged rows are highlighted
#>
[cmdletbinding(DefaultParameterSetName)]
Param(
#First file to compare
[parameter(Mandatory=$true,Position=0)]
$Referencefile ,
#Second file to compare
[parameter(Mandatory=$true,Position=1)]
$Differencefile ,
#Name(s) of worksheets to compare.
$WorkSheetName = "Sheet1",
#Properties to include in the DIFF - supports wildcards, default is "*"
$Property = "*" ,
#Properties to exclude from the the search - supports wildcards
$ExcludeProperty ,
#Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
[Parameter(ParameterSetName='B', Mandatory)]
[String[]]$Headername,
#Automatically generate property names (P1, P2, P3, ..) instead of the using the values the top row of the sheet
[Parameter(ParameterSetName='C', Mandatory)]
[switch]$NoHeader,
#The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
[int]$Startrow = 1,
#If specified, highlights all the cells - so you can make Equal cells one colour, and Diff cells another.
[System.Drawing.Color]$AllDataBackgroundColor,
#If specified, highlights the DIFF rows
[System.Drawing.Color]$BackgroundColor,
#If specified identifies the tabs which contain DIFF rows (ignored if -backgroundColor is omitted)
[System.Drawing.Color]$TabColor,
#Name of a column which is unique and will be used to add a row to the DIFF object, default is "Name"
$Key = "Name" ,
#If specified, highlights the DIFF columns in rows which have the same key.
[System.Drawing.Color]$FontColor,
#If specified opens the Excel workbooks instead of outputting the diff to the console (unless -passthru is also specified)
[Switch]$Show,
#If specified, the command tries to the show the DIFF in a Gridview and not on the console. (unless-Passthru is also specified). This Works best with few columns selected, and requires a key
[switch]$GridView,
#If specified -Passthrough full set of diff data is returned without filtering to the specified properties
[Switch]$PassThru,
#If specified the result will include equal rows as well. By default only different rows are returned
[Switch]$IncludeEqual,
#If Specified the result includes only the rows where both are equal
[Switch]$ExcludeDifferent
)
#if the filenames don't resolve, give up now.
try { $oneFile = ((Resolve-Path -Path $Referencefile -ErrorAction Stop).path -eq (Resolve-Path -Path $Differencefile -ErrorAction Stop).path)}
Catch { Write-Warning -Message "Could not Resolve the filenames." ; return }
#If we have one file , we mush have two different worksheet names. If we have two files we can a single string or two strings.
if ($onefile -and ( ($WorkSheetName.count -ne 2) -or $WorkSheetName[0] -eq $WorkSheetName[1] ) ) {
Write-Warning -Message "If both the Reference and difference file are the same then worksheet name must provide 2 different names"
return
}
if ($WorkSheetName.count -eq 2) {$worksheet1 = $WorkSheetName[0] ; $WorkSheet2 = $WorkSheetName[1]}
elseif ($WorkSheetName -is [string]) {$worksheet1 = $WorkSheet2 = $WorkSheetName}
else {Write-Warning -Message "You must provide either a single worksheet name or two names." ; return }
$params= @{ ErrorAction = [System.Management.Automation.ActionPreference]::Stop }
foreach ($p in @("HeaderName","NoHeader","StartRow")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}}
try {
$Sheet1 = Import-Excel -Path $Referencefile -WorksheetName $WorkSheet1 @params
$Sheet2 = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 @Params
}
Catch {Write-Warning -Message "Could not read the worksheet from $Referencefile and/or $Differencefile." ; return }
#Get Column headings and create a hash table of Name to column letter.
$headings = $Sheet1[-1].psobject.Properties.name # This preserves the sequence - using get-member would sort them alphabetically!
$headings | ForEach-Object -Begin {$columns = @{} ; $i=65 } -Process {$Columns[$_] = [char]($i ++) }
#Make a list of property headings using the Property (default "*") and ExcludeProperty parameters
if ($Key -eq "Name" -and $NoHeader) {$key = "p1"}
$propList = @()
foreach ($p in $Property) {$propList += ($headings.where({$_ -like $p}) )}
foreach ($p in $ExcludeProperty) {$propList = $propList.where({$_ -notlike $p}) }
if (($headings -contains $key) -and ($propList -notcontains $Key)) {$propList += $Key}
$propList = $propList | Select-Object -Unique
if ($propList.Count -eq 0) {Write-Warning -Message "No Columns are selected with -Property = '$Property' and -excludeProperty = '$ExcludeProperty'." ; return}
#Add RowNumber, Sheetname and file name to every row
$FirstDataRow = $startRow + 1
if ($Headername -or $NoHeader) {$FirstDataRow -- }
$i = $FirstDataRow ; foreach ($row in $Sheet1) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++)
Add-Member -InputObject $row -MemberType NoteProperty -Name "_Sheet" -Value $worksheet1
Add-Member -InputObject $row -MemberType NoteProperty -Name "_File" -Value $Referencefile}
$i = $FirstDataRow ; foreach ($row in $Sheet2) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++)
Add-Member -InputObject $row -MemberType NoteProperty -Name "_Sheet" -Value $worksheet2
Add-Member -InputObject $row -MemberType NoteProperty -Name "_File" -Value $Differencefile}
if ($ExcludeDifferent -and -not $IncludeEqual) {$IncludeEqual = $true}
#Do the comparison and add file,sheet and row to the result - these are prefixed with "_" to show they are added the addition will fail if the sheet has these properties so split the operations
[PSCustomObject[]]$diff = Compare-Object -ReferenceObject $Sheet1 -DifferenceObject $Sheet2 -Property $propList -PassThru -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent |
Sort-Object -Property "_Row","File"
#if BackgroundColor was specified, set it on extra or extra or changed rows
if ($diff -and $BackgroundColor) {
#Differences may only exist in one file. So gather the changes for each file; open the file, update each impacted row in the shee, save the file
$updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property "_File"
foreach ($file in $updates) {
try {$xl = Open-ExcelPackage -Path $file.name }
catch {Write-warning -Message "Can't open $($file.Name) for writing." ; return}
if ($AllDataBackgroundColor) {
$file.Group._sheet | Sort-Object -Unique | ForEach-Object {
$ws = $xl.Workbook.Worksheets[$_]
if ($headerName) {$range = "A" + $startrow + ":" + $ws.dimension.end.address}
else {$range = "A" + ($startrow + 1) + ":" + $ws.dimension.end.address}
Set-Format -WorkSheet $ws -BackgroundColor $AllDataBackgroundColor -Range $Range
}
}
foreach ($row in $file.group) {
$ws = $xl.Workbook.Worksheets[$row._Sheet]
$range = $ws.Dimension -replace "\d+",$row._row
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $BackgroundColor
}
if ($TabColor) {
foreach ($tab in ($file.group._sheet | Select-Object -Unique)) {
$xl.Workbook.Worksheets[$tab].TabColor = $TabColor
}
}
$xl.save() ; $xl.Stream.Close() ; $xl.Dispose()
}
}
#if font colour was specified, set it on changed properties where the same key appears in both sheets.
if ($diff -and $FontColor -and ($propList -contains $Key) ) {
$updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property $Key | Where-Object {$_.count -eq 2}
if ($updates) {
$XL1 = Open-ExcelPackage -path $Referencefile
if ($oneFile ) {$xl2 = $xl1}
else {$xl2 = Open-ExcelPackage -path $Differencefile }
foreach ($u in $updates) {
foreach ($p in $propList) {
if($u.Group[0].$p -ne $u.Group[1].$p ) {
Set-Format -WorkSheet $xl1.Workbook.Worksheets[$u.Group[0]._sheet] -Range ($Columns[$p] + $u.Group[0]._Row) -FontColor $FontColor
Set-Format -WorkSheet $xl2.Workbook.Worksheets[$u.Group[1]._sheet] -Range ($Columns[$p] + $u.Group[1]._Row) -FontColor $FontColor
}
}
}
$xl1.Save() ; $xl1.Stream.Close() ; $xl1.Dispose()
if (-not $oneFile) {$xl2.Save() ; $xl2.Stream.Close() ; $xl2.Dispose()}
}
}
elseif ($diff -and $FontColor) {Write-Warning -Message "To match rows to set changed cells, you must specify -Key and it must match one of the included properties." }
#if nothing was found write a message which wont be redirected
if (-not $diff) {Write-Host "Comparison of $Referencefile::$worksheet1 and $Differencefile::$WorkSheet2 returned no results." }
if ($show) {
Start-Process -FilePath $Referencefile
if (-not $oneFile) { Start-Process -FilePath $Differencefile }
if ($GridView) { Write-Warning -Message "-GridView is ignored when -Show is specified" }
}
elseif ($GridView -and $propList -contains $key) {
if ($IncludeEqual -and -not $ExcludeDifferent) {
$GroupedRows = $diff | Group-Object -Property $key
}
else { #to get the right now numbers on the grid we need to have all the rows.
$GroupedRows = Compare-Object -ReferenceObject $Sheet1 -DifferenceObject $Sheet2 -Property $propList -PassThru -IncludeEqual |
Group-Object -Property $key
}
#Additions, deletions and unchanged rows will give a group of 1; changes will give a group of 2 .
#If one sheet has extra rows we can get a single "==" result from compare, but with the row from the reference sheet
#but the row in the other sheet might so we will look up the row number from the key field build a hash table for that
$Sheet2 | ForEach-Object -Begin {$Rowhash = @{} } -Process {$Rowhash[$_.$key] = $_._row }
$ExpandedDiff = ForEach ($g in $GroupedRows) {
#we're going to create a custom object from a hash table. We want the fields to be ordered
$hash = [ordered]@{}
foreach ($result IN $g.Group) {
# if result indicates equal or "in Reference" set the reference side row. If we did that on a previous result keep it. Otherwise set to "blank"
if ($result.sideindicator -ne "=>") {$hash["<Row"] = $result._Row }
elseif (-not $hash["<Row"]) {$hash["<Row"] = "" }
#if we have already set the side, this is the second record, so set side to indicate "changed"
if ($hash.Side) {$hash.side = "<>"} else {$hash["Side"] = $result.sideindicator}
#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"
for ($i = 1; $i -lt $ExpandedDiff.Count; $i++) {if (-not $ExpandedDiff[$i].">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[$i]."<row" = $ExpandedDiff[$i-1]."<row" } }
#if we had to put the equal rows back, take them out; sort, make sure all the columns are present in row 1 so the grid puts them in, and output
if ( $ExcludeDifferent) {$ExpandedDiff = $ExpandedDiff.where({$_.side -eq "=="}) | Sort-Object -Property "<row" ,">row" }
elseif ( $IncludeEqual) {$ExpandedDiff = $ExpandedDiff | Sort-Object -Property "<row" ,">row" }
else {$ExpandedDiff = $ExpandedDiff.where({$_.side -ne "=="}) | Sort-Object -Property "<row" ,">row" }
$ExpandedDiff | Update-FirstObjectProperties | Out-GridView -Title "Comparing $Referencefile::$worksheet1 (<=) with $Differencefile::$WorkSheet2 (=>)"
}
elseif ($GridView ) {Write-Warning -Message "To use -GridView you must specify -Key and it must match one of the included properties." }
elseif (-not $PassThru) {return ($diff | Select-Object -Property (@(@{n="_Side";e={$_.SideIndicator}},"_File" ,"_Sheet","_Row") + $propList))}
if ( $PassThru) {return $diff }
}

BIN
dashboard.xlsx Normal file

Binary file not shown.

227
formatting.ps1 Normal file
View File

@@ -0,0 +1,227 @@
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
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.
#>
Param (
#The worksheet where the format is to be applied
[OfficeOpenXml.ExcelWorksheet]$WorkSheet ,
#The area of the worksheet where the format is to be applied
[OfficeOpenXml.ExcelAddress]$Range ,
#One of the standard named rules - Top / Bottom / Less than / Greater than / Contains etc
[Parameter(Mandatory=$true,ParameterSetName="NamedRule",Position=3)]
[OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType]$RuleType ,
#Text colour for matching objects
[Alias("ForeGroundColour")]
[System.Drawing.Color]$ForeGroundColor,
#colour for databar type charts
[Parameter(Mandatory=$true,ParameterSetName="DataBar")]
[Alias("DataBarColour")]
[System.Drawing.Color]$DataBarColor,
#One of the three-icon set types (e.g. Traffic Lights)
[Parameter(Mandatory=$true,ParameterSetName="ThreeIconSet")]
[OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting3IconsSetType]$ThreeIconsSet,
#A four-icon set name
[Parameter(Mandatory=$true,ParameterSetName="FourIconSet")]
[OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting4IconsSetType]$FourIconsSet,
#A five-icon set name
[Parameter(Mandatory=$true,ParameterSetName="FiveIconSet")]
[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
[string]$ConditionValue2,
#Background colour for matching items
[System.Drawing.Color]$BackgroundColor,
#Background pattern for matching items
[OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid,
#Secondary colour when a background pattern requires it
[System.Drawing.Color]$PatternColor,
#Sets the numeric format for matching items
$NumberFormat,
#Put matching items in bold face
[switch]$Bold,
#Put matching items in italic
[switch]$Italic,
#Underline matching items
[switch]$Underline,
#Strikethrough text of matching items
[switch]$StrikeThru
)
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
$ConditionValue2 -and $RuleType -match "Between") {$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 }
}
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
#>
Param (
#One or more row(s), Column(s) and/or block(s) of cells to format
[Parameter(ValueFromPipeline=$true)]
[object[]]$Address ,
#Number format to apply to cells e.g. "dd/MM/yyyy HH:mm", "£#,##0.00;[Red]-£#,##0.00", "0.00%" , "##/##" , "0.0E+0" etc
[Alias("NFormat")]
$NumberFormat,
#Style of border to draw around the range
[OfficeOpenXml.Style.ExcelBorderStyle]$BorderAround,
#Colour for the text - if none specified it will be left as it it is
[System.Drawing.Color]$FontColor,
#Clear Bold, Italic, StrikeThrough and Underline and set colour to black
[switch]$ResetFont,
#Make text bold
[switch]$Bold,
#Make text italic
[switch]$Italic,
#Underline the text using the underline style in -underline type
[switch]$Underline,
#Should Underline use single or double, normal or accounting mode : default is single normal
[OfficeOpenXml.Style.ExcelUnderLineType]$UnderLineType = [OfficeOpenXml.Style.ExcelUnderLineType]::Single,
#StrikeThrough text
[switch]$StrikeThru,
#Subscript or superscript
[OfficeOpenXml.Style.ExcelVerticalAlignmentFont]$FontShift,
#Font to use - Excel defaults to Calibri
[String]$FontName,
#Point size for the text
[float]$FontSize,
#Change background colour
[System.Drawing.Color]$BackgroundColor,
#Background pattern - solid by default
[OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern =[OfficeOpenXml.Style.ExcelFillStyle]::Solid ,
#Secondary colour for background pattern
[Alias("PatternColour")]
[System.Drawing.Color]$PatternColor,
#Turn on text wrapping
[switch]$WrapText,
#Position cell contents to left, right or centre ...
[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.
[ValidateRange(-90,90)]
[int]$TextRotation ,
#Autofit cells to width (columns or ranges only)
[switch]$AutoFit,
#Set cells to a fixed width (columns or ranges only), ignored if Autofit is specified
[float]$Width,
#Set cells to a fixed hieght (rows or ranges only)
[float]$Height,
#Hide a row or column (not a range)
[switch]$Hidden
)
process {
Foreach ($range in $Address) {
if ($ResetFont) {$Range.Style.Font.Color.SetColor("Black")
$Range.Style.Font.Bold = $false
$Range.Style.Font.Italic = $false
$Range.Style.Font.UnderLine = $false
$Range.Style.Font.Strike = $false
}
if ($Underline) {$Range.Style.Font.UnderLine = $true
$Range.Style.Font.UnderLineType =$UnderLineType
}
if ($Bold) {$Range.Style.Font.Bold = $true }
if ($Italic) {$Range.Style.Font.Italic = $true }
if ($StrikeThru) {$Range.Style.Font.Strike = $true }
if ($FontShift) {$Range.Style.Font.VerticalAlign = $FontShift }
if ($FontColor) {$Range.Style.Font.Color.SetColor( $FontColor ) }
if ($BorderAround) {$Range.Style.Border.BorderAround( $BorderAround ) }
if ($NumberFormat) {$Range.Style.Numberformat.Format= $NumberFormat }
if ($TextRotation) {$Range.Style.TextRotation = $TextRotation }
if ($WrapText) {$Range.Style.WrapText = $true }
if ($HorizontalAlignment) {$Range.Style.HorizontalAlignment= $HorizontalAlignment }
if ($VerticalAlignment) {$Range.Style.VerticalAlignment = $VerticalAlignment }
if ($BackgroundColor) {
$Range.Style.Fill.PatternType = $BackgroundPattern
$Range.Style.Fill.BackgroundColor.SetColor($BackgroundColor)
if ($PatternColor) {
$range.Style.Fill.PatternColor.SetColor( $PatternColor)
}
}
if ($Height) {
if ($Range -is [OfficeOpenXml.ExcelRow] ) {$Range.Height = $Height }
elseif ($Range -is [OfficeOpenXml.ExcelRange] ) {
($range.Start.Row)..($range.Start.Row + $range.Rows) |
ForEach-Object {$ws.Row($_).Height = $Height }
}
else {Write-Warning -Message ("Can set the height of a row or a range but not a {0} object" -f ($Range.GetType().name)) }
}
if ($AutoFit) {
if ($Range -is [OfficeOpenXml.ExcelColumn]) {$Range.AutoFit() }
elseif ($Range -is [OfficeOpenXml.ExcelRange] ) {$Range.AutoFitColumns() }
else {Write-Warning -Message ("Can autofit a column or a range but not a {0} object" -f ($Range.GetType().name)) }
}
elseif ($Width) {
if ($Range -is [OfficeOpenXml.ExcelColumn]) {$Range.Width = $Width}
elseif ($Range -is [OfficeOpenXml.ExcelRange] ) {
($range.Start.Column)..($range.Start.Column+ $range.Columns) |
ForEach-Object {$ws.Column($_).Width = $Width}
}
else {Write-Warning -Message ("Can set the width of a column or a range but not a {0} object" -f ($Range.GetType().name)) }
}
if ($Hidden) {
if ($Range -is [OfficeOpenXml.ExcelRow] -or
$Range -is [OfficeOpenXml.ExcelColumn] ) {$Range.Hidden = $True}
else {Write-Warning -Message ("Can hide a row or a column but not a {0} object" -f ($Range.GetType().name)) }
}
}
}
}
#Argument completer for colours. If we have PS 5 or Tab expansion++ then we'll register it. Otherwise it does nothing.
Function ColorCompletion{
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
[System.Drawing.KnownColor].GetFields() | Where-Object {$_.IsStatic -and $_.name -like "$wordToComplete*" } |
Sort-Object name | ForEach-Object {New-CompletionResult $_.name $_.name
}
}

BIN
images/CustomReport.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
images/PivotTableFilter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
test1.xlsx Normal file

Binary file not shown.

BIN
testTable.xlsx Normal file

Binary file not shown.