Commit f7569324 authored by  Joel  Oksanen's avatar Joel Oksanen
Browse files

Implemented first part of dialogue

parent a449bcca
......@@ -58,6 +58,7 @@ class Agent:
def extract_votes(self, phrases):
votes = {}
vote_phrases = {}
for phrase in phrases:
arguments = self.get_arguments(phrase)
sentiment = self.get_sentiment(phrase)
......@@ -65,10 +66,11 @@ class Agent:
for argument in arguments:
if (argument not in votes) or (abs(votes[argument]) < abs(sentiment)):
votes[argument] = sentiment # what if there's two phrases with same argument?
vote_phrases[argument] = {'phrase': phrase, 'sentiment': sentiment}
# normalize votes to 1 (+) or -1 (-)
for argument in votes:
votes[argument] = 1 if votes[argument] > 0 else -1
return votes
return (votes, vote_phrases)
# augment votes (Definition 4.3) obtained for a single critic
def augment_votes(self, votes):
......@@ -107,7 +109,7 @@ class Agent:
for feature in features:
base_scores[feature] = abs(argument_sums[feature]) / review_count
qbaf = {"supporters": supporters, "attackers": attackers, "base_scores": base_scores}
qbaf = {'supporters': supporters, 'attackers': attackers, 'base_scores': base_scores}
return qbaf
def combined_strength(self, args):
......@@ -132,28 +134,31 @@ class Agent:
attacker_strengths = []
supporter_strengths = []
for child in argument.children:
if child in qbaf["attackers"][argument]:
if child in qbaf['attackers'][argument]:
attacker_strengths.append(strengths[child])
elif child in qbaf["supporters"][argument]:
elif child in qbaf['supporters'][argument]:
supporter_strengths.append(strengths[child])
strengths[argument] = self.argument_strength(qbaf["base_scores"][argument], attacker_strengths, supporter_strengths)
strengths[argument] = self.argument_strength(qbaf['base_scores'][argument], attacker_strengths, supporter_strengths)
return strengths
def analyze_reviews(self, reviews):
# get ra
self.ra = []
self.vote_phrases = {argument : [] for argument in arguments}
voting_reviews = 0
review_count = 0
for _, review in reviews.iterrows():
review_id = review['review_id']
review_count += 1
phrases = self.extract_phrases(review['review_body'])
votes = self.extract_votes(phrases)
votes, vote_phrases = self.extract_votes(phrases)
self.augment_votes(votes)
voting_reviews += 1 if len(votes) > 0 else 0
# add final vote tuples to ra with simplified polarity in {+ (true), - (false)}
for argument in votes:
self.ra.append({'review_id': review_id, 'argument': argument, 'vote': votes[argument]})
for argument in vote_phrases:
self.vote_phrases[argument].append(vote_phrases[argument])
# only consider items that obtained votes from at least 33% of reviewers
if voting_reviews / review_count < 0.33:
print('warning: only a small fraction of reviews generated votes')
......@@ -166,3 +171,26 @@ class Agent:
print(self.qbaf)
print('strengths:')
print(self.strengths)
def get_strongest_supporting_subfeature(self, argument):
supporters = self.qbaf['supporters'][argument]
if len(supporters) == 0:
return None
supporter_strengths = {s : self.strengths[s] for s in supporters}
return max(supporter_strengths, key=supporter_strengths.get)
def get_strongest_attacking_subfeature(self, argument):
attackers = self.qbaf['attackers'][argument]
if len(attackers) == 0:
return None
attacker_strengths = {a : self.strengths[a] for a in attackers}
return max(attacker_strengths, key=attacker_strengths.get)
def supported_argument(self, argument):
return len(self.supporting_phrases(argument)) >= len(self.attacking_phrases(argument))
def supporting_phrases(self, argument):
return list(filter(lambda vp: vp['sentiment'] > 0, self.vote_phrases[argument]))
def attacking_phrases(self, argument):
return list(filter(lambda vp: vp['sentiment'] < 0, self.vote_phrases[argument]))
......@@ -12,27 +12,66 @@ class ADAMessage:
class Communicator:
queries = [
ArgumentQuery(1, 'Why was the {arg} highly rated?'),
ArgumentQuery(2, 'Why was the {arg} poorly rated?')
ArgumentQuery(0, 'Why was the {arg} highly rated?'),
ArgumentQuery(1, 'Why was the {arg} poorly rated?'),
ArgumentQuery(2, 'Why was the {arg} considered to be good?'),
ArgumentQuery(3, 'Why was the {arg} considered to be poor?'),
ArgumentQuery(4, 'Why did users say about the {arg} being good?'),
ArgumentQuery(5, 'Why did users say about the {arg} being poor?'),
]
product = camera # node
agent = Agent()
def __init__(self, product_id, dl):
self.product_id = product_id
def __init__(self, dl):
self.dl = dl
def set_product(self, product_id):
self.product_id = product_id
self.arguments = {arguments[i] : Argument(i, arguments[i].name) for i in range(len(arguments))}
self.argument_nodes = arguments
self.agent.analyze_reviews(self.dl.get_reviews(self.product_id))
def get_init_message(self):
text = 'What would you like to know about the {}?'.format(self.arguments[self.product].name)
if self.dl.get_avg_star_rating(self.product_id) > 3:
queries = [self.queries[0].withArgument(self.arguments[self.product])]
else:
queries = [self.queries[1].withArgument(self.arguments[self.product])]
args = [self.arguments[self.product].withQueries(queries)]
prod_node = self.argument_nodes[0]
prod = self.arguments[prod_node]
text = 'What would you like to know about the {}?'.format(prod.name)
queries = self.get_queries(prod_node)
args = [prod.withQueries(queries)]
return ADAMessage(text, args)
def get_response(self, arg_id, query_id):
return 0
def get_response(self, query_id, arg_id):
q_arg_node = self.argument_nodes[arg_id]
q_arg = self.arguments[q_arg_node]
if query_id == 0:
supp_node = self.agent.get_strongest_supporting_subfeature(q_arg_node)
att_node = self.agent.get_strongest_attacking_subfeature(q_arg_node)
text = 'The {} was highly rated because the {} was good'.format(q_arg.name, self.arguments[supp_node].name)
if att_node:
text += 'although the {} was poor.'.format(self.arguments[att_node].name)
args = [q_arg_node, supp_node, att_node]
else:
text += '.'
args = [q_arg_node, supp_node]
args = [self.arguments[arg].withQueries(self.get_queries(arg)) for arg in args]
return ADAMessage(text, args)
def get_queries(self, arg_node):
arg = self.arguments[arg_node]
queries = []
if arg.id == 0:
# product
prod = self.arguments[self.argument_nodes[0]]
if self.dl.get_avg_star_rating(self.product_id) > 3:
queries.append(self.queries[0].withArgument(prod))
else:
queries.append(self.queries[1].withArgument(prod))
else:
if self.agent.supported_argument(arg_node):
queries.append(self.queries[2].withArgument(arg))
else:
queries.append(self.queries[3].withArgument(arg))
return queries
......@@ -9,6 +9,7 @@ from dataloader import DataLoader
from communicator import Communicator
dl = DataLoader()
communicator = Communicator(dl)
def index(request):
return HttpResponse("OK")
......@@ -20,7 +21,7 @@ def product(request):
star_rating = dl.get_avg_star_rating(id)
image_url = 'https://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=US&ASIN=' + id + '&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=SL250'
communicator = Communicator(id, dl)
communicator.set_product(id)
init_message = communicator.get_init_message()
class Empty:
......@@ -40,8 +41,8 @@ def product(request):
@csrf_exempt
def message(request):
parsed = json.loads(request.body)
arg_id = parsed['argumentID']
query_id = parsed['queryID']
print(arg_id)
print(query_id)
return HttpResponse("OK")
arg_id = parsed['argumentID']
response = communicator.get_response(query_id, arg_id)
print(response.text)
return HttpResponse(jsonpickle.encode(response, unpicklable=False), content_type="application/json")
......@@ -21,9 +21,9 @@ class ChatManager: ObservableObject {
func sendQuery(query: ArgumentQuery) {
connectionManager.sendQuery(query)
sent = true
connectionManager.addMessageWithDelay(query.message, delay: 0.3)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.sent = false
self.connectionManager.addMessage(query.message)
self.clearOptions()
}
}
......
......@@ -11,6 +11,8 @@ import SwiftUI
class ConnectionManager: ObservableObject {
@Published var product = Product()
@Published var messages: [Message] = [Message]()
private var messageQueue: [Message]? = nil
// [UserMessage(text: "Why was the camera highly rated?"),
// ADAMessage(text: "The camera was highly rated because the lens was good, although the battery was poor.", arguments: [Argument(name: "camera"), Argument(name: "lens"), Argument(name: "battery")])
......@@ -29,7 +31,7 @@ class ConnectionManager: ObservableObject {
self.requestImage(at: resp.productInfo.imageURL)
self.product.name = resp.productInfo.name
self.product.starRating = resp.productInfo.starRating
self.messages.append(resp.message)
self.addMessage(resp.message)
}
} catch let parseError {
print(parseError)
......@@ -76,10 +78,18 @@ class ConnectionManager: ObservableObject {
return
}
if let mimeType = httpResponse.mimeType,
mimeType == "text/html",
mimeType == "application/json",
let receivedData = receivedData {
DispatchQueue.main.async {
print(receivedData)
do {
let resp = try JSONDecoder().decode(ADAMessage.self, from: receivedData)
DispatchQueue.main.async {
self.addMessage(resp)
}
} catch let parseError {
print(parseError)
DispatchQueue.main.async {
// Handle error in UI
}
}
}
}
......@@ -87,7 +97,21 @@ class ConnectionManager: ObservableObject {
}
func addMessage(_ message: Message) {
messages.append(message)
if messageQueue != nil {
messageQueue!.append(message)
} else {
messages.append(message)
}
}
func addMessageWithDelay(_ message: Message, delay: Double) {
messageQueue = [message]
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
for message in self.messageQueue! {
self.messages.append(message)
}
self.messageQueue = nil
}
}
private func handleClientError(error: Error) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment