-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcsv_controller.py
197 lines (177 loc) · 7.64 KB
/
csv_controller.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# -*- coding: utf-8 -*-
"""
© Copyright 2014. Joon Yang & Jaemin Cheun. All rights reserved.
Deals with creating or reading .csv files to produce flashcards.
"""
from expectimaxAgents import ExpectimaxAgent
from expectipruneAgents import ExpectipruneAgent
from progress import ProgressState
from time import time
import os
import csv
import configure
import random
import copy
class Reader(object):
"""Reads off .csv file and quizzes user"""
def __init__(self, filename, timer):
self.data_path = os.path.join(os.getcwd(), "flashcards/%s" % filename)
self.txt = open(self.data_path, 'rU')
self.progress = []
self.timer = timer
self.reader = csv.reader(self.txt, delimiter=",")
self.qna, self.prev_knwdge = self.load(self.data_path)
print "=" * 100
print("To Quit at any time, just type in \"quit\" or press Enter")
def ask(self, message):
print "=" * 100
print message
def load(self, path):
"""Loads a CSV file returns a dict of answers/questions"""
self.db = {}
total = 0 # previous overall understanding level
entries = 0
for line in self.reader:
entries += 1
total += float(line[2]) * 100
self.db[line[0]] = [line[1], float(line[2])]
return self.db, round(total / entries, 2)
def start(self):
"""
Protocol for asking questions and verifying. Integrates dyanamic horizon allocation,
as well as pruning cards that share similar levels of understanding. Multiple choice
option is also implemented.
:print: User's improvement of understanding of the deck of flashcards
"""
def run_random():
"""Util for generating random multiple choice options"""
opt1 = random.choice(self.db.values())[0]
opt2 = random.choice(self.db.values())[0]
return opt1, opt2
def update_csv():
"""Update csv file with new understanding levels"""
with open(self.data_path, 'w') as txt:
writer = csv.writer(txt, delimiter=',')
data = []
total = 0 # new overall understanding level
for q in self.qna:
total += float(self.qna[q][1]) * 100
data.append([q, self.qna[q][0], self.qna[q][1]])
writer.writerows(data)
return round(total / len(data), 2)
def random_classifier(orig_dict):
"""
Before using expectimax on every single Q&A, prune by first traversing
the list of words and grouping ones that have similar levels of understanding
and choose one from each group at random to pass onto the algorithm
"""
# new output dict
new_dict = {}
# utility to code in random choice from list (dict is troubling here)
temp_list = []
# keep note of which knowledge_level 'class' we've seen
pseu_memoizer = []
for e in orig_dict.items():
temp_list.append([e[0], e[1][0], e[1][1]])
random.shuffle(temp_list)
for item in temp_list:
knowledge_level = round(float(item[2]), 2) # yields the rounded values
if knowledge_level not in pseu_memoizer:
pseu_memoizer.append(knowledge_level)
new_dict[item[0]] = [item[1], item[2]]
return new_dict
try:
init_time = time()
new_time = 0
last_q = ''
while new_time - init_time < self.timer:
dict_copy = copy.deepcopy(self.qna)
pruned_copy = random_classifier(dict_copy)
# don't ask the same question twice in a row
if last_q in pruned_copy:
del pruned_copy[last_q]
# print func here to check how many pruned after random classifier
# print "pruned length {} / {}".format(len(pruned_copy), len(dict_copy))
# choose the horizon based on how many nodes we're looking at
if 30 <= len(pruned_copy):
agent = ExpectipruneAgent(depth = '1')
elif 20 <= len(pruned_copy) < 30:
agent = ExpectipruneAgent(depth = '2')
elif 10 <= len(pruned_copy) < 20:
agent = ExpectipruneAgent(depth = '3')
elif len(pruned_copy) < 10:
agent = ExpectipruneAgent(depth = '4')
question = agent.getPolicy(ProgressState(pruned_copy))
answer = self.qna[question][0]
if not question or not answer:
continue
self.ask("Question: %s" % question)
answer_start = time()
# assuring mutex options
opt1, opt2 = run_random()
while opt1 == answer or opt2 == answer:
opt1, opt2 = run_random()
while opt1 == opt2:
opt1, opt2 = run_random()
# answer choice is from 1~3
ans_idx = random.randrange(3)
# the other (wrong) answer choices
opt1_idx = (ans_idx + 1) % 3
opt2_idx = (ans_idx + 2) % 3
# mechanism for mixing order of options
options = [1, 2, 3]
options[ans_idx] = answer
options[opt1_idx] = opt1
options[opt2_idx] = opt2
options.sort()
for x, option in enumerate(options):
print("[{0}]: {1}".format(x + 1, option))
attempt = raw_input(">> ")
answer_end = time()
time_taken = answer_end - answer_start
if attempt == "quit" or "":
break
if options[int(attempt) - 1] == answer:
self.qna = ProgressState(self.qna,question).generateSuccessor("human", 1, time_taken).getProgress()
print "Correct!"
else:
self.qna = ProgressState(self.qna,question).generateSuccessor("human", 0, time_taken).getProgress()
print("The correct answer was: %s" % answer)
# record this card so we don't immediately ask the same question
last_q = question
# to check elapsed time
new_time = time()
finally:
# update csv file
new_knwdge = update_csv()
# tells the user improvement since last time
print "IMPROVEMENT: {} points".format(round((new_knwdge - self.prev_knwdge) / self.prev_knwdge * 1000), 2)
class Writer(object):
"""Appends questions and answers into the csv file."""
def __init__(self, data_path):
self.txt = open(data_path,'a')
self.qna = self.get_qna()
def start(self):
writer = csv.writer(self.txt)
for q in self.qna: # group is a tuple of strings
question = "%s," % q
answer = "%s," % self.qna[q]
progress = "0.5"
row = (question + answer + progress).split(",")
writer.writerow(row)
self.txt.close()
def get_qna(self):
print("To stop adding cards, type quit")
qna = {}
print "="*100
while True:
print "="*100
question = raw_input("Question: ")
if question == "quit":
break
answer = raw_input("Answer: ")
if answer == "quit":
break
qna[question] = answer
print "\n"
return qna