Compare commits

..

1 Commits

Author SHA1 Message Date
dfinke
b67801e983 wip 2023-06-07 17:59:03 -04:00
19 changed files with 20 additions and 5948 deletions

View File

@@ -3,11 +3,8 @@ on:
branches: branches:
- master - master
- Set-up-GHA-CI/CD - Set-up-GHA-CI/CD
paths-ignore:
- 'Examples/**'
pull_request: pull_request:
jobs: jobs:
validate: validate:

View File

@@ -1,35 +0,0 @@
$data = ConvertFrom-Csv @"
Region,State,Units,Price
West,Texas,927,923.71
North,Tennessee,466,770.67
East,Florida,520,458.68
East,Maine,828,661.24
West,Virginia,465,053.58
North,Missouri,436,235.67
South,Kansas,214,992.47
North,North Dakota,789,640.72
South,Delaware,712,508.55
"@
$xlfile = "$PSScriptRoot\spike.xlsx"
Remove-Item $xlfile -ErrorAction SilentlyContinue
$xlpkg = $data | Export-Excel $xlfile -WorksheetName Data -AutoNameRange -PassThru
$null = Add-Worksheet -ExcelPackage $xlpkg -WorksheetName Summary -Activate
$params = @{
Worksheet = $xlpkg.Summary
Title = "Sales by Region"
ChartType = 'ColumnClustered'
# XRange = "Data!A2:A10"
# YRange = "Data!C2:C10"
XRange = 'Data!Region'
YRange = 'Data!Units'
}
Add-ExcelChart @params
Close-ExcelPackage $xlpkg -Show

View File

@@ -6,7 +6,7 @@
RootModule = 'ImportExcel.psm1' RootModule = 'ImportExcel.psm1'
# Version number of this module. # Version number of this module.
ModuleVersion = '7.8.10' ModuleVersion = '7.8.4'
# 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'
@@ -54,7 +54,6 @@ Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5
'Export-Excel', 'Export-Excel',
'Export-ExcelSheet', 'Export-ExcelSheet',
'Get-ExcelColumnName', 'Get-ExcelColumnName',
'Get-ExcelFileSchema',
'Get-ExcelFileSummary', 'Get-ExcelFileSummary',
'Get-ExcelSheetDimensionAddress', 'Get-ExcelSheetDimensionAddress',
'Get-ExcelSheetInfo', 'Get-ExcelSheetInfo',
@@ -150,7 +149,7 @@ Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5
Tags = @("Excel", "EPPlus", "Export", "Import") Tags = @("Excel", "EPPlus", "Export", "Import")
# The web address of an icon which can be used in galleries to represent this module # The web address of an icon which can be used in galleries to represent this module
#IconUri = #IconUri = "http://pesterbdd.com/images/Pester.png"
# The web address of this module's project or support homepage. # The web address of this module's project or support homepage.
ProjectUri = "https://github.com/dfinke/ImportExcel" ProjectUri = "https://github.com/dfinke/ImportExcel"
@@ -215,4 +214,4 @@ Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = '' # DefaultCommandPrefix = ''
} }

View File

@@ -67,8 +67,9 @@ function WorksheetArgumentCompleter {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
$xlPath = $fakeBoundParameter['Path'] $xlPath = $fakeBoundParameter['Path']
if (Test-Path -Path $xlPath) { if (Test-Path -Path $xlPath) {
$xlSheet = Get-ExcelSheetInfo -Path $xlPath $xlpkg = Open-ExcelPackage -ReadOnly -Path $xlPath
$WorksheetNames = $xlSheet.Name $WorksheetNames = $xlPkg.Workbook.Worksheets.Name
Close-ExcelPackage -nosave -ExcelPackage $xlpkg
$WorksheetNames.where( { $_ -like "*$wordToComplete*" }) | foreach-object { $WorksheetNames.where( { $_ -like "*$wordToComplete*" }) | foreach-object {
New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList "'$_'", New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList "'$_'",
$_ , ([System.Management.Automation.CompletionResultType]::ParameterValue) , $_ $_ , ([System.Management.Automation.CompletionResultType]::ParameterValue) , $_

View File

@@ -19,9 +19,6 @@ function Close-ExcelPackage {
try { [OfficeOpenXml.CalculationExtension]::Calculate($ExcelPackage.Workbook) } try { [OfficeOpenXml.CalculationExtension]::Calculate($ExcelPackage.Workbook) }
catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook." } catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook." }
} }
# Set FullCalcOnLoad to false to prevent Excel from corrupting formulas during recalculation
# This fixes issues with table-structured references like [[#This Row],[ColumnName]]
$ExcelPackage.Workbook.FullCalcOnLoad = $false
if ($SaveAs) { if ($SaveAs) {
$SaveAs = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SaveAs) $SaveAs = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SaveAs)
if ($Password) { $ExcelPackage.SaveAs( $SaveAs, $Password ) } if ($Password) { $ExcelPackage.SaveAs( $SaveAs, $Password ) }

View File

@@ -8,8 +8,7 @@ function ConvertFrom-ExcelData {
[ScriptBlock]$ScriptBlock, [ScriptBlock]$ScriptBlock,
[Alias("Sheet")] [Alias("Sheet")]
$WorksheetName = 1, $WorksheetName = 1,
[Alias('HeaderRow', 'TopRow')] [int]$HeaderRow = 1,
[int]$StartRow = 1,
[string[]]$Header, [string[]]$Header,
[switch]$NoHeader, [switch]$NoHeader,
[switch]$DataOnly [switch]$DataOnly

View File

@@ -45,18 +45,11 @@ function ConvertTo-ExcelXlsx {
throw "Could not create Excel.Application ComObject. Please verify that Excel is installed." throw "Could not create Excel.Application ComObject. Please verify that Excel is installed."
} }
try { $Excel.Visible = $false
$Excel.Visible = $false $null = $Excel.Workbooks.Open($xlsFile.FullName)
$null = $Excel.Workbooks.Open($xlsFile.FullName, $null, $true) $Excel.ActiveWorkbook.SaveAs($xlsxPath, $xlFixedFormat)
$Excel.ActiveWorkbook.SaveAs($xlsxPath, $xlFixedFormat) $Excel.ActiveWorkbook.Close()
} $Excel.Quit()
finally {
if ($null -ne $Excel.ActiveWorkbook) {
$Excel.ActiveWorkbook.Close()
}
$Excel.Quit()
}
} }
} }

View File

