Search-WolframAlpha.ps1

function Search-WolframAlpha
{
    <#
    .Synopsis
        Searches Wolfram Alpha
    .Description
        Searches Wolfram Alpha for information on anything
    .Example
        Search-WolframAlpha -For "Micrososft" -ApiKeySetting WolframAlphaApiKey
    .Link
        Get-SecureSetting
    .Link
        Search-Engine
    #>

    [OutputType([PSObject])]
    param(
    # The term to search for.
    [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
    [String]$For,
    
    # The WolframAlpha pod state to return
    [string]$PodState,
       
    # The PodID of interest
    [string]$PodId,
    
    # If set, renders output as HTML
    [Switch]$AsHtml,
    
    # If set, renders the output as input to Mathemetica
    [Switch]$AsMathematicaInput,
    
    # If set, renders the output as output to Mathemetica
    [Switch]$AsMathematicaOutput,
   
    # If set, renders the output as MathML
    [Switch]$AsMathML,
    
    # Requests information as if the user was from a specific IP
    [string]$FromIP,
    
    # Requests information as if the user was from a specific location
    [string]$Location,    

    # Returns the XML result
    [Switch]$AsXml,
    
    # The API Key
    [string]$ApiKey,
    
    # A web.config or SecureSetting containing the ApiKey
    [string]$ApiKeySetting,
    
    # If set, will run the query as a background job
    [switch]$AsJob,
    
    # If set, will always refetch the data
    [Switch]$Force         
    )
    
    begin {
        #region Setup Result Cache
        Set-StrictMode -Off
        if (-not ($script:cachedRawResults)) { 
            $script:cachedRawResults = @{}            
        }
        #endregion Setup Result Cache
    }
    
    
    process {
        if ($apiKey) {
            $script:WolframAlphaAPIKey = $apiKey
        }
        if ($apiKeySetting) {
            $apiKey= Get-WebConfigurationSetting -Setting $apiKeySetting 
            if (-not $apiKey) {                
                $apiKey= Get-SecureSetting -Name $apiKeySetting -ValueOnly
            }
            if ($apiKey) {
                $script:WolframAlphaAPIKey = $apiKey
            }
            
        }
        
        $psBoundParameters.ApiKey = $script:WolframAlphaAPIKey

        if ($AsJob) {
            $myDefinition = [ScriptBLock]::Create("function Search-WolframAlpha {
$(Get-Command Search-WolframAlpha | Select-Object -ExpandProperty Definition)
}
"
)
            $null = $psBoundParameters.Remove('AsJob')            
            $myJob= [ScriptBLock]::Create("" + {
                param([Hashtable]$parameter) 
                
            } + $myDefinition + {
                
                Search-WolframAlpha @parameter
            }) 
            
            Start-Job -ScriptBlock $myJob -ArgumentList $psBoundParameters 
            return
        }
        $script:WolframAlphaWebClient = New-Object Net.WebClient
        $queryBase = "http://api.wolframalpha.com/v2/query?"
        $queryTerm = "input=$($For.Replace(' ', '%20'))"        
        if (-not $script:WolframAlphaAPIKey) {
        }
        
        $queryAPIKey = "appid=$Script:WolframAlphaAPIKey"
        
        if ($psboundParameters.PodState) {
            $queryTerm+="&podstate=$podstate"
        }
        if ($psboundParameters.PodId) {
            $queryTerm+="&includepodid=$PodId"
        }

        $query = "${QueryBase}${QueryTerm}&${QueryAPIKey}"
        if ($AsHTML) {
            $query = "${QueryBase}${QueryTerm}&${QueryAPIKey}&format=html"
            $result = $script:WolframAlphaWebClient.DownloadString($query)
            $result
        } elseif ($AsMathematicaInput) {
            $query = "${QueryBase}${QueryTerm}&${QueryAPIKey}&format=minput"
            $result = $script:WolframAlphaWebClient.DownloadString($query)
            $result
        
        } elseif ($AsMathematicaOutput) {
            $query = "${QueryBase}${QueryTerm}&${QueryAPIKey}&format=moutput"
            $result = $script:WolframAlphaWebClient.DownloadString($query)
            $result        
        } elseif ($AsMathML) {
            $query = "${QueryBase}${QueryTerm}&${QueryAPIKey}&format=mathml"
            $result = $script:WolframAlphaWebClient.DownloadString($query)
            $result       
        } elseif ($AsXML) {
            $query = "${QueryBase}${QueryTerm}&${QueryAPIKey}"
            $result = $webClient.DownloadString($query)
            $result
        } else {
            # Turn regular XML output into a psuedo object
            $query = "${QueryBase}${QueryTerm}&${QueryAPIKey}"            
            if ($location) {
                $query += "&location=$location"
            }
            
            
            if ($fromIP) {                
                $query += "&ip=$fromIP"
            }
            $result = if ($script:CachedRawResults[$query] -and (-not $Force)) {
                $script:cachedRawResults[$query]
            } else {                
                $script:cachedRawResults[$query] = $script:WolframAlphaWebClient.DownloadString($query)
                $script:cachedRawResults[$query]
            }
            $resultError = ([xml]$result).SelectSingleNode("//error")            
            if ($resultError) {            
                Write-Error -Message $resultError.msg -ErrorId "WolframAlphaWebServiceError$($resultError.Code)"    
                return
            }
            $pods = @{}
            
                Write-Verbose "$result"
            # Scrape the results from the pods
            $result | 
                Select-Xml //pod | 
                ForEach-Object  -Begin {
                    $psObject = New-Object PSObject
                } {        
                    $pod = $_.Node            
                    if ($pod.Id -eq 'Input') {
                        $psObject.psobject.Properties.Add(
                            (New-Object Management.Automation.PSNoteProperty "InputInterpretation","$($pod.subpod.plaintext)"
                        ))
                    }
                    
                    if ($pod.Id -ne 'Input') {
                        # Try and try and try
                        $textInPods = $pod.SubPod  |Select-Object -ExpandProperty plaintext
                        $textInPods = $textInPods -join ([Environment]::NewLine)
                        $lines = $textInPods.Split([Environment]::NewLine, [StringSplitOptions]'RemoveEmptyEntries')
                        # Since Wolfram|Alpha is a data-oriented search engine that strangely enough, returns text instead of data, it's time to parse (and pray)
                        $averageItemsPerLine = $lines | 
                            ForEach-Object {
                                $_.ToCharArray() |
                                    Where-Object {$_ -eq '|' } |
                                    Measure-Object
                            } | Measure-Object -Average -Property Count |
                            Select-Object -ExpandProperty Average
                        
                        # If there's more than one item per line average, it's probably a series of items
                        if ($averageItemsPerLine -lt 1) {
                            if (-not $lines) {
                                $psNoteProperty = New-Object Management.Automation.PSNoteProperty $pod.Title, $pod.Subpod.img.src
                                $null = $psObject.psobject.Properties.Add($psNoteProperty)                            
                            } else {
                                $psNoteProperty = New-Object Management.Automation.PSNoteProperty $pod.Title, $lines
                                $null = $psObject.psobject.Properties.Add($psNoteProperty)                            
                            }
                            
                        } elseif ($averageItemsPerLine -ge 1 -and $averageItemsPerLine -lt 2) {
                            # It's probably a table of properties, but it could also be a result list
                            $outputObject = New-Object PSObject
                            $lastProperty = $null
                            foreach ($line in $lines) {
                                $chunks = @($line.Split('|', [StringSplitOptions]'RemoveEmptyEntries'))
                                # If it's greater than 1, treat it as a pair of values
                                
                                if ($chunks.Count -gt 1) {
                                    # Heading and value.
                                    $lastProperty = $chunks[0]
                                    if ("$lastProperty".Trim()) {
                                        $value = $chunks[1] | 
                                            Where-Object {"$_".Trim() -notlike "(*)"}
                                        $outputObject | Add-Member NoteProperty $chunks[0] "$value".Trim() -Force
                                    }
                                } elseif ($chunks.Count -eq 1) {
                                    # Additional value
                                    $newValue = @($outputObject.$lastProperty) +  "$($chunks[0])".Trim()
                                    if ("$lastProperty".Trim()) {
                                        $outputObject | Add-Member NoteProperty $lastProperty $newValue -Force
                                    }
                                }
                            }
                            $psNoteProperty = New-Object Management.Automation.PSNoteProperty $pod.Title, $outputObject
                            $null = $psObject.psobject.Properties.Add($psNoteProperty)                            
                        } else {
                            # It's probably a table
                            
                            if ($lines.Count -eq 1) {
                                $itemValue, $itemSource = $lines[0] -split "[\(\)]"
                                
                                $outputObject =New-Object PSOBject |
                                    Add-Member NoteProperty Value $itemValue -PassThru | 
                                    Add-Member NoteProperty Source $itemSource -PassThru
                                $psNoteProperty = New-Object Management.Automation.PSNoteProperty $pod.Title, $outputObject    
                                $null = $psObject.psobject.Properties.Add($psNoteProperty)                            
                            } else {
                                $columns = $lines[0] -split "\|" | ForEach-Object {$_.Trim() } 
                                $rows = foreach ($l in $lines[1..($lines.Count -1)]) {    
                                    $l -split "\|" | ForEach-Object { $_.Trim() } 
                                }

                                $outputObject = 
                                    for ($i =0;$i -lt $rows.Count; $i+=$columns.Count) {
                                        $outputObject = New-Object PSObject 
                                        foreach ($n in 1..$columns.Count) {
                                            $columnName =
                                                if (-not $columns[$n -1]) {
                                                    "Name"
                                                } else {
                                                    $columns[$n -1]
                                                }
                                            $outputObject | 
                                                Add-Member NoteProperty $columnName ($rows[$i + $n - 1]) -Force
                                             
                                        }
                                        $outputObject 
                                    }
                                $psNoteProperty = New-Object Management.Automation.PSNoteProperty $pod.Title, $outputObject    
                                $null = $psObject.psobject.Properties.Add($psNoteProperty)                            
                            }
                        } 
                    }
                    
                    # Append pod details into the result
                    $pods[$pod.Id] = New-Object PSObject -Property @{
                        PodText = $pod.subpod | Select-Object -ExpandProperty plaintext -ErrorAction SilentlyContinue
                        PodImage = $pod.subpod | 
                            Select-Object -ExpandProperty img -ErrorAction SilentlyContinue |
                            Select-Object -ExpandProperty src
                        PodXml = $pod
                    }                    
                } -End {
                    # Add on the raw output
                    $psObject.psobject.Properties.Add(
                        (New-Object Management.Automation.PSNoteProperty "OutputXml",([xml]$result)
                    ))
                    try {
                        # Add on specific pod information
                    $psObject.psObject.Properties.Add(
                        (New-Object Management.Automation.PSNoteProperty "Pods",(New-Object PSObject -Property $pods)
                    ))
                    } catch {
                        # If there weren't any pods, add a null property
                    $psObject.psObject.Properties.Add(
                        (New-Object Management.Automation.PSNoteProperty "Pods", $null
                    ))    
                    }
                    # Give the search result the correct typename, so PowerShell can display it sanely
                    $psObject.pstypenames.Clear()
                    $psObject.pstypenames.add('WolframAlphaResult')
                    $psObject
                    
                    
                }
        }
    }
}