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

Feature/poet 61 query issues based on status #30

Open
wants to merge 46 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
358b5cb
NLP using LUIS Api
sabiipoks Nov 21, 2016
2dde489
NLP classification model implementation
sabiipoks Nov 21, 2016
624871f
Methods added for handling additional questions
sabiipoks Nov 21, 2016
c430870
Modifications to work with JIRA Api
sabiipoks Nov 21, 2016
57c35fe
Methods for JIRA info extraction
sabiipoks Nov 21, 2016
e330e2c
Skeleton for additional questions
sabiipoks Nov 21, 2016
4f5586e
Updated code to fetch info from JIRA
sabiipoks Feb 28, 2017
4e3030c
Added response for when POET understands the question but the feature…
sabiipoks Mar 1, 2017
cd2dc88
Added further response for when POET understands the question but the…
sabiipoks Mar 1, 2017
1da2ed6
Updated code to work with the BOT
sabiipoks Mar 1, 2017
31632e5
Merge branch 'master' into features/POET-26-Add-intelligence
sabiipoks Mar 2, 2017
4248f29
Code cleaning. Made LUIS App ID as environment variables.
sabiipoks Mar 5, 2017
134cb8a
Merge remote-tracking branch 'origin/features/POET-26-Add-intelligenc…
sabiipoks Mar 5, 2017
7d94b2c
Change method name.
sabiipoks Mar 8, 2017
30c92ed
Made methods private.
sabiipoks Mar 8, 2017
883e583
Add method to response to greeting from the users.
sabiipoks Mar 8, 2017
e8530a9
Merge branch 'master' into features/POET-26-Add-intelligence
sabiipoks Mar 8, 2017
271f98c
Remove commented lines.
sabiipoks Mar 8, 2017
6014ea7
Merge remote-tracking branch 'origin/features/POET-26-Add-intelligenc…
sabiipoks Mar 8, 2017
66abf37
BugFix after resolving conflict.
sabiipoks Mar 8, 2017
deac5dc
Change return type from JSON to IntentEntity in several methods.
sabiipoks Mar 8, 2017
95ad00c
Handle exceptions.
sabiipoks Mar 13, 2017
1633bad
Updated error message.
sabiipoks Mar 13, 2017
9438738
Merged code for GetIssueStatus
sabiipoks Mar 19, 2017
5fa79d4
Merge branch 'features/POET-26-Add-intelligence' into feature/restruc…
dxli94 Mar 28, 2017
319111d
Refactoring code. (not tested yet)
dxli94 Mar 28, 2017
b5c321d
Refactor UserQueryHandler.
dxli94 Mar 29, 2017
4102c81
Refactor JiraService.
dxli94 Mar 29, 2017
23908ac
remove files not in use.
dxli94 Mar 29, 2017
0435f19
Refactor utilities. Add error message for luis failure.
dxli94 Mar 29, 2017
f5e9180
update comment on toJson()
dxli94 Mar 29, 2017
df09ff2
Refactor UserQueryHandler.
dxli94 Mar 29, 2017
c9ed870
Rename fetchJiraApi() to fetchTicketByApi().
dxli94 Mar 29, 2017
42969cf
make hyperlink() case insensitive.
u5833088 Mar 30, 2017
1089ed0
refactor code. Injection not done.
dxli94 Apr 20, 2017
c3e5110
Merge branch 'feature/restructure-code' of github.com:agiledigital/po…
dxli94 Apr 20, 2017
00e2ea3
Merge branch 'master' into feature/restructure-code
sabiipoks Apr 20, 2017
0792eee
Add questions asked by users to Database and query it.
sabiipoks Apr 23, 2017
d1476c1
Convert database response into JSON format.
sabiipoks Apr 23, 2017
9602389
Reformat bot reply for displaying list of questions.
sabiipoks Apr 23, 2017
52b00ee
Display list of questions as bullet points
sabiipoks Apr 23, 2017
15cc526
Display list of questions as bullet points
sabiipoks Apr 23, 2017
b1a0939
Query issues based on assignee username.
sabiipoks May 3, 2017
e1aca71
Return only the issues that is "In progress"
sabiipoks May 3, 2017
7ac83d5
Query Issues based on their status.
sabiipoks May 3, 2017
a36c21e
Udated code to understand more status.
sabiipoks May 11, 2017
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
8 changes: 5 additions & 3 deletions client/scripts/bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

