Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Changes_James.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Added examples.docx and graphvizAttributes.xlsx in docs

Changed Record.ps1 Adding a TitleSpan parameter and making TableStyle, Fontname, FillColor, and TitlePosition into parameters-with-defaults instead of constants.
Changed node.ps1 Adding parameters Shape and Label
Changed edge.ps1 Adding parameters Label, Style and Direction
and allowing parameters To, From, Label, Style and Direction to be properties of objects passed from the pipeline.
This required moving the setting of attributes from the begin block into process block (multiple items couldn't be piped in before).

Added new commands (explained in examples.docx)
cells
New-NodeAttributeSet
New-EdgeAttributeSet
svg as an all in one to call graph and then export-PSGraph. Depending on the name used to call it it will change the format passed to Export-PSGraph
Added Global aliases for xxxGraph --> svg .

Changed Get-Translated argument so if parameter value is null or empty no paramter is created.
Changed Export-PsGraph so that Destinationpath resolved from a partial path (but not a null or empty one)
(Net effect of these two changes is calling with -Destinationpath "" doesn't path an output path to graphviz and output goes to std out)
Changed Export-PsGraph so graphviz output is put into a variable instead of null, and this variable is returned if DestinationPath is empty.
4 changes: 2 additions & 2 deletions PSGraph/PSGraph.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
# NestedModules = @()

# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = @('Edge','Entity','Export-PSGraph','Graph','Inline','Install-GraphViz','Node','Rank','Record','Row','Set-NodeFormatScript','Show-PSGraph','SubGraph')
FunctionsToExport = @('Cells', 'Edge','Entity','Export-PSGraph','Graph','Inline','Install-GraphViz','New-EdgeAttributeSet', 'New-NodeAttributeSet', 'Node','Rank','Record','Row','Set-NodeFormatScript','Show-PSGraph','SubGraph','svg')

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()
Expand All @@ -78,7 +78,7 @@
VariablesToExport = '*'

# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @('digraph')
AliasesToExport = @('digraph','EdgeAttributes','NodeAttributes','cmapxGraph', 'dotGraph', 'gifGraph', 'imapGraph', 'jp2Graph', 'jpgGraph', 'jsonGraph', 'pdfGraph', 'plainGraph', 'pngGraph', 'svgGraph')

# DSC resources to export from this module
# DscResourcesToExport = @()
Expand Down
4 changes: 3 additions & 1 deletion PSGraph/Private/Get-TranslatedArgument.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ function Get-TranslatedArgument( $InputObject )
foreach ( $key in $InputObject.keys )
{
Write-Debug $key
if ( $null -ne $key -and $paramLookup.ContainsKey( $key ) )
#A hashtable key can't be null. Don't pass null or empty string. (But allow zero, false etc.)
#This means we can get output to std-out by making the destination an empty string.
if ( -not [string]::IsNullOrEmpty($InputObject[$key]) -and $paramLookup.ContainsKey( $key ) )
{
$newArgument = $paramLookup[$key]
if ( $newArgument -like '*{0}*' )
Expand Down
72 changes: 72 additions & 0 deletions PSGraph/Public/Cells.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
function Cells
{
[OutputType('System.String')]
[cmdletbinding()]
param(
[Parameter(
Mandatory,
Position = 0,
ValueFromPipeline
)]
$InputObject,

$Properties = '*' ,
[string[]]
$ExcludeProperty,
[ValidateSet('LEFT','CENTER','RIGHT')]
$Align = "LEFT",
[string]
$PortPoroperty,
[switch]
$HtmlEncode,
[switch]
$NoHeader
)
begin {
$portlist = @{}
$script:Header = @()
}
process
{
foreach ($Targetdata in $InputObject) {
if (-not $script:Header) {
foreach ($p in $Properties) {
$InputObject.PSObject.Properties.where({$_.name -like $p}).Name | ForEach-Object {
if ($_ -notin $script:Header) {$script:Header += $_ }
}
}
foreach ($exclusion in $ExcludeProperty) {$script:Header = $script:Header -notlike $exclusion}
if (-not $NoHeader) {
$row = "<tr>"
foreach ($Name in $script:Header) {
$row += ('<TD ALIGN="{1}"><B>{0}</B></TD>' -f $Name, $Align.toupper())
}
$row + "</tr>"
}
}
$row = "<tr>"
foreach ($Name in $script:Header) {
$cellText = ($TargetData.$Name).tostring()
$newPortName = $cellText -replace '\W',''

if ( $PortPoroperty -eq $name -and -not $portlist[$newPortName])
{
$row += ('<TD PORT="{1}" ALIGN="{0}">' -f $Align.toupper(), $newPortName)
$portlist[$newPortName] = $true
}
else
{
$row += ('<TD ALIGN="{0}">' -f $Align.toupper())
}
if ($HtmlEncode)
{
$row += ([System.Net.WebUtility]::HtmlEncode($cellText)) + '</TD>'
}
else {
$row += $cellText + '</TD>'
}
}
$row + "</tr>"
}
}
}
62 changes: 51 additions & 11 deletions PSGraph/Public/Edge.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ function Edge
[Parameter(
Mandatory = $true,
Position = 0,
ParameterSetName = 'Node'
ParameterSetName = 'Node',
ValueFromPipelineByPropertyName = $true
)]
[Parameter(
Mandatory = $true,
Expand All @@ -68,7 +69,8 @@ function Edge
[Parameter(
Mandatory = $false,
Position = 1,
ParameterSetName = 'Node'
ParameterSetName = 'Node',
ValueFromPipelineByPropertyName = $true
)]
[alias('Destination', 'TargetName', 'RightHandSide', 'rhs')]
[string[]]
Expand Down Expand Up @@ -103,8 +105,7 @@ function Edge
$Node,

# start node script or source of edge
[Parameter(
ParameterSetName = 'script')]
[Parameter(ParameterSetName = 'script')]
[alias('FromScriptBlock', 'SourceScript')]
[scriptblock]
$FromScript = {$_},
Expand All @@ -119,24 +120,60 @@ function Edge
[string]
$LiteralAttribute = $null,

#Label for the edge, escaped or HTML Text
[Parameter(ValueFromPipelineByPropertyName = $true)]
[AllowEmptyString()]
[String]
$Label,

#Style for the edge, dashed, solid etc.
[Parameter(ValueFromPipelineByPropertyName = $true)]
[ValidateSet("dashed", "dotted", "solid", "invis", "bold" , "tapered")]
[String]
$Style,

#Indicates which ends of the edge should be decorated with an arrowhead.
[Parameter(ValueFromPipelineByPropertyName = $true)]
[ValidateSet('forward','back','both','none')]
[String]
$Direction,

# Not used, but can be specified for verbosity
[switch]
$Default
)

begin
{
if ( -Not [string]::IsNullOrEmpty($LiteralAttribute) )
#re-create any scriptblock passed as a parameter, otherwise variables in this function are out of its scope.
if ($ToScript)
{
$GraphVizAttribute = $LiteralAttribute
$ToScript = [scriptblock]::create( $ToScript )
}
if ($FromScript)
{
$FromScript = [scriptblock]::create( $FromScript )
}
#moved handling of $LiteralAttribute to make behavior later easier to follow
}

process
{
try
{

#Don't want to make all possible attributes parameters, just key ones, label , direction and style for the edge
if ($PSBoundParameters.ContainsKey('Label')) #Label may be an empty string.
{
$Attributes['label'] = $Label
}
if ($Style) #Style must not be empty but wrong case may slip though
{
$Attributes['style'] = $Style.ToLower()
}
if ($Direction) #Same agaign
{
$Attributes['dir'] = $Direction.ToLower()
}
if ( $Node.count -eq 1 -and $node[0] -is [Hashtable] -and !$PSBoundParameters.ContainsKey('FromScript') -and !$PSBoundParameters.ContainsKey('ToScript') )
{
#Deducing the pattern 'edge @{}' as default edge definition
Expand Down Expand Up @@ -165,12 +202,15 @@ function Edge
{
foreach ( $tNode in $To )
{
if ( [string]::IsNullOrEmpty( $LiteralAttribute ) )
if ( $LiteralAttribute)
{
$GraphVizAttribute = ConvertTo-GraphVizAttribute -Attributes $Attributes -From $sNode -To $tNode
$GraphVizAttribute = $LiteralAttribute
}

if ($GraphVizAttribute -match 'ltail=' -or $GraphVizAttribute -match 'lhead=')
else
{
$GraphVizAttribute = ConvertTo-GraphVizAttribute -Attributes $Attributes -From $sNode -To $tNode
}
if ( $GraphVizAttribute -match 'ltail=' -or $GraphVizAttribute -match 'lhead=')
{
# our subgraph to subgraph edges can crash the layout engine
# adding invisible edge for layout hints helps resolve this
Expand Down
13 changes: 11 additions & 2 deletions PSGraph/Public/Export-PSGraph.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ function Export-PSGraph
if ( $useStandardInput )
{
Write-Verbose 'Processing standard input'
#Create a destination path if there is none, but if it is an empty string don't resolve it
#This allows empty string to mean "send graphviz output to stdOut"
if ( -Not $PSBoundParameters.ContainsKey( 'DestinationPath' ) )
{
Write-Verbose ' Creating temporary path to save graph'
Expand All @@ -181,15 +183,22 @@ function Export-PSGraph
}
$PSBoundParameters["DestinationPath"] = Join-Path ([system.io.path]::GetTempPath()) "$file.$OutputFormat"
}

elseif (-not [string]::IsNullOrEmpty($PSBoundParameters["DestinationPath"]) )
{
$PSBoundParameters["DestinationPath"] = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($DestinationPath)
}
$arguments = Get-GraphVizArgument $PSBoundParameters
Write-Verbose " Arguments: $($arguments -join ' ')"

$null = $standardInput.ToString() | & $graphViz @($arguments)
$result = $standardInput.ToString() | & $graphViz @($arguments)
if ($LastExitCode)
{
Write-Error -ErrorAction Stop -Exception ([System.Management.Automation.ParseException]::New())
}
elseif ( [string]::IsNullOrEmpty($PSBoundParameters["DestinationPath"]))
{
return $result
}

if ( $ShowGraph )
{
Expand Down
93 changes: 93 additions & 0 deletions PSGraph/Public/New-EdgeAttributeSet.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Function New-EdgeAttributeSet {
<#
.synopsis
Creates a set of attributes for the node command.
.description
The node commands take a hash table of attributes, but it can be a memory and typing test to write it.
Names are case sensitive so specifying blue works but Blue fails,
Is the font name "courier New", "Courier new" or "Courier_New" ? Is the direction "back or backward"
This function uses argument completers to fill in as many of the parameters as possible and converts
values to lower case where needed.
.example
$edgeattr = New-EdgeAttr ibuteSet -direction both -arrowhead 'crow' -arrowtail 'lcrow' -color blue -fontname 'Calibri' -label "test" -style dashed
This defines a two way dashed arrow, in blue with a "crow" head and left-half-crow tail, with "test" as in black calibri as the style
Note that the argument completer will help arrows in line with the grammar at http://www.graphviz.org/doc/info/arrows.html,
only 42 of the 60 possible permuations are valid, and it allows double arrows but doesn't check for a redundant "none" in the sytle
#>
[CmdletBinding()]
[Alias('EdgeAttributes')]
Param(
#Style of arrowhead on the head node of an edge. This will only appear if the dir attribute is "forward" or "both".
[string]
$arrowhead,
#Multiplicative scale factor for arrowheads
[double]
$arrowsize,
#Style of arrowhead on the tail node of an edge. This will only appear if the dir attribute is "back" or "both"
[string]
$arrowtail,
#Basic drawing color for graphics, not text (which requires font color to be set)
[string]
$color,
#If false, the edge is not used in ranking the nodes (defaults to true)
[bool]
$constraint,
#Indicates which ends of the edge should be decorated with an arrowhead.
[ValidateSet('forward','back','both','none')]
[String]
$direction,
[string]
#Color used for text.
$fontcolor,
#Font used for text.
[string]
$fontname,
#Font size, in points, used for text.
[double]
$fontsize,
#Text label to be placed near head of edge
[string]
$headlabel,
#Text label attached to the edge
[string]
$label,
#Color used for headlabel and taillabel. If not set, defaults to edge's fontcolor.
[string]
$labelfontcolor,
#Font used for headlabel and taillabel. If not set, defaults to edge's fontname.
[string]
$labelfontname,
#Font size, in points, used for headlabel and taillabel. If not set, defaults to edge's fontsize.
[double]
$labelfontsize,
#Preferred edge length, in inches.
[double]
$length,
#width of the pen, in points, used to draw lines and curves, including the boundaries of edges. It has no effect on text.
[double]
$penwidth,
#Style for the edge, dashed, solid etc.
[ValidateSet("dashed", "dotted", "solid", "invis", "bold" , "tapered")]
[String]
$style,
#Text label to be placed near tail of edge.
[string]
$taillabel
)
$values = @{}
#Attributes where name is shortened and doesn't match the parameter name
if ($direction) {$values['dir'] = $direction.ToLower()}
if ($length) {$values['len'] = $length}
#Attributes where graphviz expects all lower case and other case might have got through
foreach ($param in @( 'arrowhead', 'arrowtail', 'color', 'fontcolor', 'labelfontcolor','style')) {
if ($PSBoundParameters[$param]) {$values[$param] = $PSBoundParameters[$param].ToLower() }
}
#Attributes that can go through unchanged.
foreach ($param in @( 'arrowsize', 'constraint', 'fontname', 'fontsize', 'headlabel',
'label', 'labelfontname', 'labelfontsize', 'penwidth', 'taillabel')) {
if ($PSBoundParameters[$param]) {$values[$param] = $PSBoundParameters[$param].ToLower() }
}

$values
}

Loading