Monday, March 25, 2013

To alias or not to alias.

To alias, or not to alias, or to imply an alias (or Function)

The difference between Help and Get-Help was raised in the discussion portion of this months Saturday PowerShell SIG.  I had always assumed that Help was just an alias for Get-Help, and was happily ignorant.

Help Vs. Get-Help


This isn't really an alias, it is a function that can be seen with:
get-item function:Help | select -expand ScriptBlock

For example a PS3.0 install with the PSCX  module installed gives:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
[CmdletBinding(DefaultParameterSetName='AllUsersView', HelpUri='http://go.microsoft.com/fwlink/?LinkID=113316')]
param(
[Parameter(Position=0, ValueFromPipelineByPropertyName=$true)]
[System.String]
${Name},

[System.String]
${Path},

[ValidateSet('Alias','Cmdlet','Provider','General','FAQ','Glossary','HelpFile','ScriptCommand','Function','F
ilter'
,'ExternalScript','All','DefaultHelp','Workflow')]
[System.String[]]
${Category},

[System.String[]]
${Component},

[System.String[]]
${Functionality},

[System.String[]]
${Role},

[Parameter(ParameterSetName='DetailedView', Mandatory=$true)]
[Switch]
${Detailed},

[Parameter(ParameterSetName='AllUsersView')]
[Switch]
${Full},

[Parameter(ParameterSetName='Examples', Mandatory=$true)]
[Switch]
${Examples},

[Parameter(ParameterSetName='Parameters', Mandatory=$true)]
[System.String]
${Parameter},

[Parameter(ParameterSetName='Online', Mandatory=$true)]
[switch]
${Online},

[Parameter(ParameterSetName='ShowWindow', Mandatory=$true)]
[switch]
${ShowWindow}
)

$outputEncoding=[System.Console]::OutputEncoding

if ($Pscx:Preferences["PageHelpUsingLess"])
{
    Get-Help @PSBoundParameters | less
}
else
{
    Get-Help @PSBoundParameters | more
}


Without PSCX in PowerShell 2.0 the same Command generates:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
<# .FORWARDHELPTARGETNAME Get-Help .FORWARDHELPCATEGORY Cmdlet #>
[CmdletBinding(DefaultParameterSetName='AllUsersView')]
param(
    [Parameter(Position=0, ValueFromPipelineByPropertyName=$true)]
    [System.String]
    ${Name},

    [System.String]
    ${Path},

    [System.String[]]
    ${Category},

    [System.String[]]
    ${Component},

    [System.String[]]
    ${Functionality},

    [System.String[]]
    ${Role},

    [Parameter(ParameterSetName='DetailedView')]
    [Switch]
    ${Detailed},

    [Parameter(ParameterSetName='AllUsersView')]
    [Switch]
    ${Full},

    [Parameter(ParameterSetName='Examples')]
    [Switch]
    ${Examples},

    [Parameter(ParameterSetName='Parameters')]
    [System.String]
    ${Parameter},

    [Switch]
    ${Online})
$outputEncoding=[System.Console]::OutputEncoding

      Get-Help @PSBoundParameters | more


Help is not an alias, it is a function in both installs.  People treat these as interchangeable in many online references that I have seen.  For most part they are, but they are not the 'same' and this distinction may impact learning.

Alias Ambiguity 

We also talked about where alias are helpful and a hindrance   The general idea being that alias make life easier on you, and potentially more difficult on someone else.  Specifically that someone else would be those reading your script, forum post, or forum answers.  Potentially to someone new to PowerShell this can start with confusion.

For Example:

Get-Alias -Definition Get-ChildItem

CommandType     Name                                               ModuleName                                    
-----------     ----                                               ----------                                    
Alias           dir -> Get-ChildItem                                                                              
Alias           gci -> Get-ChildItem                                                                              
Alias           ls -> Get-ChildItem



If you mix these alias with full cmdlets you can have 4 completely correct answers.  If you are trying to learn the Verb-Noun pairing of PowerShell you could completely miss it. The Verb-Noun pairs are also often referenced as key learning and exploring benefit of PowerShell.

So what could we do to completely overreact to this information?

  1. We could wipe out all of alias:\* and all function:\* that don't contain a hyphen (-).  We would probably not be able to run many other scripts though.  Not sure what this would break actually.  I could only see it as really helping in a high-wire style class room setting.
  2. Run a Function to open a PS1, or ISE and expand aliases.  Something like: http://PoshCode.org/embed/2980 
  3. Improve visibility into what you write and echo an expanded version of the last thing you typed in expanded form.  While at it, expanding any ordered or trimmed argument names would be good too (not show below).
001
002
003
004
005
006
007
008
009
# Generic append to existing Prompt Line
Copy-item Function:Prompt Function:Prompt_Backup

