Skip to content

Commit d638526

Browse files
Merge pull request dataplat#3892 from marcingminski/ConvertTo-DbaTimeline
Convert to dba timeline
2 parents e5e583a + e611a82 commit d638526

File tree

4 files changed

+322
-2
lines changed

4 files changed

+322
-2
lines changed

functions/ConvertTo-DbaTimeline.ps1

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
function ConvertTo-DbaTimeline {
2+
<#
3+
.SYNOPSIS
4+
Converts InputObject to a html timeline using Google Chart
5+
6+
.DESCRIPTION
7+
This function accepts input as pipeline from the following psdbatools functions:
8+
Get-DbaAgentJobHistory
9+
Get-DbaBackupHistory
10+
(more to come...)
11+
And generates Bootstrap based, HTML file with Google Chart Timeline
12+
13+
.PARAMETER InputObject
14+
15+
Pipe input, must an output from the above functions.
16+
17+
.PARAMETER EnableException
18+
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
19+
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
20+
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
21+
22+
.NOTES
23+
Tags: Chart
24+
Author: Marcin Gminski (@marcingminski)
25+
Dependency: ConvertTo-JsDate, Convert-DbaTimelineStatusColor
26+
27+
Website: https://dbatools.io
28+
Copyright: (C) Chrissy LeMaire, clemaire@gmail.com
29+
- License: MIT https://opensource.org/licenses/MIT
30+
31+
.LINK
32+
https://dbatools.io/ConvertTo-DbaTimeline
33+
34+
.EXAMPLE
35+
Get-DbaAgentJobHistory -SqlInstance sql-1 -StartDate '2018-08-13 00:00' -EndDate '2018-08-13 23:59' -NoJobSteps | ConvertTo-DbaTimeline | Out-File C:\temp\DbaAgentJobHistory.html -Encoding ASCII
36+
37+
Creates an output file containing a pretty timeline for all of the agent job history results for sql-1 the whole day of 2018-08-13
38+
39+
.EXAMPLE
40+
Get-DbaRegisteredServer -SqlInstance sqlcm | Get-DbaBackupHistory -Since '2018-08-13 00:00' | ConvertTo-DbaTimeline | Out-File C:\temp\DbaBackupHistory.html -Encoding ASCII
41+
42+
Creates an output file containing a pretty timeline for the agent job history since 2018-08-13 for all of the registered servers on sqlcm
43+
44+
.EXAMPLE
45+
$messageParameters = @{
46+
Subject = "Backup history for sql2017 and sql2016"
47+
Body = Get-DbaBackupHistory -SqlInstance sql2017, sql2016 -Since '2018-08-13 00:00' | ConvertTo-DbaTimeline
48+
From = "dba@ad.local"
49+
To = "dba@ad.local"
50+
SmtpServer = "smtp.ad.local"
51+
}
52+
Send-MailMessage @messageParameters -BodyAsHtml
53+
54+
Sends an email to dba@ad.local with the results of Get-DbaBackupHistory. Note that viewing these reports may not be supported in all email clients.
55+
#>
56+
57+
[CmdletBinding()]
58+
Param (
59+
[parameter(Mandatory, ValueFromPipeline)]
60+
[object[]]$InputObject,
61+
[switch]$EnableException
62+
)
63+
begin {
64+
$body = $servers = @()
65+
$begin = @"
66+
<html>
67+
<head>
68+
<!-- Developed by Marcin Gminski, https://marcin.gminski.net, 2018 -->
69+
<!-- Load jQuery required to autosize timeline -->
70+
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
71+
<!-- Load Bootstrap -->
72+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
73+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
74+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
75+
<!-- Load Google Charts library -->
76+
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
77+
<!-- a bit of custom styling to work with bootstrap grid -->
78+
<style>
79+
80+
html,body{height:100%;background-color:#c2c2c2;}
81+
.viewport {height:100%}
82+
83+
.chart{
84+
background-color:#fff;
85+
text-align:left;
86+
padding:0;
87+
border:1px solid #7D7D7D;
88+
-webkit-box-shadow:1px 1px 3px 0 rgba(0,0,0,.45);
89+
-moz-box-shadow:1px 1px 3px 0 rgba(0,0,0,.45);
90+
box-shadow:1px 1px 3px 0 rgba(0,0,0,.45)
91+
}
92+
.badge-custom{background-color:#939}
93+
.container {
94+
height:100%;
95+
}
96+
.fill{
97+
width:100%;
98+
height:100%;
99+
min-height:100%;
100+
padding:10px;
101+
}
102+
.timeline-tooltip{
103+
border:1px solid #E0E0E0;
104+
font-family:Arial,Helvetica;
105+
font-size:10pt;
106+
padding:12px
107+
}
108+
.timeline-tooltip div{padding:6px}
109+
.timeline-tooltip span{font-weight:700}
110+
</style>
111+
<script type="text/javascript">
112+
google.charts.load('43', {'packages':['timeline']});
113+
google.charts.setOnLoadCallback(drawChart);
114+
function drawChart() {
115+
var container = document.getElementById('Chart');
116+
var chart = new google.visualization.Timeline(container);
117+
var dataTable = new google.visualization.DataTable();
118+
dataTable.addColumn({type: 'string', id: 'vLabel'});
119+
dataTable.addColumn({type: 'string', id: 'hLabel'});
120+
dataTable.addColumn({type: 'string', role: 'style' });
121+
dataTable.addColumn({type: 'date', id: 'date_start'});
122+
dataTable.addColumn({type: 'date', id: 'date_end'});
123+
124+
dataTable.addRows([
125+
"@
126+
}
127+
128+
process {
129+
# build html container
130+
$BaseObject = $InputObject.PsObject.BaseObject
131+
132+
# create server list to support multiple servers
133+
if ($InputObject[0].SqlInstance -notin $servers) {
134+
$servers += $InputObject[0].SqlInstance
135+
}
136+
# This is where do column mapping.
137+
138+
# Check for types - this will help support if someone assigns a variable then pipes
139+
# AgentJobHistory is a forced type while backuphistory is a legit type
140+
if ($InputObject[0].TypeName -eq 'AgentJobHistory') {
141+
$CallerName = "Get-DbaAgentJobHistory"
142+
$data = $InputObject | Select-Object @{ Name = "SqlInstance"; Expression = { $_.SqlInstance } }, @{ Name = "InstanceName"; Expression = { $_.InstanceName } }, @{ Name = "vLabel"; Expression = { $_.Job } }, @{ Name = "hLabel"; Expression = { $_.Status } }, @{ Name = "Style"; Expression = { $(Convert-DbaTimelineStatusColor($_.Status)) } }, @{ Name = "StartDate"; Expression = { $(ConvertTo-JsDate($_.StartDate)) } }, @{ Name = "EndDate"; Expression = { $(ConvertTo-JsDate($_.EndDate)) } }
143+
144+
}
145+
elseif ($InputObject[0] -is [Sqlcollaborative.Dbatools.Database.BackupHistory]) {
146+
$CallerName = "Get-DbaBackupHistory"
147+
$data = $InputObject | Select-Object @{ Name = "SqlInstance"; Expression = { $_.SqlInstance } }, @{ Name = "InstanceName"; Expression = { $_.InstanceName } }, @{ Name = "vLabel"; Expression = { $_.Database } }, @{ Name = "hLabel"; Expression = { $_.Type } }, @{ Name = "StartDate"; Expression = { $(ConvertTo-JsDate($_.Start)) } }, @{ Name = "EndDate"; Expression = { $(ConvertTo-JsDate($_.End)) } }
148+
}
149+
else {
150+
# sorry to be so formal, can't help it ;)
151+
Stop-Function -Message "Unsupported input data. To request support for additional commands, please file an issue at dbatools.io/issues and we'll take a look"
152+
return
153+
}
154+
$body += "$($data | ForEach-Object{ "['$($_.vLabel)','$($_.hLabel)','$($_.Style)',$($_.StartDate), $($_.EndDate)]," })"
155+
}
156+
end {
157+
if (Test-FunctionInterrupt) { return }
158+
$end = @"
159+
]);
160+
var paddingHeight = 20;
161+
var rowHeight = dataTable.getNumberOfRows() * 41;
162+
var chartHeight = rowHeight + paddingHeight;
163+
dataTable.insertColumn(2, {type: 'string', role: 'tooltip', p: {html: true}});
164+
var dateFormat = new google.visualization.DateFormat({
165+
pattern: 'dd/MM/yy HH:mm:ss'
166+
});
167+
for (var i = 0; i < dataTable.getNumberOfRows(); i++) {
168+
var duration = (dataTable.getValue(i, 5).getTime() - dataTable.getValue(i, 4).getTime()) / 1000;
169+
var hours = parseInt( duration / 3600 ) % 24;
170+
var minutes = parseInt( duration / 60 ) % 60;
171+
var seconds = duration % 60;
172+
var tooltip = '<div class="timeline-tooltip"><span>' +
173+
dataTable.getValue(i, 1).split(",").join("<br />") + '</span></div><div class="timeline-tooltip"><span>' +
174+
dataTable.getValue(i, 0) + '</span>: ' +
175+
dateFormat.formatValue(dataTable.getValue(i, 4)) + ' - ' +
176+
dateFormat.formatValue(dataTable.getValue(i, 5)) + '</div>' +
177+
'<div class="timeline-tooltip"><span>Duration: </span>' +
178+
hours + 'h ' + minutes + 'm ' + seconds + 's ';
179+
dataTable.setValue(i, 2, tooltip);
180+
}
181+
var options = {
182+
timeline: {
183+
rowLabelStyle: { },
184+
barLabelStyle: { },
185+
},
186+
hAxis: {
187+
format: 'dd/MM HH:mm',
188+
},
189+
}
190+
// Autosize chart. It would not be enough to just count rows and expand based on row height as there can be overlappig rows.
191+
// this will draw the chart, get the size of the underlying div and apply that size to the parent container and redraw:
192+
chart.draw(dataTable, options);
193+
// get the size of the chold div:
194+
var realheight= parseInt(`$("#Chart div:first-child div:first-child div:first-child div svg").attr( "height"))+70;
195+
// set the height:
196+
options.height=realheight
197+
// draw again:
198+
chart.draw(dataTable, options);
199+
}
200+
</script>
201+
</head>
202+
<body>
203+
<div class="container-fluid">
204+
<div class="pull-left"><h3><code>$($CallerName)</code> timeline for server <code>$($servers -join ', ')</code></h3></div><div class="pull-right text-right"><img class="text-right" style="vertical-align:bottom; margin-top: 10px;" src="https://dbatools.io/wp-content/uploads/2016/05/dbatools-logo-1.png" width=150></div>
205+
<div class="clearfix"></div>
206+
<div class="col-12">
207+
<div class="chart" id="Chart"></div>
208+
</div>
209+
<hr>
210+
<p><a href="https://dbatools.io">dbatools.io</a> - the community's sql powershell module. Find us on Twitter: <a href="https://twitter.com/psdbatools">@psdbatools</a> | Chart by <a href="https://twitter.com/marcingminski">@marcingminski</a></p>
211+
</div>
212+
</body>
213+
</html>
214+
"@
215+
$begin, $body, $end
216+
}
217+
}

functions/Get-DbaAgentJobHistory.ps1

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,13 @@ function Get-DbaAgentJobHistory {
269269
}
270270
Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name OutputFileName -value $outname
271271
Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name RemoteOutputFileName -value $outremote
272-
Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message, OutputFileName, RemoteOutputFileName
272+
# Add this in for easier ConvertTo-DbaTimeline Support
273+
Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name TypeName -value AgentJobHistory
274+
Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message, OutputFileName, RemoteOutputFileName -TypeName AgentJobHistory
273275
}
274276
else {
275-
Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message
277+
Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name TypeName -value AgentJobHistory
278+
Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message -TypeName AgentJobHistory
276279
}
277280

278281
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
function Convert-DbaTimelineStatusColor {
2+
<#
3+
.SYNOPSIS
4+
Converts literal string status to a html color
5+
6+
.DESCRIPTION
7+
This function acceptes Agnet Job status as literal string input and covnerts to html color.
8+
This is internal function, part of ConvertTo-DbaTimeline
9+
10+
.PARAMETER Status
11+
12+
The Status input parameter must be a valid SQL Agent Job status as literal string as defined in MS Books:
13+
Status of the job execution:
14+
Failed
15+
Succeeded
16+
Retry
17+
Canceled
18+
In Progress
19+
20+
.NOTES
21+
Tags: Internal
22+
Author: Marcin Gminski (@marcingminski)
23+
24+
Dependency: None
25+
Requirements: None
26+
27+
Website: https://dbatools.io
28+
Copyright: (C) Chrissy LeMaire, clemaire@gmail.com
29+
- License: MIT https://opensource.org/licenses/MIT
30+
31+
.LINK
32+
--internal function, not exposed to end user
33+
34+
.EXAMPLE
35+
Convert-DbaTimelineStatusColor ("Succeeded")
36+
37+
Returned string: #36B300
38+
#>
39+
40+
[CmdletBinding()]
41+
param (
42+
[Parameter(Mandatory = $true)]
43+
[string]
44+
$Status
45+
)
46+
$out = switch($Status){
47+
"Failed" {"#FF3D3D"}
48+
"Succeeded" {"#36B300"}
49+
"Retry" {"#FFFF00"}
50+
"Canceled" {"#C2C2C2"}
51+
"In Progress" {"#00CCFF"}
52+
default {"#FF00CC"}
53+
}
54+
return $out
55+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
function ConvertTo-JsDate {
2+
<#
3+
.SYNOPSIS
4+
Converts Datetime input to a Java Script date format
5+
6+
.DESCRIPTION
7+
This function acceptes date time input and converts to a Java script compatible format.
8+
Java Script date time format:
9+
New date (yyyy, MM, dd, HH, mm, ss)
10+
11+
This is internal function part of ConvertTo-DbaTimeline
12+
13+
.PARAMETER InputDate
14+
15+
The InputDate parameter must be a valid datetime type
16+
17+
.NOTES
18+
Tags: Internal
19+
Author: Marcin Gminski (@marcingminski)
20+
21+
Dependency: None
22+
Requirements: None
23+
24+
Website: https://dbatools.io
25+
Copyright: (C) Chrissy LeMaire, clemaire@gmail.com
26+
- License: MIT https://opensource.org/licenses/MIT
27+
28+
.LINK
29+
--internal function, not exposed to end user
30+
31+
.EXAMPLE
32+
ConvertTo-JsDate (Get-Date)
33+
34+
Returned output: new Date(2018, 7, 14, 07, 40, 42)
35+
#>
36+
37+
[CmdletBinding()]
38+
param (
39+
[Parameter(Mandatory = $true)]
40+
[datetime]
41+
$InputDate
42+
)
43+
[string]$out = "new Date($(Get-Date $InputDate -format "yyyy"), $($(Get-Date $InputDate -format "MM")-1), $(Get-Date $InputDate -format "dd"), $(Get-Date $InputDate -format "HH"), $(Get-Date $InputDate -format "mm"), $(Get-Date $InputDate -format "ss"))"
44+
return $out
45+
}

0 commit comments

Comments
 (0)