rev |
line source |
igor@46
|
1 #!/usr/bin/python
|
igor@46
|
2
|
igor@46
|
3 import random
|
igor@46
|
4 import sys
|
igor@52
|
5 import time
|
igor@46
|
6
|
igor@46
|
7 # TODO:
|
igor@46
|
8 # * persistent weight dict
|
igor@46
|
9 # * log
|
igor@46
|
10 # * stats (top5, time_total, time_last, correct_answers_rate_total, correct_answers_rate_last)
|
igor@46
|
11
|
igor@46
|
12 # DONE:
|
igor@46
|
13 # * correct quit (ctrl d)
|
igor@46
|
14
|
igor@52
|
15 logfile = "/home/igor/Langs/Deutsch/training-scripts/geschlecht/zubrator.log"
|
igor@52
|
16
|
igor@52
|
17 class _Getch:
|
igor@52
|
18 """Gets a single character from standard input. Does not echo to the
|
igor@52
|
19 screen."""
|
igor@52
|
20 def __init__(self):
|
igor@52
|
21 try:
|
igor@52
|
22 self.impl = _GetchWindows()
|
igor@52
|
23 except ImportError:
|
igor@52
|
24 self.impl = _GetchUnix()
|
igor@52
|
25
|
igor@52
|
26 def __call__(self): return self.impl()
|
igor@52
|
27
|
igor@52
|
28
|
igor@52
|
29 class _GetchUnix:
|
igor@52
|
30 def __init__(self):
|
igor@52
|
31 import tty, sys
|
igor@52
|
32
|
igor@52
|
33 def __call__(self):
|
igor@52
|
34 import sys, tty, termios
|
igor@52
|
35 fd = sys.stdin.fileno()
|
igor@52
|
36 old_settings = termios.tcgetattr(fd)
|
igor@52
|
37 try:
|
igor@52
|
38 tty.setraw(sys.stdin.fileno())
|
igor@52
|
39 ch = sys.stdin.read(1)
|
igor@52
|
40 finally:
|
igor@52
|
41 termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
igor@52
|
42 return ch
|
igor@52
|
43
|
igor@52
|
44
|
igor@52
|
45 class _GetchWindows:
|
igor@52
|
46 def __init__(self):
|
igor@52
|
47 import msvcrt
|
igor@52
|
48
|
igor@52
|
49 def __call__(self):
|
igor@52
|
50 import msvcrt
|
igor@52
|
51 return msvcrt.getch()
|
igor@52
|
52
|
igor@52
|
53
|
igor@52
|
54 getch = _Getch()
|
igor@52
|
55
|
igor@52
|
56 def log_answer(result, question, correct_answer, given_answer):
|
igor@52
|
57 with open(logfile, "a") as f:
|
igor@52
|
58 timestamp = time.strftime("%Y-%m-%d %H:%M")
|
igor@52
|
59 f.write(" ".join([timestamp, result, question, correct_answer, given_answer])+"\n")
|
igor@52
|
60
|
igor@52
|
61
|
igor@46
|
62 def color_for_answer(answer):
|
igor@46
|
63 color_table = {
|
igor@46
|
64 'der': 'Blue',
|
igor@46
|
65 'das': 'Green',
|
igor@46
|
66 'die': 'Red',
|
igor@46
|
67 }
|
igor@46
|
68 if not answer in color_table:
|
igor@46
|
69 return 'Normal'
|
igor@46
|
70 else:
|
igor@46
|
71 return color_table[answer]
|
igor@46
|
72
|
igor@46
|
73 def colorprint(string, color=None):
|
igor@46
|
74 color_table = {
|
igor@46
|
75 'Gray': '\033[1;30m',
|
igor@46
|
76 'Red': '\033[1;31m',
|
igor@46
|
77 'Green': '\033[1;32m',
|
igor@46
|
78 'Yellow': '\033[1;33m',
|
igor@46
|
79 'Blue': '\033[1;34m',
|
igor@46
|
80 'Magenta': '\033[1;35m',
|
igor@46
|
81 'Cyan': '\033[1;36m',
|
igor@46
|
82 'White': '\033[1;37m',
|
igor@46
|
83 'Crimson': '\033[1;38m',
|
igor@46
|
84 'Highlighted_Red': '\033[1;41m',
|
igor@46
|
85 'Highlighted_Green': '\033[1;42m',
|
igor@46
|
86 'Highlighted_Brown': '\033[1;43m',
|
igor@46
|
87 'Highlighted_Blue': '\033[1;44m',
|
igor@46
|
88 'Highlighted_Magenta': '\033[1;45m',
|
igor@46
|
89 'Highlighted_Cyan': '\033[1;46m',
|
igor@46
|
90 'Highlighted_Gray': '\033[1;47m',
|
igor@46
|
91 'Highlighted_Crimson': '\033[1;48m',
|
igor@46
|
92 }
|
igor@46
|
93 normal_color_code = '\033[1;m'
|
igor@46
|
94 if not color or color == 'Normal' or not color in color_table:
|
igor@46
|
95 print string
|
igor@46
|
96 else:
|
igor@46
|
97 print "%s%s%s" % (color_table[color], string, normal_color_code)
|
igor@46
|
98
|
igor@46
|
99 def wrandom(dict):
|
igor@46
|
100 total = sum(dict.values())
|
igor@46
|
101 n = random.uniform(0, total)
|
igor@46
|
102
|
igor@46
|
103 for key in sorted(dict.keys()):
|
igor@46
|
104 item = key
|
igor@46
|
105 if n < dict[key]:
|
igor@46
|
106 break
|
igor@46
|
107 n -= dict[key]
|
igor@46
|
108
|
igor@46
|
109 return item
|
igor@46
|
110
|
igor@46
|
111 def set_weight(weight, word, new_weight):
|
igor@46
|
112 if len(weight) <= 1:
|
igor@46
|
113 raise Exception("Can't set weight; weight dictionary is too small; need at least two members")
|
igor@46
|
114 sum_before = sum(weight.values())
|
igor@46
|
115 w_before = weight[word]
|
igor@46
|
116 w_after = new_weight
|
igor@46
|
117 delta = (w_after - w_before)*1.0/(len(weight)-1)
|
igor@46
|
118 for k in weight.keys():
|
igor@46
|
119 if k == word:
|
igor@46
|
120 weight[k] = w_after
|
igor@46
|
121 else:
|
igor@46
|
122 weight[k] -= delta
|
igor@46
|
123 sum_after = sum(weight.values())
|
igor@46
|
124 if abs(sum_before-sum_after)> 0.0001:
|
igor@46
|
125 raise Exception("%s != %s ; function set_weight works incorrectly" % (sum_before, sum_after))
|
igor@46
|
126 return weight
|
igor@46
|
127
|
igor@46
|
128 def print_stats(stats, weight, correct_answer):
|
igor@46
|
129 print "------------------------"
|
igor@46
|
130 print "total questions = %s" % stats['total_questions']
|
igor@46
|
131 print "last questions = %s" % stats['last_questions']
|
igor@46
|
132 print "total errors = %s (%.2f)" % (stats['total_errors'], 1.0*stats['total_errors']/stats['total_questions'])
|
igor@46
|
133 print "last errors = %s (%.2f)" % (stats['last_errors'], 1.0*stats['last_errors']/stats['last_questions'])
|
igor@46
|
134 print "top 5 questions:"
|
igor@46
|
135 for question in sorted(weight.keys(),key=lambda x: weight[x], reverse=True)[:5]:
|
igor@46
|
136 colorprint(
|
igor@46
|
137 " %s %s %5.2f" % (correct_answer[question], question, weight[question]),
|
igor@46
|
138 color_for_answer(correct_answer[question])
|
igor@46
|
139 )
|
igor@46
|
140
|
igor@46
|
141 print "------------------------"
|
igor@46
|
142
|
igor@46
|
143 filename = sys.argv[1]
|
igor@46
|
144 correct_answer = {}
|
igor@46
|
145 with open(filename) as f:
|
igor@46
|
146 for line in f.readlines():
|
igor@46
|
147 line = line.rstrip('\n')
|
igor@46
|
148 try:
|
igor@46
|
149 (q, a) = line.split(' ', 1)
|
igor@46
|
150 correct_answer[q] = a
|
igor@46
|
151 except:
|
igor@46
|
152 pass
|
igor@46
|
153
|
igor@46
|
154 saved_weight = {
|
igor@46
|
155 'Auskunft' : 2,
|
igor@46
|
156 }
|
igor@46
|
157
|
igor@46
|
158 weight = {}
|
igor@46
|
159 for word in correct_answer.keys():
|
igor@46
|
160 if word in saved_weight:
|
igor@46
|
161 weight[word] = saved_weight[word]
|
igor@46
|
162 else:
|
igor@46
|
163 weight[word] = 1
|
igor@46
|
164
|
igor@46
|
165 stats = {
|
igor@46
|
166 'total_errors' :0,
|
igor@46
|
167 'last_errors' :0,
|
igor@46
|
168 'total_questions' :0,
|
igor@46
|
169 'last_questions' :0,
|
igor@46
|
170 }
|
igor@46
|
171
|
igor@46
|
172 while 1:
|
igor@46
|
173 question = wrandom(weight)
|
igor@46
|
174 colorprint(question, 'Yellow')
|
igor@52
|
175 #answer = sys.stdin.readline().rstrip('\n')
|
igor@52
|
176 ch = getch()
|
igor@52
|
177 codes = {
|
igor@52
|
178 'q': 'der',
|
igor@52
|
179 'w': 'das',
|
igor@52
|
180 'e': 'die',
|
igor@52
|
181
|
igor@52
|
182 'p': 'der',
|
igor@52
|
183 '[': 'das',
|
igor@52
|
184 ']': 'die'
|
igor@52
|
185 }
|
igor@52
|
186 if ch in codes:
|
igor@52
|
187 answer = codes[ch]
|
igor@52
|
188 else:
|
igor@52
|
189 answer = ''
|
igor@52
|
190
|
igor@46
|
191 if not answer:
|
igor@46
|
192 break
|
igor@52
|
193
|
igor@52
|
194 result = "OK"
|
igor@46
|
195 if answer != correct_answer[question]:
|
igor@52
|
196 result = "FAIL"
|
igor@46
|
197 colorprint(
|
igor@46
|
198 "%s %s" % (correct_answer[question], question),
|
igor@46
|
199 color_for_answer(correct_answer[question])
|
igor@46
|
200 )
|
igor@46
|
201 weight = set_weight(weight, question, weight[question]*1.5)
|
igor@46
|
202 stats['total_errors'] += 1
|
igor@46
|
203 stats['last_errors'] += 1
|
igor@46
|
204 else:
|
igor@46
|
205 weight = set_weight(weight, question, weight[question]*0.8)
|
igor@46
|
206
|
igor@52
|
207 log_answer(result, question, correct_answer[question], answer)
|
igor@52
|
208
|
igor@46
|
209 stats['total_questions'] += 1
|
igor@46
|
210 stats['last_questions'] += 1
|
igor@46
|
211
|
igor@46
|
212 if stats['last_questions'] == 20:
|
igor@46
|
213 print_stats(stats, weight, correct_answer)
|
igor@46
|
214 stats['last_questions'] = 0
|
igor@46
|
215 stats['last_errors'] = 0
|
igor@46
|
216
|
igor@46
|
217 print
|
igor@46
|
218
|