module.exports = function (robot) {

return robot.hear(/[A-Za-z] (.*)/i, function (res) {
return robot.hear(/poet-slack-bot (.*)/i, function (res) {

if (jiraIgnoreUsers && res.envelope.user.name.match(new RegExp(jiraIgnoreUsers, "gi"))) {
return;
Expand All @@ -15,6 +15,10 @@
var message = res.match[1];

httpRequest().doGET(message).then(function (response) {
/*
The response type is not checked here as we want the BOT
to respond back to the user whenever a question is asked.
*/
if (response) {
return res.send("@" + res.envelope.user.name + " " + response.message);

Expand All @@ -26,8 +30,6 @@

}).call(this);

///


// Connect to JIRA through httprequest

Expand Down
26 changes: 13 additions & 13 deletions server/app/controllers/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import play.libs.ws.WSClient;
import play.mvc.*;

import services.queryHandler.QueryHandler;
import services.ServicesManager;

import javax.inject.Inject;
import java.io.IOException;
Expand All @@ -12,18 +11,19 @@

public class Application extends Controller {

@Inject
WSClient ws;
@Inject
WSClient ws;

public Result index() {
return ok("Hi!");
}
public Result index() {
return ok("Hi!");
}

public CompletionStage<Result> getAnwser(String query) throws IOException,
ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, IllegalAccessException {
public CompletionStage<Result> show(String query)
throws IOException, ClassNotFoundException,
NoSuchMethodException, InvocationTargetException,
IllegalAccessException {

QueryHandler queryHandler = new QueryHandler(query, ws);
return queryHandler.handleQuery();
}
ServicesManager servicesManager = new ServicesManager(ws);
return servicesManager.interpretQueryAndActOnJira(query);
}
}
15 changes: 15 additions & 0 deletions server/app/models/JiraAuth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package models;

/**
* @author dxli
*/

public class JiraAuth {
public String username;
public String password;

public JiraAuth(String username, String password) {
this.username = username;
this.password = password;
}
}
18 changes: 18 additions & 0 deletions server/app/models/LuisResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package models;

/**
* @author sabinapokhrel
*
*/

public class LuisResponse {
public String intent;
public String entityType;
public String entityName;

public LuisResponse(String intent, String entityType, String entityName) {
this.intent = intent;
this.entityType = entityType;
this.entityName = entityName;
}
}
16 changes: 16 additions & 0 deletions server/app/models/ResponseToClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package models;

/**
* @author sabinapokhrel
*
*/

public class ResponseToClient {
public String status;
public String message;

public ResponseToClient(String status, String message) {
this.status = status;
this.message = message;
}
}
7 changes: 7 additions & 0 deletions server/app/modules/PoetModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package modules;

/**
* Created by dxli on 30/03/17.
*/
public class PoetModule {
}
264 changes: 264 additions & 0 deletions server/app/services/JiraReaderService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
package services;

import com.fasterxml.jackson.databind.JsonNode;
import models.JiraAuth;
import models.ResponseToClient;
import play.libs.Json;
import play.libs.ws.WSAuthScheme;
import play.libs.ws.WSClient;
import play.libs.ws.WSRequest;
import play.libs.ws.WSResponse;
import utils.ConfigUtilities;

import java.util.concurrent.CompletionStage;

/**
* The class is for all "reading actions" on JIRA.
* Extracting fields from raw response on need.
*/
public class JiraReaderService {
private String messageToReturn;
private WSClient ws;
private JiraAuth jiraAuth;


public JiraReaderService(WSClient ws, JiraAuth jiraAuth) {
this.ws = ws;
this.jiraAuth = jiraAuth;
}

/**
* To request for JIRA ticket info page via REST API. A non-blocking call.
*
* @param ticketId ticket ID in string.
* @return info page encoded in JSON.
*/
public CompletionStage<JsonNode> fetchTicketByApi(String ticketId) {

System.out.println(ConfigUtilities.getString("jira.baseUrl")
+ ConfigUtilities.getString("jira.issueEndpoint")
+ ticketId);

WSRequest request = ws.url(ConfigUtilities.getString("jira.baseUrl")
+ ConfigUtilities.getString("jira.issueEndpoint")
+ ticketId);
WSRequest complexRequest = request.setAuth(jiraAuth.username, jiraAuth.password, WSAuthScheme.BASIC);

return complexRequest.get().thenApply(WSResponse::asJson);
}

/**
* To request information based on username via REST API. A non-blocking call.
*
* @param jiraUsername jira username in string.
* @return info page encoded in JSON.
*/
public CompletionStage<JsonNode> fetchAssigneeInfoByApi(String jiraUsername) {

WSRequest request = ws.url("https://jira.agiledigital.com.au/rest/api/2/search")
.setQueryParameter("jql", "assignee=" + jiraUsername + " and status='in progress'");
WSRequest complexRequest = request.setAuth(jiraAuth.username, jiraAuth.password, WSAuthScheme.BASIC);
return complexRequest.get().thenApply(WSResponse::asJson);
}

/**
* To request information based on status via REST API. A non-blocking call.
*
* @param status status of issue in string.
* @return info page encoded in JSON.
*/
public CompletionStage<JsonNode> fetchIssuesForStatusByApi(String status) {

WSRequest request = ws.url("https://jira.agiledigital.com.au/rest/api/2/search")
.setQueryParameter("jql", "status='" + status +"'");
WSRequest complexRequest = request.setAuth(jiraAuth.username, jiraAuth.password, WSAuthScheme.BASIC);
return complexRequest.get().thenApply(WSResponse::asJson);
}

/**
* //TODO : comment this.
Copy link
Member

Choose a reason for hiding this comment

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

Comment this?

*
* @param response
* @param intent
* @param entity
* @return
*/
public JsonNode read(JsonNode response, String intent, String entity) {
Boolean isSuccess = false;
System.out.println(intent + " " + entity);
switch (intent) {
case "IssueDescription":
isSuccess = readDescription(entity, response);
break;
case "IssueAssignee":
isSuccess = readAssignee(entity, response);
break;
case "IssueStatus":
isSuccess = readStatus(entity, response);
break;
case "AssigneeIssues":
isSuccess = readIssues(entity, response);
break;
case "IssuesForStatus":
isSuccess = readIssuesForStatus(entity, response);
break;
}

if (isSuccess) {
return Json.toJson(new ResponseToClient(JiraServiceProvider.REQUEST_SUCCESS, messageToReturn));
} else {
return Json.toJson(new ResponseToClient(JiraServiceProvider.REQUEST_FAILURE,
ConfigUtilities.getString("error-message.issue-not-found")));
}
}

/**
* This method reads assignee of the ticket.
*
* @param ticketNo is the IssueID of type string which was mentioned in the query by the user.
* @param responseBody is the JSON object received from JIRA Rest API.
* @return true if success, otherwise if no such ticket exists, false.
*/
private Boolean readAssignee(String ticketNo, JsonNode responseBody) {
if (responseBody.get("errorMessages") != null) {
return false;
} else {
this.messageToReturn = responseBody.get("fields").get("assignee").get("displayName").textValue()
+ " is working on "
+ ticketNo + ".";
return true;
}
}

/**
* This method reads description of the ticket.
*
* @param responseBody is the JSON object received from JIRA Rest API.
* @return true if success, otherwise if no such ticket exists, false.
*/
private Boolean readDescription(String ticketNo, JsonNode responseBody) {
if (responseBody.get("errorMessages") != null) {
return false;
} else {
this.messageToReturn = hyperlinkTicketNo("Description of "
// + '<' + "http://google.com" +'|' +ticketNo+ '>' // replace google by the actual ticket page
Copy link
Member

Choose a reason for hiding this comment

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

Please remove this commented code.

Copy link
Member

Choose a reason for hiding this comment

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

I think this is the same file in another PR. Please make sure the other PR merged first, then rebase this branch's changes on top of master.

+ ticketNo
+ " is as follows: \n"
+ responseBody.get("fields").get("description").textValue());

return true;
}
}

/**
* This method reads status of the ticket.
*
* @param ticketNo is the IssueID of type string which was mentioned in the query by the user.
* @param responseBody is the JSON object received from JIRA Rest API.
* @return true if success, otherwise if no such ticket exists, false.
*/
private Boolean readStatus(String ticketNo, JsonNode responseBody) {
if (responseBody.get("errorMessages") != null) {
return false;
} else {
this.messageToReturn = hyperlinkTicketNo("The status of "
+ ticketNo
+ " is "
+ responseBody.get("fields").get("status").get("name").textValue());
return true;
}
}

/**
* This method reads status of the ticket.
*
* @param assignee is the username of assignee of type string which was mentioned in the query by the user.
* @param responseBody is the JSON object received from JIRA Rest API.
* @return true if success, otherwise if no such ticket exists, false.
*/
private Boolean readIssues(String assignee, JsonNode responseBody) {
if (responseBody.get("errorMessages") != null) {
return false;
} else {
int issueCount = Integer.parseInt(responseBody.get("total").toString());
if (issueCount > 0) {
StringBuffer issues = new StringBuffer();
for (int i = 0, j = 0; i < issueCount; i++) {
String string = responseBody.get("issues").findValues("key").get(j).textValue();
StringBuffer tmp;
if (i < issueCount - 1)
tmp = new StringBuffer(string + ", ");
else
tmp = new StringBuffer(string + ".");
issues.append(tmp);
j = j + 6;
}
this.messageToReturn = hyperlinkTicketNo(assignee + " is working on " + issues.toString());
} else {
this.messageToReturn = assignee + " is currently not working on any issue.";
}
}
return true;
}

/**
* This method reads status of the ticket.
*
* @param assignee is the username of assignee of type string which was mentioned in the query by the user.
* @param responseBody is the JSON object received from JIRA Rest API.
* @return true if success, otherwise if no such ticket exists, false.
*/
private Boolean readIssuesForStatus(String status, JsonNode responseBody) {
System.out.println(responseBody);
if (responseBody.get("errorMessages") != null) {
return false;
} else {
//System.out.println(responseBody);
int issueCount = Integer.parseInt(responseBody.get("total").toString());
if (issueCount > 0) {
StringBuffer issues = new StringBuffer();
for (int i = 0, j = 0; i < issueCount; i++) {
String string = responseBody.get("issues").findValues("key").get(j).textValue();
StringBuffer tmp;
if (i < issueCount - 1)
tmp = new StringBuffer(string + ", ");
else
tmp = new StringBuffer(string + ".");
issues.append(tmp);
j = j + 6;
}
this.messageToReturn = hyperlinkTicketNo(status + " issues are " + issues.toString());
} else {
this.messageToReturn = "There are no issues " + status + ".";
}
}
return true;
}




/**
* The methods hyperlinks the ticket number appearing in the
* description, or actually, any string fed into. This is implemented
* by first checking the project name configured in the application.conf
* and then use the slack formatting to wrap around the ticket number.
*
* @param issueDescription the description of the ticket queried.
* This can also be applied to any other strings.
* @return the issue description after formatting. This
* should hyperlink the ticket numbers appearing in
* the input string.
* @Reference: https://api.slack.com/docs/message-formatting
*/
private static String hyperlinkTicketNo(String issueDescription) {
String jiraIssueName = ConfigUtilities.getString("jira.issueName");
String pattern = "((?i)" + jiraIssueName + "-\\d+)";
String issueUrl = ConfigUtilities.getString("jira.baseUrl") + "/browse/";
String hyperlink = "<" + issueUrl + "$1|$1>";

issueDescription = issueDescription.replaceAll(pattern, hyperlink);
return issueDescription;
}

}
Loading