Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dropdown list for selecting viewName to BuildFilter view #5

Merged
merged 11 commits into from
Jun 21, 2016
2 changes: 1 addition & 1 deletion ApiFun/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ private static async Task CheckUnknown()
var date = DateTimeOffset.UtcNow - TimeSpan.FromDays(1);
var populator = new BuildTablePopulator(account.CreateCloudTableClient(), CreateClient(), Console.Out);
var table = account.CreateCloudTableClient().GetTableReference(AzureConstants.TableNames.BuildResultDate);
foreach (var entity in buildUtil.GetBuildResults(date, ClassificationKind.Unknown, AzureUtil.ViewNameAll))
foreach (var entity in buildUtil.GetBuildResultsByKindName(date, BuildResultClassification.Unknown.Name, AzureUtil.ViewNameAll))
{
var entityDate = DateKey.Parse(entity.PartitionKey);
var before = new DateKey(entityDate.Date.AddDays(-1));
Expand Down
28 changes: 26 additions & 2 deletions Dashboard.Storage/Azure/BuildUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ public List<BuildResultEntity> GetBuildResults(DateTimeOffset startDate, string
return _buildResultDateTable.ExecuteQuery(query).ToList();
}

public List<BuildResultEntity> GetBuildResults(DateTimeOffset startDate, ClassificationKind kind, string viewName)
public List<BuildResultEntity> GetBuildResultsByKindName(DateTimeOffset startDate, string kindName, string viewName)
{
var filter = FilterUtil
.SinceDate(ColumnNames.PartitionKey, startDate)
.And(FilterUtil.Column(nameof(BuildResultEntity.ClassificationKindRaw), kind.ToString()));
.And(FilterUtil.Column(nameof(BuildResultEntity.ClassificationName), kindName));
filter = FilterView(filter, viewName);
var query = new TableQuery<BuildResultEntity>().Where(filter);
return _buildResultDateTable.ExecuteQuery(query).ToList();
Expand All @@ -71,6 +71,30 @@ public List<BuildFailureEntity> GetTestCaseFailures(DateTimeOffset startDate, st
return _buildFailureDateTable.ExecuteQuery(query).ToList();
}

public List<string> GetViewNames(DateTimeOffset startDate)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this approach is fine now. Going forward though I wonder if we should have a separate table that tracks view names.

For a given date we have thousands of rows but only about 10-20 view names. That means our select here is bringing down thousands of pieces of data that we don't use. May behoove us to build a parallel table that just has the data that we need. I'll file a bug to track that work.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed: #6

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I think that would be extremely beneficial; while I made the query asynchronous to minimize the impact, it would at least allow the dropdown box on the page to get loaded much faster, particularly for a larger date range. 😄

{
var filter = FilterUtil
.SinceDate(ColumnNames.PartitionKey, startDate)
.And(FilterUtil.Column(nameof(BuildResultEntity.ViewName), null, ColumnOperator.NotEqual));
var query = new TableQuery<BuildResultEntity>()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should consider doing a DynamicTableEntity query here. We are only using the ViewName column in the result so we can reduce the data size we are querying by just dynamically selecting that column.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... I'm not familiar with DynamicTableEntity, but it makes sense to optizmize that way (I was using the Select clause for a smiliar effect, at least on the final output processing). I'll take a look at what the impact of the switch would be.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See an example here:

var query = new TableQuery<DynamicTableEntity>().Select(new[] { "PartitionKey", "RowKey" });

Essentially just use DynamicTableEntity as the entity type and then specify the exact columns you want in the Select query. In this case it would be just ViewName

.Select(new List<string>() {nameof(BuildResultEntity.ViewName)})
.Where(filter);

var defaultViewNames = new List<string>() { "all" };

// TODO should we union the results from querying _buildFailureDateTable ?
// The query takes much longer than _buildResultDateTable for some reason,
// and doesn't appear to contain useful data for this purpose (yet).
// If we DO need the latter, we need a different approach as the double query
// becomes prohibitively slow.
var buildResultViewNames = _buildResultDateTable.ExecuteQuery(query)
.Select(b => b.ViewName)
.Distinct()
.ToList();

return defaultViewNames.Union(buildResultViewNames).ToList();
}

private static FilterUtil FilterView(FilterUtil util, string viewName)
{
Debug.Assert(nameof(BuildResultEntity.ViewName) == nameof(BuildFailureEntity.ViewName));
Expand Down
4 changes: 4 additions & 0 deletions Dashboard/Content/Site.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ select,
textarea {
max-width: 280px;
}

.error_message {
color: red;
}
Binary file added Dashboard/Content/loading-squares.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions Dashboard/Controllers/BuildsApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ public TestFailureData GetTestFailure([FromUri] string name, [FromUri] DateTimeO
return data;
}

[Route("api/builds/viewNames")]
public List<string> GetViewNames([FromUri] DateTimeOffset? startDate = null)
{
var startDateValue = startDate ?? DateTimeOffset.UtcNow - TimeSpan.FromDays(1);
return _buildUtil.GetViewNames(startDateValue);
}

// DEMAND: should return the URI they can use for getting updates
[Route("api/builds/demand")]
public async Task<string> CreateDemandBuild(DemandRunRequestModel model)
Expand Down
27 changes: 24 additions & 3 deletions Dashboard/Controllers/BuildsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,42 @@ public ActionResult View(bool pr = false, DateTimeOffset? startDate = null, stri
public ActionResult Kind(string name = null, bool pr = false, DateTime? startDate = null, string viewName = AzureUtil.ViewNameRoslyn)
{
var filter = CreateBuildFilter(nameof(Kind), name, viewName, pr, startDate);
var kindValue = EnumUtil.Parse(name, ClassificationKind.Unknown);
var startDateValue = startDate ?? DateTimeOffset.UtcNow - TimeSpan.FromDays(1);
var list = _buildUtil
.GetBuildResults(startDateValue, kindValue, viewName)
.GetBuildResultsByKindName(startDateValue, name, viewName)
.Where(x => pr || !JobUtil.IsPullRequestJobName(x.JobName))
.ToList();
var model = new BuildResultKindModel()
{
Filter = filter,
ClassificationKind = kindValue.ToString(),
ClassificationKind = name,
Entries = list,
};
return View(viewName: "Kind", model: model);
}

public ActionResult KindByViewName(string name = null, bool pr = false, DateTime? startDate = null)
{
var filter = CreateBuildFilter(actionName: nameof(KindByViewName), name: name, startDate: startDate, pr: pr);
var startDateValue = startDate ?? DateTimeOffset.UtcNow - TimeSpan.FromDays(1);
var results = _buildUtil
.GetBuildResultsByKindName(startDateValue, name, AzureUtil.ViewNameAll)
.Where(x => pr || !JobUtil.IsPullRequestJobName(x.JobId))
.ToList();
var builds = results
.GroupBy(x => x.ViewName)
.Select(x => new BuildViewNameModel() { ViewName = x.Key, Count = x.Count() })
.ToList();
var model = new BuildResultKindByViewNameModel()
{
Filter = filter,
ClassificationKind = name,
Builds = builds,
TotalResultCount = results.Count
};
return View(viewName: "KindByViewName", model: model);
}

public string Csv(string viewName = AzureUtil.ViewNameRoslyn, bool pr = false, DateTime? startDate = null)
{
var filter = CreateBuildFilter(nameof(Csv), viewName: viewName, pr: pr, startDate: startDate);
Expand Down
4 changes: 4 additions & 0 deletions Dashboard/Dashboard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@
<ItemGroup>
<Content Include="Content\bootstrap.css" />
<Content Include="Content\bootstrap.min.css" />
<Content Include="Content\loading-squares.gif" />
<Content Include="favicon.ico" />
<Content Include="fonts\glyphicons-halflings-regular.svg" />
<Content Include="Global.asax" />
Expand All @@ -256,7 +257,9 @@
<None Include="Properties\PublishProfiles\jdash.pubxml" />
<None Include="Properties\PublishProfiles\rosdash.pubxml" />
<None Include="Scripts\jquery-1.10.2.intellisense.js" />
<Content Include="Scripts\build-kind-by-viewname.js" />
<Content Include="Scripts\build-view.js" />
<Content Include="Scripts\build-filter.js" />
<Content Include="Scripts\testrun-data.js" />
<Content Include="Scripts\job-waiting-chart.js" />
<Content Include="Scripts\job-queue-chart.js" />
Expand Down Expand Up @@ -305,6 +308,7 @@
<Content Include="Views\Builds\View.cshtml" />
<Content Include="Views\Builds\Unprocessed.cshtml" />
<Content Include="Views\Builds\BuildFilter.cshtml" />
<Content Include="Views\Builds\KindByViewName.cshtml" />
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
Expand Down
14 changes: 14 additions & 0 deletions Dashboard/Models/BuildsModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ public class BuildViewModel
public int Count;
}

public class BuildViewNameModel
{
public string ViewName;
public int Count;
}

public class BuildResultKindByViewNameModel
{
public BuildFilterModel Filter { get; set; }
public string ClassificationKind { get; set; }
public List<BuildViewNameModel> Builds { get; set; } = new List<BuildViewNameModel>();
public int TotalResultCount { get; set; }
}

public class BuildResultKindModel
{
public BuildFilterModel Filter { get; set; }
Expand Down
Binary file modified Dashboard/Scripts/_references.js
Binary file not shown.
27 changes: 27 additions & 0 deletions Dashboard/Scripts/build-filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
$(document).ready(function () {
if ($('#view_name_div select').length > 0) {
var startDate = $('input[name=startDate]').val();
var selectedViewName = $('input[name=viewName]').val();
$('#view_name_div select').change(function (e) {
$('input[name=viewName]').val(e.target.value)
});
$.ajax({
dataType: 'json',
url: '/api/builds/viewNames',
data: {
startDate: startDate
},
success: function (list) {
for (var i = 0; i < list.length; i++) {
$('#view_name_div select').append('<option>' + list[i] + '</option>');
}
$('#view_name_div img').css('display', 'none');
$('#view_name_div select').val(selectedViewName).css('display', '');
},
error: function () {
$('#view_name_div img').css('display', 'none');
$('#view_name_div').append('<span class="error_message">[[ Failed to fetch data ]]</span>');
}
});
}
});
47 changes: 47 additions & 0 deletions Dashboard/Scripts/build-kind-by-viewname.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
$(document).ready(function () {
google.charts.load('current', { packages: ['corechart', 'bar'] });
google.charts.setOnLoadCallback(drawBuildSummary);

function drawBuildSummary() {
var elem = $('#build_summary_chart');
var data = [['View Name', 'Count']];
var categories = [];

var values = elem.attr('data-values').split(';');
values.forEach(function (str, _, _) {
var all = str.split(',');
data.push([all[0], parseInt(all[1])]);
categories.push(all[0]);
});

var dataTable = google.visualization.arrayToDataTable(data);
var options = {
title: 'View Name (Project)',
curveType: 'function',
bar: { groupWidth: '75%' },
isStacked: true
};

var chart = new google.visualization.BarChart(elem.get(0));
chart.draw(dataTable, options);

google.visualization.events.addListener(chart, 'select', function () {
var selectedItem = chart.getSelection()[0];
if (selectedItem) {
var viewName = categories[selectedItem.row];
$('#viewname_form_kind').attr('value', viewName);
var form = $('#viewname_form').submit()
}
});
}
});

$(document).ready(function () {
var all_results_link = $('#submit_form_all');
var all_viewname = all_results_link.attr('data-viewname');
all_results_link.click(function (e) {
e.preventDefault();
$('#viewname_form_kind').attr('value', all_viewname);
$('#viewname_form').submit();
});
});
8 changes: 8 additions & 0 deletions Dashboard/Views/Builds/BuildFilter.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@
<div>
<div>Include Pull Requests <input name="pr" type="checkbox" @prValue value="true" /></div>
<div>Start Date <input name="startDate" type="date" value="@startDateValue"/></div>
@if (Model.ViewName != null)
{
<div id="view_name_div" data-selected-viewname="@Model.ViewName">
View Name
<select style="display:none;"></select>
<img src="@Url.Content("/Content/loading-squares.gif")" alt="Loading..." />
</div>
}
<div><input type="submit" value="Refresh" /></div>
</div>
}
3 changes: 3 additions & 0 deletions Dashboard/Views/Builds/BuildResult.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@

@Html.Partial("BuildFilter", Model.Filter)

@section scripts {
<script type="text/javascript" src="@Url.Content("/Scripts/build-filter.js")"></script>
}
4 changes: 4 additions & 0 deletions Dashboard/Views/Builds/BuildResultList.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@
</table>

@Html.Partial("BuildFilter", Model.Filter)

@section scripts {
<script type="text/javascript" src="@Url.Content("/Scripts/build-filter.js")"></script>
}
5 changes: 4 additions & 1 deletion Dashboard/Views/Builds/Kind.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}

<h2>Kind @Model.ClassificationKind</h2>
<h3>Jenkins Jobs</h3>
<h3>Jenkins Jobs (@Model.Filter.ViewName)</h3>
<table class="table">
<thead><tr>
<th>Job</th>
Expand Down Expand Up @@ -39,3 +39,6 @@

@Html.Partial("BuildFilter", Model.Filter)

@section scripts {
<script type="text/javascript" src="@Url.Content("/Scripts/build-filter.js")"></script>
}
39 changes: 39 additions & 0 deletions Dashboard/Views/Builds/KindByViewName.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@model Dashboard.Models.BuildResultKindByViewNameModel

@{
ViewBag.Title = "Build Results";

var builds = Model.Builds.OrderByDescending(x => x.Count);
var buildValues = string.Join(";", builds.Select(x => $"{x.ViewName},{x.Count}"));
var prValue = Model.Filter.IncludePullRequests ? @"checked=""checked""" : "";
var startDateValue = Model.Filter.StartDate.ToString("yyyy-MM-dd");
}

<div>
<h2>Kind @Model.ClassificationKind</h2>
<div>Total Count @Model.TotalResultCount.ToString("n0")</div>
</div>

<div id="build_summary_chart" style="width: 900px; height: 500px" data-values="@buildValues"></div>

<div>
<a href="#" id="submit_form_all" data-viewname="@Dashboard.Azure.AzureUtil.ViewNameAll">Show Jenkins jobs for all projects</a>
</div>

<!-- Filter the action results -->
@Html.Partial("BuildFilter", Model.Filter)

<!-- Hidden form for navigating on selection of view names -->
@using (Html.BeginForm(controllerName: "Builds", actionName: "Kind", method: FormMethod.Get, htmlAttributes: new { id = "viewname_form" }))
{
<input id="viewname_form_kind" name="viewName" value="" hidden="hidden" />
<input name="pr" type="checkbox" @prValue value="true" hidden="hidden" />
<input name="startDate" type="date" value="@startDateValue" hidden="hidden" />
<input name="name" value="@Model.ClassificationKind" hidden="hidden" />
}

@section scripts {
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript" src="@Url.Content("/Scripts/build-kind-by-viewname.js")"></script>
<script type="text/javascript" src="@Url.Content("/Scripts/build-filter.js")"></script>
}
4 changes: 4 additions & 0 deletions Dashboard/Views/Builds/TestFailure.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@
</table>

@Html.Partial("BuildFilter", Model.Filter)

@section scripts {
<script type="text/javascript" src="@Url.Content("/Scripts/build-filter.js")"></script>
}
4 changes: 4 additions & 0 deletions Dashboard/Views/Builds/TestFailureList.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@
</table>

@Html.Partial("BuildFilter", Model.Filter)

@section scripts {
<script type="text/javascript" src="@Url.Content("/Scripts/build-filter.js")"></script>
}
16 changes: 13 additions & 3 deletions Dashboard/Views/Builds/View.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
var prValue = Model.Filter.IncludePullRequests ? @"checked=""checked""" : "";
var startDateValue = Model.Filter.StartDate.ToString("yyyy-MM-dd");
var successRate = Model.TotalSucceededCount / (double)Model.TotalBuildCount;
var isSingleViewName = Model.Filter.ViewName != Dashboard.Azure.AzureUtil.ViewNameAll;
}

<div>
<h2>Build Result Summary</h2>
<div>@Model.Filter.ViewName</div>
<div>Ran @Model.TotalBuildCount.ToString("n0")</div>
<div>Succeded @Model.TotalSucceededCount.ToString("n0")</div>
<div>Success rate @successRate.ToString("n2")</div>
Expand All @@ -23,15 +25,23 @@
@Html.Partial("BuildFilter", Model.Filter)

<!-- Hidden form for navigating on selection of categories -->
<!-- TODO Move this to the shared filter as well -->
@using (Html.BeginForm(controllerName: "Builds", actionName: "Kind", method: FormMethod.Get, htmlAttributes: new { id = "category_form" }))
@using (Html.BeginForm(
controllerName: "Builds",
actionName: isSingleViewName ? "Kind" : "KindByViewName",
method: FormMethod.Get,
htmlAttributes: new { id = "category_form" }))
{
<input id="category_form_kind" name="kind" value="" hidden="hidden" />
<input id="category_form_kind" name="name" value="" hidden="hidden" />
<input name="pr" type="checkbox" @prValue value="true" hidden="hidden" />
<input name="startDate" type="date" value="@startDateValue" hidden="hidden"/>
if (isSingleViewName)
{
<input name="viewName" value="@Model.Filter.ViewName" hidden="hidden"/>
}
}

@section scripts {
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript" src="@Url.Content("/Scripts/build-view.js")" ></script>
<script type="text/javascript" src="@Url.Content("/Scripts/build-filter.js")"></script>
}
3 changes: 3 additions & 0 deletions Jenkins.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Jenkins", "Dashboard.Jenkins\Dashboard.Jenkins.csproj", "{93ED4B41-CBC6-4423-BDDC-612C9BB607BA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Tests", "Dashboard.Tests\Dashboard.Tests.csproj", "{37C8575D-8E4E-45EB-861B-3720E883A663}"
ProjectSection(ProjectDependencies) = postProject
{33DB7715-7742-44BE-AE4B-34CE54A40AF5} = {33DB7715-7742-44BE-AE4B-34CE54A40AF5}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JenkinsThis", "JenkinsThis\JenkinsThis.csproj", "{1FCDD236-70A8-42C3-B3FC-631A4CF32351}"
EndProject
Expand Down