Function Prompt {
Write-host `n
# Expand Alias from http://PoshCode.org/embed/2980
Expand-Alias -script (get-history -count 1 ) | write-host
Function:Prompt_Backup
}

Saturday, January 12, 2013

Connective tissue


Did I mention I have a fondness for PowerShell.  I like to call it glue. I often use it to put things together.  Tuc Goodwin said I should be careful with this association as this is the same thing that VB6 was labeled as before it took off at a gallop.  Say what you will about VB6; its largest issue politically was that it was kind enough to let people create horrible things.  But really it shouldn't be condemned for being usable especially as it has done good in the world.  I have been thinking about this idea the last few weeks.  So if PowerShell is glue, it isn't Gorilla Glue, it isn't Elmer's Glue...  I can't think of a glue that doesn't come across as sticky and then rigid.

A Google Search for "connective material" lands me in a large collection of biology search results.  Google seemed to think that when I typed "material" that I meant "tissue".  Biology was my worst subject in high school, but I won't hold a grudge.  Per Wikipedia "Connective tissue (CT) is a kind of biological tissue that supports, connects, or separates different types of tissues and organs of the body. "  I think I can get behind "Supporting", "Connecting", and, "separating".

Now I need to poor through Google Images, and find something too replace that Elmer's Glue bottle in my PowerPoint slide decks.
(from http://www.3dscience.com/3D_Models/Human_Anatomy/Female_Systems/Female_Connective_tissue.php)

Friday, January 4, 2013

PowerShell Reporting and a rant

Today I worked my way through +Don Jones's "Making Historical and Trend Reports in PowerShell".  This is a Beta 1 copy, but something really popped when I read it.

Let me say a few disjoint items, then tie them together.


  1. When it comes to reporting data, anything that puts the output in front of a user fulfills the idea of reporting to some extent.
  2. There was an earlier E-Book on HTML Reporting in November when I was preparing a user group presentation on reporting options.  I started a brief discussion @ http://powershell.org/discuss/viewtopic.php?f=2&t=665 where I was really grasping at straws on how to go from table reporting to a more graphic, chart based report.  
  3. In the newer ebook, Don references the horrors that come from taking something simple like Excel and COM or the Office .NET Interop (http://www.microsoft.com/en-us/download/details.aspx?id=3508) and building via commands data and charts.  It is amazing that even with simple parts that this does become a blob of code that automates user actions, which is not the ideal way you want to generate a report.
  4. Will was at one point working with Primal Forms to build a basic UI.  I had a problem with this because Primal Forms builds a PS1 from its project contributing all of the ugly winforms initialization.   If you want to change something after you have modified the generated PS1 then you have use the original project, and re add all of your own code.  At least this was true when we were looking at it several months ago.
  5. One of the things I see come up is when is PowerShell the right tool, and when should you move on to a more robust development solution.  Sometime this grey area can be tough to see, but once your single .PS1 gets giant, and you think, "Hey should I move this to a module?" you should also ask should I move the code to C#?  Because at this point managing the Project can be a key component.  I am not discounting other reason to go between .PS1 .PSM1 and .DLL, but size can be a factor.
  6. For the January DFW PowerShell meeting, +Michael Cruz is geoing to show us a WPF UI built in PowerShell.  He sent me a copy of this to show me the direction.  What I absolutely loved about it, was that he was copying the XAML directly into an @String.  This meant that if something needed to be tweaked it could be pulled out, changed, and with some restrictions everything would work when it was copied back.  We have the maintainability sitting great, and the convenience of a single file deployment. 
  7. Along with with the Reporting Options presentation, I had built up a sample HTML Generation process that used the Google Charts API to show some simple information.  To get the information into the charts, I converted PowerShell hashtables to AJAX. (See below)  I was going for more proof of concept then the Reusable PowerShell style and sole that Don put in the HTML reporting document.  I would have liked to make it work the way we had discussed in the PowerShell.org discussion, but it looked like it was just too complicated wiring the <DIV> tags in HTML and making sure there was one for any chart or table generated against them.
  8. When doing the Reporting Options I went with the Google AP vs. the MS API that Don had used because there were more online examples of hand wiring the Javascript.
  9. look past the code below.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<#
  'title': 'Final gold medal count for 2012 olympics',
  'titleTextStyle': { color: 'black', fontName: '"Verdana"', fontSize: 12 },
  'width': 350,
  'height': 350,
  'backgroundColor': 'white',
  'chartArea': { 'width': '90%', 'height': '70%' },
  'pieSliceText': 'percentage',
  'tooltip': { textStyle: { color: 'black', fontName: '"Verdana"',fontSize:12}},
  'is3D': true
    vAxis: {title: 'Candidate',  titleTextStyle: {color: 'blue'}},
20           hAxis: {title: '# Contributions',  titleTextStyle: {color: 'blue'}}
#>
function GooglePieChart {
Param ($Data = @{'Yin'=1;'Yang'=1},
 $HeaderRow = [ordered]@{'Topping'='string';'Slices'='number'},
 $options = @{"title"="Test Title";
            "width"=500;
            "height"=500;           
            "is3D"="true";
            'legend'='bottom'; # top, none, left, right, bottom
            "titleTextStyle"=@{"color"="black";
                                "fontName"='"Verdana"';
                                "fontsize"=12};
            'backgroundColor'= 'white';
            'pieSliceText'= 'percentage'
            }
 )
 
$HTML = @"
<html>
  <head>
    <!--Load the AJAX API-->
    <!--     background-color:#b0e0e6; -->
    <style>
    .left
    {
        float:left;
        width:300px;   
    }
    .right
    {
        float:right;
        width:300px;     
    }
    </style>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
 
      // Load the Visualization API and the piechart package.
      google.load('visualization', '1.0', {'packages':['corechart','table']});
      google.load('visualization', '1.0', {'packages':['corechart','table']});      
      //google.load('visualization', '1.0', {'packages':['piechart']});
 
 
      // Set a callback to run when the Google Visualization API is loaded.
      google.setOnLoadCallback(drawChart);
 
      // Callback that creates and populates a data table,
      // instantiates the pie chart, passes in the data and
      // draws it.
       function drawChart() {
 
        // Create the data table.
        var data = new google.visualization.DataTable();
        
"@
$columns = $HeaderRow
$HTML +=  $columns.GetEnumerator() | % { "data.addColumn('$($_.Value)','$($_.Name)');`n"; }   
 
if ($data.Gettype().Name -eq "OrderedDictionary")
{
    $HTML += "data.addRows($($data.count));`n"
    $i=0;
    $HTML +=  $data.GetEnumerator() | % {
        "data.setValue($i,0,'$($_.Name)');`ndata.setValue($i,1,$($_.Value));`n";
         $i++; }
} elseif ($data[0] -is "object[]") {
    #$JSON =     ($Data | ConvertTo-JSON -compress) -replace '([{]"value":)|(,"Count":2[}])'
    $JSON =     ConvertTo-JSON -compress $Data
    $HTML += "data.addRows($JSON);`n"
} else {
    $HTML += "data.addRows($($data.count));`n"
    $i=0;
    $a=@();$a += $columns.keys | %{ $_ };
    $HTML +=  $data| % {
        "data.setValue($i,0,'$($_.$($a[0]))');`ndata.setValue($i,1,$($_.$($a[1])));`n";
         $i++; }
}
    
$HTML += "var options =$($options | ConvertTo-Json  );"
$HTML += @"
        // Instantiate and draw our chart, passing in some options.
      //  alert('Test')
         
        var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
        chart.draw(data, options);
         
        var chart2 = new google.visualization.BarChart(document.getElementById('chart_div2'));
        chart2.draw(data, options);
 
        var visualization = new google.visualization.Table(document.getElementById('table'));
        visualization.draw(data, options);
 
      }
    </script>
  </head>
 
  <body>
    <!--Div that will hold the pie chart-->
    <div>
    <div class="left" id="chart_div"></div>
     
    <div class="right" id="chart_div2"></div>
    </Div>
     
 
    <div id="table"></div>
 
    <div id="Links">
    <p>If you don't see anything try a refresh.</P>
    <div><a href="">https://developers.google.com/chart/interactive/docs/examples</a>https://developers.google.com/chart/interactive/docs/examples </div>
    <!-- <div><a href=""></a> </div> -->
     
    </div>
 
 
  </body>
