add idpc ui

This commit is contained in:
jbai 2016-02-10 19:17:47 -08:00 committed by Mars Lan
parent b36f774feb
commit 3e59b304b4
14 changed files with 1038 additions and 0 deletions

View File

@ -25,6 +25,7 @@ import views.html.index;
import views.html.login;
import views.html.lineage;
import views.html.schemaHistory;
import views.html.idpc;
import static play.data.Form.form;
import org.apache.commons.lang3.StringUtils;
import security.AuthenticationManager;
@ -107,6 +108,17 @@ public class Application extends Controller
return ok(schemaHistory.render(username));
}
@Security.Authenticated(Secured.class)
public static Result idpc()
{
String username = session("user");
if (username == null)
{
username = "";
}
return ok(idpc.render(username));
}
public static Result login()
{
//You cann generate the Csrf token such as String csrfToken = SecurityPlugin.getInstance().getCsrfToken();

View File

@ -0,0 +1,79 @@
/**
* Copyright 2015 LinkedIn Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package controllers.api.v1;
import com.fasterxml.jackson.databind.node.ObjectNode;
import dao.JiraDAO;
import dao.UserDAO;
import models.JiraTicket;
import org.apache.commons.lang3.StringUtils;
import play.libs.Json;
import play.mvc.Controller;
import play.mvc.Result;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Jira extends Controller
{
public static String HEADLESS_COMPONENT = "Auto Purge Program - Headless";
public static Result getLdapInfo()
{
ObjectNode result = Json.newObject();
result.put("status", "ok");
result.set("ldapinfo", Json.toJson(JiraDAO.getLdapInfo()));
return ok(result);
}
public static Result getFirstLevelLdapInfo(String managerId)
{
ObjectNode result = Json.newObject();
result.put("status", "ok");
result.set("currentUser", Json.toJson(JiraDAO.getCurrentUserLdapInfo(managerId)));
result.set("members", Json.toJson(JiraDAO.getFirstLevelLdapInfo(managerId)));
return ok(result);
}
public static Result getJiraTickets(String managerId)
{
ObjectNode result = Json.newObject();
List<JiraTicket> headlessTickets = new ArrayList<JiraTicket>();
List<JiraTicket> userTickets = new ArrayList<JiraTicket>();
List<JiraTicket> tickets = JiraDAO.getUserTicketsByManagerId(managerId);
if ( tickets != null && tickets.size() > 0)
{
for( JiraTicket ticket : tickets)
{
if (ticket.ticketComponent.equalsIgnoreCase(HEADLESS_COMPONENT))
{
headlessTickets.add(ticket);
}
else
{
userTickets.add(ticket);
}
}
}
result.put("status", "ok");
result.set("headlessTickets", Json.toJson(headlessTickets));
result.set("userTickets", Json.toJson(userTickets));
return ok(result);
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright 2015 LinkedIn Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package dao;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
public class AbstractMySQLDAO
{
private static JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource.getDataSource("wherehows_mysql"));
private static NamedParameterJdbcTemplate nPJdbcTemplate =
new NamedParameterJdbcTemplate(DataSource.getDataSource("wherehows_mysql"));
protected static JdbcTemplate getJdbcTemplate()
{
return jdbcTemplate;
}
protected static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate()
{
return nPJdbcTemplate;
}
}

64
web/app/dao/JiraDAO.java Normal file
View File

@ -0,0 +1,64 @@
/**
* Copyright 2015 LinkedIn Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package dao;
import java.util.*;
import models.*;
public class JiraDAO extends AbstractMySQLOpenSourceDAO
{
private final static String GET_LDAP_INFO = "SELECT full_name, display_name, email, " +
"department_id, department_name, manager_user_id, org_hierarchy, user_id " +
"FROM dir_external_user_info";
private final static String GET_CURRENT_USER_INFO = "SELECT full_name, display_name, email, " +
"department_id, department_name, manager_user_id, org_hierarchy, user_id " +
"FROM dir_external_user_info WHERE user_id = ?";
private final static String GET_FIRST_LEVEL_LDAP_INFO = "SELECT full_name, display_name, email, " +
"department_id, department_name, manager_user_id, org_hierarchy, user_id " +
"FROM dir_external_user_info WHERE manager_user_id = ?";
private final static String GET_TICKETS_BY_MANAGER_ID = "SELECT u.user_id, u.full_name, " +
"u.display_name, u.title, u.manager_user_id, u.email, u.org_hierarchy, l.hdfs_name, l.directory_path, " +
"l.total_size_mb, l.num_of_files, l.jira_key, l.jira_status, l.jira_component " +
"FROM dir_external_user_info u JOIN log_jira__hdfs_directory_to_owner_map l " +
"on u.user_id = l.current_assignee_id WHERE u.org_hierarchy like ?";
public static List<LdapInfo> getLdapInfo()
{
return getJdbcTemplate().query(GET_LDAP_INFO, new LdapInfoRowMapper());
}
public static List<LdapInfo> getCurrentUserLdapInfo(String userId)
{
return getJdbcTemplate().query(GET_CURRENT_USER_INFO, new LdapInfoRowMapper(), userId);
}
public static List<LdapInfo> getFirstLevelLdapInfo(String managerId)
{
return getJdbcTemplate().query(GET_FIRST_LEVEL_LDAP_INFO, new LdapInfoRowMapper(), managerId);
}
public static List<LdapInfo> getFirstLevelLdapInfo()
{
return getJdbcTemplate().query(GET_LDAP_INFO, new LdapInfoRowMapper());
}
public static List<JiraTicket> getUserTicketsByManagerId(String managerId)
{
return getJdbcTemplate().query(GET_TICKETS_BY_MANAGER_ID, new JiraTicketRowMapper(), "/%" + managerId + "%");
}
}

View File

@ -0,0 +1,78 @@
/**
* Copyright 2015 LinkedIn Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package dao;
import controllers.api.v1.Jira;
import models.DatasetComment;
import models.JiraTicket;
import models.LdapInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JiraTicketRowMapper implements RowMapper<JiraTicket>
{
public static String USER_ID_COLUMN = "user_id";
public static String FULL_NAME_COLUMN = "full_name";
public static String DISPLAY_NAME_COLUMN = "display_name";
public static String EMAIL_COLUMN = "email";
public static String ORG_HIERARCHY_COLUMN = "org_hierarchy";
public static String TITLE_COLUMN = "title";
public static String MANAGER_USER_ID_COLUMN = "manager_user_id";
public static String HDFS_NAME_COLUMN = "hdfs_name";
public static String DIRECTORY_PATH_COLUMN = "directory_path";
public static String TOTAL_SIZE_COLUMN = "total_size_mb";
public static String NUM_OF_FILES_COLUMN = "num_of_files";
public static String JIRA_KEY_COLUMN = "jira_key";
public static String JIRA_STATUS_COLUMN = "jira_status";
public static String JIRA_COMPONENT_COLUMN = "jira_component";
@Override
public JiraTicket mapRow(ResultSet rs, int rowNum) throws SQLException {
String userId = rs.getString(USER_ID_COLUMN);
String fullName = rs.getString(FULL_NAME_COLUMN);
String displayName = rs.getString(DISPLAY_NAME_COLUMN);
String title = rs.getString(TITLE_COLUMN);
String managerId = rs.getString(MANAGER_USER_ID_COLUMN);
String email = rs.getString(EMAIL_COLUMN);
String orgHierarchy = rs.getString(ORG_HIERARCHY_COLUMN);
String hdfsName = rs.getString(HDFS_NAME_COLUMN);
String directoryPath = rs.getString(DIRECTORY_PATH_COLUMN);
Long totalSize = rs.getLong(TOTAL_SIZE_COLUMN);
Long numOfFiles = rs.getLong(NUM_OF_FILES_COLUMN);
String jiraKey = rs.getString(JIRA_KEY_COLUMN);
String jiraStatus = rs.getString(JIRA_STATUS_COLUMN);
String jiraComponent = rs.getString(JIRA_COMPONENT_COLUMN);
JiraTicket ticket = new JiraTicket();
ticket.currentAssignee = userId;
ticket.assigneeDisplayName = displayName;
ticket.assigneeFullName = fullName;
ticket.assigneeTitle = title;
ticket.assigneeEmail = email;
ticket.currentAssigneeOrgHierarchy = orgHierarchy;
ticket.assigneeManagerId = managerId;
ticket.ticketHdfsName = hdfsName;
ticket.ticketDirectoryPath = directoryPath;
ticket.ticketTotalSize = totalSize;
ticket.ticketNumOfFiles = numOfFiles;
ticket.ticketKey = jiraKey;
ticket.ticketStatus = jiraStatus;
ticket.ticketComponent = jiraComponent;
return ticket;
}
}

View File

@ -0,0 +1,61 @@
/**
* Copyright 2015 LinkedIn Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package dao;
import models.DatasetComment;
import models.LdapInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
public class LdapInfoRowMapper implements RowMapper<LdapInfo>
{
public static String USER_ID_COLUMN = "user_id";
public static String FULL_NAME_COLUMN = "full_name";
public static String DISPLAY_NAME_COLUMN = "display_name";
public static String EMAIL_COLUMN = "email";
public static String DEPARTMENT_ID_COLUMN = "department_id";
public static String DEPARTMENT_NAME_COLUMN = "department_name";
public static String MANAGER_USER_ID_COLUMN = "manager_user_id";
public static String ORG_HIERARCHY_COLUMN = "org_hierarchy";
public static String ICON_URL_PREFIX = "https://cinco.corp.linkedin.com/api/profile/";
public static String ICON_URL_SUBFIX = "/picture?access_token=2rzmbzEMGlHsszQktFY-B1TxUic";
@Override
public LdapInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
String userId = rs.getString(USER_ID_COLUMN);
String fullName = rs.getString(FULL_NAME_COLUMN);
String displayName = rs.getString(DISPLAY_NAME_COLUMN);
String email = rs.getString(EMAIL_COLUMN);
Integer departmentId = rs.getInt(DEPARTMENT_ID_COLUMN);
String departmentName = rs.getString(DEPARTMENT_NAME_COLUMN);
String managerUserId = rs.getString(MANAGER_USER_ID_COLUMN);
String orgHierarchy = rs.getString(ORG_HIERARCHY_COLUMN);
LdapInfo ldapInfo = new LdapInfo();
ldapInfo.userName = userId;
ldapInfo.displayName = displayName;
ldapInfo.fullName = fullName;
ldapInfo.email = email;
ldapInfo.departmentId = departmentId;
ldapInfo.departmentName = departmentName;
ldapInfo.managerUserId = managerUserId;
ldapInfo.orgHierarchy = orgHierarchy;
ldapInfo.iconUrl = ICON_URL_PREFIX + userId + ICON_URL_SUBFIX;
return ldapInfo;
}
}

View File

@ -23,6 +23,10 @@ public class MySQLDataSource extends DataSource
public static String DATABASE_WHEREHOWS_OPENSOURCE_USER_NAME_KEY = "database.opensource.username";
public static String DATABASE_WHEREHOWS_OPENSOURCE_USER_PASSWORD_KEY = "database.opensource.password";
public static String DATABASE_WHEREHOWS_OPENSOURCE_URL_KEY = "database.opensource.url";
public static String DATABASE_WHEREHOWS_DB = "wherehows_mysql";
public static String DATABASE_WHEREHOWS_DB_USER_NAME_KEY = "database.wherehows.username";
public static String DATABASE_WHEREHOWS_DB_USER_PASSWORD_KEY = "database.wherehows.password";
public static String DATABASE_WHEREHOWS_DB_URL_KEY = "database.wherehows.url";
@Override
public String getType()
@ -39,6 +43,13 @@ public class MySQLDataSource extends DataSource
setPassword(Play.application().configuration().getString(DATABASE_WHEREHOWS_OPENSOURCE_USER_PASSWORD_KEY));
setJdbcUrl(Play.application().configuration().getString(DATABASE_WHEREHOWS_OPENSOURCE_URL_KEY));
}
else if (StringUtils.isNotBlank(identifier) && identifier.equalsIgnoreCase(DATABASE_WHEREHOWS_DB))
{
setUsername(Play.application().configuration().getString(DATABASE_WHEREHOWS_DB_USER_NAME_KEY));
setPassword(Play.application().configuration().getString(DATABASE_WHEREHOWS_DB_USER_PASSWORD_KEY));
setJdbcUrl(Play.application().configuration().getString(DATABASE_WHEREHOWS_DB_URL_KEY));
}
setIdleConnectionTestPeriodInMinutes(1);
setIdleMaxAgeInMinutes(1);
setMaxConnectionsPerPartition(10);

View File

@ -0,0 +1,27 @@
/**
* Copyright 2015 LinkedIn Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package models;
public class IdpcInfo {
public String userName;
public String fullName;
public String displayName;
public int departmentId;
public String departmentName;
public String email;
public String managerUserId;
public String orgHierarchy;
public String iconUrl;
}

View File

@ -0,0 +1,32 @@
/**
* Copyright 2015 LinkedIn Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package models;
public class JiraTicket {
public String currentAssignee;
public String currentAssigneeOrgHierarchy;
public String assigneeDisplayName;
public String assigneeFullName;
public String assigneeTitle;
public String assigneeManagerId;
public String assigneeEmail;
public String ticketHdfsName;
public String ticketDirectoryPath;
public Long ticketTotalSize;
public Long ticketNumOfFiles;
public String ticketKey;
public String ticketStatus;
public String ticketComponent;
}

View File

@ -0,0 +1,27 @@
/**
* Copyright 2015 LinkedIn Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package models;
public class LdapInfo {
public String userName;
public String fullName;
public String displayName;
public int departmentId;
public String departmentName;
public String email;
public String managerUserId;
public String orgHierarchy;
public String iconUrl;
}

View File

@ -0,0 +1,387 @@
@(user: String)
@main(user, "") {
<div id="content">
<script type="text/x-handlebars" id="jira">
<div id="schemaView" class="container-fluid">
<div class="col-xs-12">
<div class="row">
<div class="col-xs-12">
<h3>Jira - Closed Account Auto Purge</h3>
</div>
</div>
<div class="row">
<div class="col-xs-12 well well-sm">
<ul class="breadcrumbs">
{{#each crumb in breadcrumbs}}
<li>
<a {{bind-attr href=crumb.idpcUrl title=crumb.title }}>
{{ crumb.title }}
</a>
</li>
{{/each}}
</ul>
</div>
</div>
<ul id="jiratabs" class="nav nav-tabs">
<li id="headlesspage">
<a id="headlesstablink" data-toggle="tab" href="#headlesstab">Headless</a>
</li>
<li id="userpage">
<a id="usertablink" data-toggle="tab" href="#usertab">User</a>
</li>
</ul>
<div class="tab-content">
<div id="headlesstab" class="tab-pane">
<div class="row">
<div class="col-xs-12 col-md-4">
<h4>Tickets for {{ selectedUser.displayName }}</h4>
<table class="table table-bordered table-condensed table-hover">
<thead>
<tr>
<th class="text-center">Name</th>
<th class="text-center">Completion</th>
<th class="text-center">Total</th>
<th class="text-center">Open</th>
<th class="text-center">Closed</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<img {{bind-attr src=selectedUser.iconUrl title=selectedUser.displayName }}
width="25px"
height="25px"/>
<a {{bind-attr href=selectedUser.orgHierarchy title=selectedUser.displayName }}>
{{ selectedUser.displayName }}
</a>
</td>
<td class="text-center">{{ selectedUser.headlessTicketsCompletion }}%</td>
<td class="text-center">{{ selectedUser.totalHeadlessTickets }}</td>
<td class="text-center">{{ selectedUser.openedHeadlessTickets }}</td>
<td class="text-center">{{ selectedUser.closedHeadlessTickets }}</td>
</tr>
</tbody>
</table>
<h4>Tickets Roll-Up for Headless Group</h4>
<table class="table table-bordered table-condensed table-hover">
<thead>
<tr>
<th class="text-center">Name</th>
<th class="text-center">Completion</th>
<th class="text-center">Total</th>
<th class="text-center">Open</th>
<th class="text-center">Closed</th>
</tr>
</thead>
<tbody>
{{#if membersInProgress}}
<tr>
<td colspan="5" class="text-center">
<div class="row">
<div class="col-xs-12">
<i class="fa fa-spinner spinning fa-4x">
</i>
</div>
</div>
</td>
</tr>
{{else}}
{{#if userNoMembers}}
<tr>
<td colspan="5">
<div class="row">
<div class="col-xs-12 text-center">
This user has no one that reports to them.
</div>
</div>
</td>
</tr>
{{else}}
{{#each user in members}}
<tr>
<td>
<img {{bind-attr src=user.iconUrl title=user.displayName }}
width="25px"
height="25px"/>
<a {{bind-attr href=user.url title=user.displayName }}>
{{ user.displayName }}
</a>
</td>
<td class="text-center">{{ user.headlessTicketsCompletion }}%</td>
<td class="text-center">{{ user.totalHeadlessTickets }}</td>
<td class="text-center">{{ user.openedHeadlessTickets }}</td>
<td class="text-center">{{ user.closedHeadlessTickets }}</td>
</tr>
{{/each}}
{{/if}}
{{/if}}
</tbody>
</table>
</div>
<div class="col-xs-12 col-md-8">
<div class="row">
<div class="col-xs-9">
<h4>List of Headless Group Tickets</h4>
</div>
<div class="col-xs-3 headerBarFilter">
<form class="form-inline">
<div class="form-group">
<label>Sort Options</label>
<select class="form-control">
{{#each option in sortOptions}}
<option {{bind-attr value=option }}>{{option}}
</option>
{{/each}}
</select>
</div>
</form>
</div>
</div>
<table class="table table-bordered table-condensed table-hover table-striped">
<thead>
<tr>
<th class="text-center">Assignee</th>
<th class="text-center">Ticket</th>
<th class="text-center">Status</th>
<th class="text-center">HDFS Name</th>
<th class="text-center">Directory Path</th>
<th class="text-center">Total Size (MB)</th>
<th class="text-center"># of Files</th>
<th class="text-center">Last Update Time</th>
</tr>
</thead>
<tbody>
{{#if ticketsInProgress}}
<tr>
<td colspan="8" class="text-center">
<div class="row">
<div class="col-xs-12">
<i class="fa fa-spinner spinning fa-4x">
</i>
</div>
</div>
</td>
</tr>
{{else}}
{{#if headlessNoTickets}}
<tr>
<td colspan="8">
<div class="row">
<div class="col-xs-12 text-center">
This user has no tickets.
</div>
</div>
</td>
</tr>
{{else}}
{{#each ticket in headlessTickets}}
<tr>
<td>
<img {{bind-attr src=ticket.assigneeIconUrl title=ticket.assigneeDisplayName }}
width="25px"
height="25px"/>
{{ ticket.assigneeDisplayName }}
</td>
<td class="text-center">
<a {{bind-attr href=ticket.jiraKeyUrl title=ticket.jiraKey }} target="_blank">
{{ ticket.jiraKey }}
</a>
</td>
<td class="text-center">{{ ticket.jiraStatus }}</td>
<td class="text-center">{{ ticket.hdfsName }}</td>
<td class="text-left">{{ ticket.directoryPath }}</td>
<td class="text-right">{{ ticket.totalSizeMb }}</td>
<td class="text-right">{{ ticket.numOfFiles}}</td>
<td class="text-center">{{ ticket.jiraLastUpdatedTime }}</td>
</tr>
{{/each}}
{{/if}}
{{/if}}
</tbody>
</table>
</div>
</div>
</div>
<div id="usertab" class="tab-pane">
<div class="row">
<div class="col-xs-12 col-md-4">
<h4>Tickets for {{ selectedUser.displayName }}</h4>
<table class="table table-bordered table-condensed table-hover">
<thead>
<tr>
<th class="text-center">Name</th>
<th class="text-center">Completion</th>
<th class="text-center">Total</th>
<th class="text-center">Open</th>
<th class="text-center">Closed</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<img {{bind-attr src=selectedUser.iconUrl title=selectedUser.displayName }}
width="25px"
height="25px"/>
<a {{bind-attr href=selectedUser.orgHierarchyUrl title=selectedUser.displayName }}>
{{ selectedUser.displayName }}
</a>
</td>
<td class="text-center">{{ selectedUser.userCompletion }}%</td>
<td class="text-center">{{ selectedUser.userTotalTickets }}</td>
<td class="text-center">{{ selectedUser.userOpenTickets }}</td>
<td class="text-center">{{ selectedUser.userClosedTickets }}</td>
</tr>
</tbody>
</table>
<h4>Ticket Roll-Up for Individual User</h4>
<table class="table table-bordered table-condensed table-hover">
<thead>
<tr>
<th class="text-center">Name</th>
<th class="text-center">Completion</th>
<th class="text-center">Total</th>
<th class="text-center">Open</th>
<th class="text-center">Closed</th>
</tr>
</thead>
<tbody>
{{#if membersInProgress}}
<tr>
<td colspan="5" class="text-center">
<div class="row">
<div class="col-xs-12">
<i class="fa fa-spinner spinning fa-4x">
</i>
</div>
</div>
</td>
</tr>
{{else}}
{{#if userNoMembers}}
<tr>
<td colspan="5">
<div class="row">
<div class="col-xs-12 text-center">
This user has no one that reports to them.
</div>
</div>
</td>
</tr>
{{else}}
{{#each user in members}}
<tr>
<td>
<img {{bind-attr src=user.iconUrl title=user.displayName }}
width="25px"
height="25px"/>
<a {{bind-attr href=user.orgHierarchyUrl title=user.displayName }}>
{{ user.displayName }}
</a>
</td>
<td class="text-center">{{ user.userCompletion }}%</td>
<td class="text-center">{{ user.userTotalTickets }}</td>
<td class="text-center">{{ user.userOpenTickets }}</td>
<td class="text-center">{{ user.userClosedTickets }}</td>
</tr>
{{/each}}
{{/if}}
{{/if}}
</tbody>
</table>
</div>
<div class="col-xs-12 col-md-8">
<div class="row">
<div class="col-xs-9">
<h4>List of User Tickets</h4>
</div>
<div class="col-xs-3 headerBarFilter">
<form class="form-inline">
<div class="form-group">
<label>Sort Options</label>
<select class="form-control">
{{#each option in sortOptions}}
<option {{bind-attr value=option }}>{{option}}
</option>
{{/each}}
</select>
</div>
</form>
</div>
</div>
<table class="table table-bordered table-condensed table-hover table-striped">
<thead>
<tr>
<th class="text-center">Assignee</th>
<th class="text-center">Ticket</th>
<th class="text-center">Status</th>
<th class="text-center">HDFS Name</th>
<th class="text-center">Directory Path</th>
<th class="text-center">Total Size (Mb)</th>
<th class="text-center"># of Files</th>
<th class="text-center">Last Update Time</th>
</tr>
</thead>
<tbody>
{{#if ticketsInProgress}}
<tr>
<td colspan="8" class="text-center">
<div class="row">
<div class="col-xs-12">
<i class="fa fa-spinner spinning fa-4x">
</i>
</div>
</div>
</td>
</tr>
{{else}}
{{#if userNoTickets}}
<tr>
<td colspan="8">
<div class="row">
<div class="col-xs-12 text-center">
This user has no tickets.
</div>
</div>
</td>
</tr>
{{else}}
{{#each ticket in userTickets}}
<tr>
<td>
<img {{bind-attr src=ticket.currentAssigneeIconUrl
title=ticket.currentAssigneeDisplayName }}
width="25px"
height="25px"/>
{{ ticket.assigneeDisplayName }}
</td>
<td class="text-center">
<a {{bind-attr href=ticket.jiraKeyUrl title=ticket.jiraKey }} target="_blank">
{{ ticket.ticketKey }}
</a>
</td>
<td class="text-center">{{ ticket.ticketStatus }}</td>
<td class="text-center">{{ ticket.ticketHdfsName }}</td>
<td class="text-left">{{ ticket.ticketDirectoryPath }}</td>
<td class="text-right">{{ ticket.ticketTotalSize }}</td>
<td class="text-right">{{ ticket.ticketNumOfFiles}}</td>
<td class="text-center">{{ ticket.jiraLastUpdatedTime }}</td>
</tr>
{{/each}}
{{/if}}
{{/if}}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</script>
</div>
<script src="@routes.Assets.at("vendors/ember-1.12.0/ember-template-compiler.js")" type="text/javascript"></script>
<script src="@routes.Assets.at("vendors/ember-1.12.0/ember.js")" type="text/javascript"></script>
<script src="@routes.Assets.at("javascripts/idpc.js")" type="text/javascript"></script>
}

View File

@ -89,6 +89,13 @@
</span>
</a>
</li>
<li>
<a href="/idpc">
<span>
IDPC
</span>
</a>
</li>
</ul>
</li>
</ul>

View File

@ -35,6 +35,8 @@ GET /schemaHistory controllers.Application.sche
GET /api/v1/owner/types controllers.api.v1.Dataset.getDatasetOwnerTypes()
GET /idpc controllers.Application.idpc()
GET /api/v1/party/entities controllers.api.v1.User.getAllUserEntities()
GET /api/v1/party/employees controllers.api.v1.User.getAllCompanyUsers()
@ -185,8 +187,14 @@ GET /api/v1/lineage/flow/:application/:project/:flowId controllers.api.v1.
GET /api/v1/schemaHistory/datasets controllers.api.v1.SchemaHistory.getPagedDatasets()
GET /api/v1/jira/ldapinfo controllers.api.v1.Jira.getLdapInfo()
GET /api/v1/jira/members/:managerId controllers.api.v1.Jira.getFirstLevelLdapInfo(managerId :String)
GET /api/v1/schemaHistory/historyData/:id controllers.api.v1.SchemaHistory.getSchemaHistory(id: Int)
GET /api/v1/jira/tickets/:managerId controllers.api.v1.Jira.getJiraTickets(managerId :String)
POST /api/v1/tracking controllers.api.v1.Tracking.addTrackingEvent()
# Map static resources from the /public folder to the /assets URL path

View File

@ -0,0 +1,211 @@
(function ($) {
$(document).ready(function() {
App = Ember.Application.create({rootElement: "#content"});
App.Router.map(function() {
this.resource('jira', function(){
this.resource('user', {path: '/:user'});
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('user', "jweiner");
}
});
var genBreadcrumbs = function(urn) {
var breadcrumbs = []
var b = urn.split('/')
b.shift()
for(var i = 0; i < b.length; i++) {
if(i === 0)
{
breadcrumbs.push({title: b[i], urn: b[i]})
}
else
{
var urn = genUrn(b.slice(0, (i+1)))
breadcrumbs.push({title: b[i], urn: urn})
}
}
return breadcrumbs
}
var setActiveTab = function(){
$('#jiratabs a:first').tab("show");
}
var getMemberHeadlessTickets = function(members, tickets)
{
if (!members)
{
return members;
}
var memberTotal = 0;
var memberOpened = 0;
var memberRollupTotal = 0;
var memberRollupOpened = 0;
for(var i = 0; i < members.length; i++)
{
memberTotal = 0;
memberOpened = 0;
if (tickets)
{
for(var j = 0; j < tickets.length; j++)
{
if (tickets[j].currentAssigneeOrgHierarchy &&
(tickets[j].currentAssigneeOrgHierarchy.indexOf(members[i].userName) != -1))
{
memberTotal += 1;
if (tickets[j].ticketStatus.toLowerCase() === 'open')
{
memberOpened += 1;
}
}
}
}
members[i].totalHeadlessTickets = memberTotal;
members[i].openedHeadlessTickets = memberOpened;
members[i].closedHeadlessTickets = memberTotal - memberOpened;
if (members[i].totalHeadlessTickets > 0)
{
members[i].headlessTicketsCompletion =
Math.round( ( (memberTotal - memberOpened) / memberTotal ) * 100 );
}
else
{
members[i].headlessTicketsCompletion = 100;
}
members[i].url = "/idpc#/jira/" + members[i].userName;
memberRollupOpened += memberOpened;
memberRollupTotal += memberTotal;
}
if (jiraController)
{
var currentUser = jiraController.get('selectedUser');
var totalHeadlessTickets = memberRollupTotal;
var openedHeadlessTickets = memberRollupOpened;
var headlessTicketsCompletion;
var closedHeadlessTickets =
totalHeadlessTickets - openedHeadlessTickets;
if (totalHeadlessTickets > 0)
{
headlessTicketsCompletion =
Math.round( ( (
totalHeadlessTickets - openedHeadlessTickets) / totalHeadlessTickets ) * 100 );
}
else
{
headlessTicketsCompletion = 100;
}
Ember.set(currentUser, 'totalHeadlessTickets', totalHeadlessTickets);
Ember.set(currentUser, 'openedHeadlessTickets', openedHeadlessTickets);
Ember.set(currentUser, 'closedHeadlessTickets', closedHeadlessTickets);
Ember.set(currentUser, 'headlessTicketsCompletion', headlessTicketsCompletion);
}
return members;
}
var jiraController = null;
var hierarchy = '/jweiner';
var breadcrumbs;
var sortOptions = ['Assignee First', 'Jira Status First', 'Directory Path First'];
var selectedUser = {
'userId': 'jweiner',
'displayName': 'jweiner',
'headlessTicketsCompletion': 0,
'totalHeadlessTickets': 0,
'openedHeadlessTickets': 0,
'closedHeadlessTickets': 0,
'userCompletion': 0,
'userTotalTickets': 0,
'userOpenTickets': 0,
'userClosedTickets': 0};
setTimeout(setActiveTab, 500);
App.JiraRoute = Ember.Route.extend({
setupController: function(controller) {
jiraController = controller;
breadcrumbs = genBreadcrumbs(hierarchy);
jiraController.set('breadcrumbs', breadcrumbs);
jiraController.set('selectedUser', selectedUser);
jiraController.set('sortOptions', sortOptions);
}
});
App.UserRoute = Ember.Route.extend({
setupController: function(controller, params) {
if (params && params.user)
{
jiraController.set('ticketsInProgress', true);
var ticketsUrl = 'api/v1/jira/tickets/' + params.user;
var headlessTickets;
var userTickets;
$.get(ticketsUrl, function(data) {
jiraController.set('ticketsInProgress', false);
if (data && data.status == "ok") {
jiraController.set('headlessTickets', data.headlessTickets);
if (data.headlessTickets && data.headlessTickets.length > 0)
{
jiraController.set('headlessNoTickets', false);
headlessTickets = data.headlessTickets;
jiraController.set('headlessTickets', data.headlessTickets);
}
else
{
jiraController.set('headlessNoTickets', true);
}
jiraController.set('usersTickets', data.userTickets);
if (data.userTickets && data.userTickets.length > 0)
{
jiraController.set('userNoTickets', false);
userTickets = data.userTickets;
jiraController.set('userTickets', data.userTickets);
}
else
{
jiraController.set('userNoTickets', true);
}
jiraController.set('membersInProgress', true);
var membersUrl = 'api/v1/jira/members/' + params.user;
$.get(membersUrl, function(data) {
jiraController.set('membersInProgress', false);
if (data && data.status == "ok") {
var currentUser = jiraController.get('selectedUser');
Ember.set(currentUser, 'displayName', data.currentUser[0].displayName);
if (data.members && data.members.length > 0)
{
jiraController.set('userNoMembers', false);
var members = getMemberHeadlessTickets(data.members, headlessTickets);
jiraController.set('members', members);
}
else
{
jiraController.set('userNoMembers', true);
}
}
});
}
});
}
}
});
App.JiraController = Ember.Controller.extend({
actions: {
onSelect: function(dataset, data) {
highlightRow(dataset, data, false);
if (dataset && (dataset.id != 0))
{
updateTimeLine(dataset.id, false);
}
}
}
});
});
})(jQuery)