@@ -682,9 +682,6 @@
else { else {
if ($ReturnRange) { $dataRange } if ($ReturnRange) { $dataRange }
# Set FullCalcOnLoad to false to prevent Excel from corrupting formulas during recalculation
# This fixes issues with table-structured references like [[#This Row],[ColumnName]]
$pkg.Workbook.FullCalcOnLoad = $false
if ($Password) { $pkg.Save($Password) } if ($Password) { $pkg.Save($Password) }
else { $pkg.Save() } else { $pkg.Save() }
Write-Verbose -Message "Saved workbook $($pkg.File)" Write-Verbose -Message "Saved workbook $($pkg.File)"

View File

@@ -1,47 +0,0 @@
function Get-ExcelFileSchema {
<#
.SYNOPSIS
Gets the schema of an Excel file.
.DESCRIPTION
The Get-ExcelFileSchema function gets the schema of an Excel file by returning the property names of the first row of each worksheet in the file.
.PARAMETER Path
Specifies the path to the Excel file.
.PARAMETER Compress
Indicates whether to compress the json output.
.OUTPUTS
Json
.EXAMPLE
Get-ExcelFileSchema -Path .\example.xlsx
#>
[CmdletBinding()]
param(
[Parameter(ValueFromPipelineByPropertyName, Mandatory)]
[Alias('FullName')]
$Path,
[Switch]$Compress
)
Begin {
$result = @()
}
Process {
$excelFiles = Get-ExcelFileSummary $Path
foreach ($excelFile in $excelFiles) {
$data = Import-Excel $Path -WorksheetName $excelFile.WorksheetName | Select-Object -First 1
$names = $data[0].PSObject.Properties.name
$result += $excelFile | Add-Member -MemberType NoteProperty -Name "PropertyNames" -Value $names -PassThru
}
}
End {
$result | ConvertTo-Json -Compress:$Compress
}
}

View File

@@ -50,9 +50,7 @@ function Get-HtmlTable {
else { else {
$h = ConvertFrom-Html -Content $r.Content $h = ConvertFrom-Html -Content $r.Content
if ($TableIndex -is [valuetype]) { $TableIndex += 1} if ($TableIndex -is [valuetype]) { $TableIndex += 1}
$rows = try { $rows = $h.SelectNodes("//table[$TableIndex]//tr")
$h.SelectSingleNode("//table[$TableIndex]").SelectNodes(".//tr")
} catch {}
if (-not $rows) {Write-Warning "Could not find rows for `"//table[$TableIndex]`" in $Url ."} if (-not $rows) {Write-Warning "Could not find rows for `"//table[$TableIndex]`" in $Url ."}
if ( -not $propertyNames) { if ( -not $propertyNames) {
if ( $tableHeaders = $rows[$FirstDataRow].SelectNodes("th")) { if ( $tableHeaders = $rows[$FirstDataRow].SelectNodes("th")) {

View File

@@ -37,7 +37,8 @@
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[String]$Password, [String]$Password,
[Int[]]$ImportColumns, [Int[]]$ImportColumns,
[Switch]$Raw [Switch]$Raw,
[Switch]$UseSpectre
) )
end { end {
$sw = [System.Diagnostics.Stopwatch]::StartNew() $sw = [System.Diagnostics.Stopwatch]::StartNew()
@@ -252,6 +253,9 @@
$entry.Value $entry.Value
} }
} }
elseif ($UseSpectre) {
$xlBook | Format-SpectreTable
}
elseif ($Worksheet.Count -eq 1) { elseif ($Worksheet.Count -eq 1) {
$xlBook["$targetSheetname"] $xlBook["$targetSheetname"]
} }

View File

@@ -19,13 +19,10 @@ Consider donating. Thank you!
<br> <br>
<br> <br>
[![Follow on Twitter](https://img.shields.io/twitter/follow/dfinke.svg?style=social&label=Follow%20%40dfinke)](https://twitter.com/dfinke) [![Build Status](https://dougfinke.visualstudio.com/ImportExcel/_apis/build/status/dfinke.ImportExcel?branchName=master)](https://dougfinke.visualstudio.com/ImportExcel/_build)
[![Subscribe on YouTube](https://img.shields.io/youtube/channel/subscribers/UCP47ZkO5EDkoI2sr-3P4ShQ
)](https://youtube.com/@dougfinke/)
<br/>
<br/>
[![](https://img.shields.io/powershellgallery/v/ImportExcel.svg)](https://www.powershellgallery.com/packages/ImportExcel) [![](https://img.shields.io/powershellgallery/v/ImportExcel.svg)](https://www.powershellgallery.com/packages/ImportExcel)
[![](https://img.shields.io/powershellgallery/dt/ImportExcel.svg)](https://www.powershellgallery.com/packages/ImportExcel) [![](https://img.shields.io/powershellgallery/dt/ImportExcel.svg)](https://www.powershellgallery.com/packages/ImportExcel)
[![](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/dfinke/ImportExcel/tree/70ab9e46c776e96fb287682d5b9b4b51a0ec3bac/LICENSE.txt)
<a href="https://www.paypal.com/paypalme/DougCharlesFinke"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a> <a href="https://www.paypal.com/paypalme/DougCharlesFinke"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a>
# Overview # Overview

View File

@@ -1,54 +0,0 @@
if (-not (Get-command Import-Excel -ErrorAction SilentlyContinue)) {
Import-Module $PSScriptRoot\..\ImportExcel.psd1
}
Describe "Test getting the schema of an Excel file" -Tag GetExcelFileSchema {
BeforeAll {
$script:excelFile = "TestDrive:\test.xlsx"
$data = ConvertFrom-Csv @"
Region,State,Units,Price
West,Texas,927,923.71
North,Tennessee,466,770.67
East,Florida,520,458.68
East,Maine,828,661.24
West,Virginia,465,053.58
North,Missouri,436,235.67
South,Kansas,214,992.47
North,North Dakota,789,640.72
South,Delaware,712,508.55
"@
$data | Export-Excel $excelFile
}
It "Test Get-ExcelFileSchema function exists" {
$function = Get-Command Get-ExcelFileSchema -ErrorAction SilentlyContinue
$function | Should -Not -Be $null
}
It "Test Get-ExcelFileSchema returns json" {
$actual = Get-ExcelFileSchema -Path $excelFile
$actual | Should -Not -Be $null
$actual | Should -BeOfType [string]
}
It "Test Get-ExcelFileSchema correct json" {
$actual = Get-ExcelFileSchema -Path $excelFile
$actual = $actual | ConvertFrom-Json
$actual.ExcelFile | Should -BeExactly "test.xlsx"
$actual.WorksheetName | Should -BeExactly "Sheet1"
$actual.Visible | Should -Be $true
$actual.Rows | Should -Be 10
$actual.Columns | Should -Be 4
$actual.Address | Should -BeExactly "A1:D10"
$actual.Path | Should -BeExactly ("TestDrive:" + [System.IO.Path]::DirectorySeparatorChar)
$actual.PropertyNames.Count | Should -Be 4
$actual.PropertyNames[0] | Should -BeExactly "Region"
$actual.PropertyNames[1] | Should -BeExactly "State"
$actual.PropertyNames[2] | Should -BeExactly "Units"
$actual.PropertyNames[3] | Should -BeExactly "Price"
}
}

Binary file not shown.

View File

@@ -1,125 +0,0 @@
#Requires -Modules @{ ModuleName="Pester"; ModuleVersion="4.0.0" }
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'False Positives')]
param()
Describe "Table Formula Bug Fix" -Tag "TableFormula" {
BeforeAll {
$WarningAction = "SilentlyContinue"
}
Context "FullCalcOnLoad is set to false to prevent formula corruption" {
BeforeAll {
$path = "TestDrive:\table_formula.xlsx"
Remove-Item -Path $path -ErrorAction SilentlyContinue
# Create a table with a blank record
$BlankRecordForFile = [PsCustomObject]@{
'Action Add' = ''
UserName = ''
Address = ''
Name = ''
NewName = ''
}
# Export as a table
$ExcelFile = $BlankRecordForFile | Export-Excel -Path $path -WorksheetName 'Data' `
-TableName 'DataTable' -TableStyle 'Light1' -AutoSize:$false -AutoFilter `
-BoldTopRow -FreezeTopRow -StartRow 1 -PassThru
$Worksheet = $ExcelFile.Workbook.Worksheets['Data']
# Insert a row and add a complex formula with table-structured references
$Worksheet.InsertRow(2, 1)
# This formula uses old-style table references [[#This Row],[ColumnName]]
# which Excel converts to [@ColumnName] when opening
$Formula = '=IFS( [[#This Row],[UserName]]="","", [[#This Row],[Action Add]]=TRUE, CONCAT([[#This Row],[Address]],"-",[[#This Row],[UserName]]), CONCAT([[#This Row],[Address]],"-",[[#This Row],[UserName]]) <> [[#This Row],[Name]], CONCAT([[#This Row],[Address]],"-",[[#This Row],[UserName]]), TRUE, "")'
$Cell = $Worksheet.Cells['e2']
$Cell.Formula = $Formula
Close-ExcelPackage $ExcelFile
# Reopen to verify
$ExcelFile2 = Open-ExcelPackage -Path $path
$Worksheet2 = $ExcelFile2.Workbook.Worksheets['Data']
}
It "Sets fullCalcOnLoad to false in the workbook XML" {
# Extract and check the XML directly
$TempExtractPath = Join-Path -Path $TestDrive -ChildPath "extracted_$(Get-Random)"
Expand-Archive -Path $path -DestinationPath $TempExtractPath -Force
$WorkbookXml = Get-Content (Join-Path -Path $TempExtractPath -ChildPath "xl/workbook.xml") -Raw
$WorkbookXml | Should -Match 'fullCalcOnLoad="0"'
Remove-Item -Path $TempExtractPath -Recurse -Force
}
It "Preserves the formula correctly after save and reopen" {
$Cell2 = $Worksheet2.Cells['e2']
$Cell2.Formula | Should -Not -BeNullOrEmpty
$Cell2.Formula | Should -Match 'IFS\('
$Cell2.Formula | Should -Match 'CONCAT\('
}
It "Does not corrupt the formula with extra @ symbols" {
$Cell2 = $Worksheet2.Cells['e2']
# The formula should not have extra @ symbols added by Excel during recalculation
# The specific bug was an @ being inserted before CONCAT in the middle of the formula
# We can't test this directly without opening in Excel, but we can verify the formula is unchanged
$Cell2.Formula.Length | Should -BeGreaterThan 100
}
AfterAll {
if ($ExcelFile2) {
Close-ExcelPackage -ExcelPackage $ExcelFile2 -NoSave
}
}
}
Context "FullCalcOnLoad setting works with different save methods" {
It "Sets fullCalcOnLoad to false when using SaveAs" {
$path = "TestDrive:\saveas_test.xlsx"
$path2 = "TestDrive:\saveas_test2.xlsx"
Remove-Item -Path $path, $path2 -ErrorAction SilentlyContinue
$data = [PSCustomObject]@{ Name = 'Test' }
$excel = $data | Export-Excel -Path $path -PassThru
Close-ExcelPackage $excel -SaveAs $path2
# Extract and check the XML
$TempExtractPath = Join-Path -Path $TestDrive -ChildPath "extracted_saveas_$(Get-Random)"
Expand-Archive -Path $path2 -DestinationPath $TempExtractPath -Force
$WorkbookXml = Get-Content (Join-Path -Path $TempExtractPath -ChildPath "xl/workbook.xml") -Raw
$WorkbookXml | Should -Match 'fullCalcOnLoad="0"'
Remove-Item -Path $TempExtractPath -Recurse -Force
}
It "Sets fullCalcOnLoad to false when using Calculate flag" {
$path = "TestDrive:\calculate_test.xlsx"
Remove-Item -Path $path -ErrorAction SilentlyContinue
$data = [PSCustomObject]@{ Name = 'Test'; Value = 100 }
$excel = $data | Export-Excel -Path $path -PassThru
# Set a formula
$ws = $excel.Workbook.Worksheets[1]
$ws.Cells['C2'].Formula = 'B2*2'
Close-ExcelPackage $excel -Calculate
# Extract and check the XML
$TempExtractPath = Join-Path -Path $TestDrive -ChildPath "extracted_calc_$(Get-Random)"
Expand-Archive -Path $path -DestinationPath $TempExtractPath -Force
$WorkbookXml = Get-Content (Join-Path -Path $TempExtractPath -ChildPath "xl/workbook.xml") -Raw
$WorkbookXml | Should -Match 'fullCalcOnLoad="0"'
Remove-Item -Path $TempExtractPath -Recurse -Force
}
}
}

View File

@@ -1,60 +1,3 @@
# 7.8.10
- Thank you https://github.com/evenmartinsen for the PR to fix the AV
# 7.8.9
- Thanks to (Edward Miller)[https://github.com/edwardmiller-mesirow] for improving `ConvertTo-ExcelXlsx`and making it more robust
# 7.8.8
- Fix the release
# 7.8.7
- Thanks to [Phil Bossman](https://github.com/pbossman) for the PR and fixing this.
Now, back again, you can type `Import-Excel .\yearlySales.xlsx`, press <ctrl+space> and get a list of the worksheets in the Excel file
![alt text](images/AutoCompleteSheetNames.png)
# Infrastructure change
- Thank you to [RipFence](https://github.com/RipFence) who asked how to place a chart on a different sheet from the data and then did a PR adding the example.
- added `ignore` so files checked into examples do not trigger a CI run
# 7.8.6
- Thank you [John Boyne](https://github.com/kyllath)
- Add missing parameter aliases to align with caller/callee
# 7.8.5
- Added `Get-ExcelFileSchema` to get the schema of an Excel file.
- This was added to support interacting with `ChatGPT`. Passing the schema to the `ChatGPT` via `PowerShellAI` let's you ask questions about the data including generating code based on the schema.
```powershell
Get-ExcelFileSchema .\salesData.xlsx
```
```json
{
"ExcelFile": "salesData.xlsx",
"WorksheetName": "Sheet1",
"Visible": true,
"Rows": 10,
"Columns": 4,
"Address": "A1:D10",
"Path": ".",
"PropertyNames": [
"Region",
"State",
"Units",
"Price"
]
}
```
# 7.8.x # 7.8.x
Thanks to [Thomas Hofkens](https://github.com/thkn-hofa) Thanks to [Thomas Hofkens](https://github.com/thkn-hofa)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because it is too large Load Diff