</html>
"@
 write-output $HTML
}
#  $oie = Out-InternetExplorer -NavigateTo "$home\Dropbox\Nov12SIG\googlechart.html" -PassThru
#$oie = Out-InternetExplorer -Text ([io.file]::ReadAllText("$home\Dropbox\Nov12SIG\googlechart.html")) -PassThru
 
$Source = [ordered]@{'Apples'= 3;'Onions'=1;'Olives'= 1;'Zucchini'=1;'Pepperoni'= 2}
#$Source = @(('Mushrooms', 3),('pie',1),('Olives',1),('Zucchini',1),('Pepperoni', 2))
#$source = @([PSCustomObject][ordered]@{"Fruit"="Apple";"Amount"="2"},[PSCustomObject][ordered]@{"Fruit"="orange";"Amount"="5"})
$ObjectHeader =[ordered]@{'Fruit'='string';'Amount'='number'}
$html = GooglePieChart $Source -HeaderRow $ObjectHeader


I was looking at the SSRS report generated in the current ebook, and I thought, oh, all I need to do to is save this HTML and just wire up some AJAX or ViewState tweaks to inject new data.

I mean if you need to persist data and you can use a database, use that database.  Sometimes though I just need a snapshot of a data collection and get an idea for the top item types and see the data in something that can be easily passed to someone else for feedback or to alert them to issues.

Despite all of the above rambling, the "POP" that I had was about complexity and maintenance.  Populating an HTML or Excel report from PowerShell is the role that PowerShell should fill.  If you want a single HTML report to attach to an email, then generate it in one of the many HTML tools out there.  Then wire your data to it.  Trying to Define the report in PowerShell is where it can be cumbersome, ugly, bulky, and a maintenance nightmare.  If you want a report in Excel Build your excel document and import your data from CSV.

I know the idea of reporting tools are ages old, but really they are built for this, so we should use them.
Add to this things like XAML that were designed for MVVM development and the abstraction we need is built in.

Good night,
        Josh