| 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 
 |