diff options
author | Timotej Lazar <timotej.lazar@fri.uni-lj.si> | 2015-12-21 20:11:11 +0100 |
---|---|---|
committer | Timotej Lazar <timotej.lazar@fri.uni-lj.si> | 2015-12-21 20:13:09 +0100 |
commit | f4d7b0e5963f3b1fd3f0eab0be3eef01131cf2d9 (patch) | |
tree | 25194d0698e13e5bda1b99fc5c6deaea882b220b | |
parent | 8c4047f06e7bf24358da32ca6b1ce6fc0a27e449 (diff) |
Support all action types in monkey.action
-rw-r--r-- | monkey/action.py | 113 | ||||
-rw-r--r-- | readme.md | 25 |
2 files changed, 75 insertions, 63 deletions
diff --git a/monkey/action.py b/monkey/action.py index b8bbc40..8a1e477 100644 --- a/monkey/action.py +++ b/monkey/action.py @@ -17,32 +17,68 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. class Action: - # type ∈ ['insert', 'remove', 'solve', 'solve_all', 'next', 'stop', 'test', 'hint'] - # time: absolute elapsed time since the attempt started, in ms - # offset: position of the first inserted/removed character - # text: inserted/removed text or query - # total, passed: number of test cases - def __init__(self, type, time, offset=0, text='', total=0, passed=0): - self.type = type - self.time = time - if type in {'insert', 'remove'}: - self.offset = offset - self.length = len(text) - self.text = text - elif type in {'prolog_solve', 'solve_all'}: - self.query = text - elif type == 'test': - self.total = total - self.passed = passed + def __init__(self, abstime, data): + self.type = data['typ'] + self.time = abstime # time from start + + # generic actions + if self.type == 'open': + self.timestamp = data['time'] + elif self.type == 'ins': + self.type = 'insert' + self.offset = data['off'] + self.text = data['txt'] + self.length = len(self.text) + elif self.type == 'rm': + self.type = 'remove' + self.offset = data['off'] + self.text = data['txt'] + self.length = len(self.text) + elif self.type == 'test': + self.feedback = data['feedback'] + elif self.type == 'hint': + self.feedback = data['feedback'] + elif self.type == 'hnt': + # obsolete Prolog hint action, with no additional info + self.type = 'hint' + self.feedback = None + + # Prolog actions + elif self.type == 'prolog_solve': + self.query = data['query'] + elif self.type == 'slva': + # obsolete Prolog "solve all" action + self.type = 'prolog_solve' + self.query = data['qry'] + + # Python actions + elif self.type == 'python_input': + self.text = data['txt'] + elif self.type == 'python_run': + self.program = data['program'] + + # robot actions + elif self.type == 'robot_run': + self.program = data['program'] def __str__(self): s = 't = ' + str(self.time/1000.0) + ' ' + self.type if self.type in {'insert', 'remove'}: s += ' "' + self.text.replace('\n', '\\n').replace('\t', '\\t') + '" at ' + str(self.offset) - elif self.type in {'prolog_solve', 'solve_all'}: - s += ' "' + self.query + '"' elif self.type == 'test': - s += ' {0} / {1}'.format(self.passed, self.total) + if self.feedback is not None: + for hint in self.feedback: + if hint['id'] == 'test_results': + args = hint['args'] + s += ' {0} / {1}'.format(args['passed'], args['total']) + break + elif self.type == 'hint': + if self.feedback is not None: + s += ' ' + ', '.join(sorted([hint['id'] for hint in self.feedback])) + else: + s += ' ?' + elif self.type == 'prolog_solve': + s += ' "' + self.query + '"' return s # apply this action to text @@ -63,16 +99,6 @@ class Action: else: return text -_packet_action_map = { - 'ins': lambda packet, time, code: Action('insert', time, offset=packet['off'], text=packet['txt']), - 'rm': lambda packet, time, code: Action('remove', time, offset=packet['off'], text=packet['txt']), - 'tst': lambda packet, time, code: Action('test', time, total=packet['tot'], passed=packet['pas']), - 'hnt': lambda packet, time, code: Action('hint', time), - 'slva': lambda packet, time, code: Action('solve_all', time, text=packet['qry']), - 'prolog_solve': lambda packet, time, code: Action('prolog_solve', time, text=packet['query']), - 'prolog_next': lambda packet, time, code: Action('next', time), - 'prolog_end': lambda packet, time, code: Action('stop', time), -} # parse log from database into a list of actions, cleaning up some fluff. # ignore non-text actions (queries and tests) def parse(data): @@ -87,7 +113,7 @@ def parse(data): for packet in data: try: time += packet['dt'] - action = _packet_action_map[packet['typ']](packet, time, code) + action = Action(time, packet) except: # ignore any errors while decoding a packet continue @@ -112,7 +138,7 @@ def parse(data): # replace current action with something better length = action.length - prev.length - new = Action('insert', prev.time, offset = prev.offset, text = action.text[:length]) + new = Action(prev.time, {'typ': 'ins', 'off': prev.offset, 'txt': action.text[:length]}) actions.append(new) code = new.apply(code) @@ -127,25 +153,10 @@ def parse(data): # replace current action with something better length = prev.length - action.length - new = Action('remove', prev.time, offset = prev.offset, text = prev.text[:length]) + new = Action(prev.time, {'typ': 'rem', 'off': prev.offset, 'txt': prev.text[:length]}) actions.append(new) code = new.apply(code) - # discard INSERT/REMOVE pairs (typos) - elif prev.type == 'insert' and action.type == 'remove' and \ - action.time - prev.time < 10000 and \ - action.offset == prev.offset and action.text == prev.text: - # discard last and current actions - code = prev.unapply(code) - actions.pop() - - # discard REMOVE/INSERT pairs (deleted char then typed back) - elif prev.type == 'remove' and action.type == 'insert' and \ - action.offset == prev.offset and action.text == prev.text: - # discard last and current actions - code = prev.unapply(code) - actions.pop() - # otherwise, simply append the current action else: actions.append(action) @@ -160,12 +171,14 @@ def expand(actions): if actions[i].type == 'insert' and len(actions[i].text) > 1: a = actions.pop(i) for offset in range(len(a.text)): - actions.insert(i+offset, Action('insert', a.time, a.offset+offset, a.text[offset])) + actions.insert(i+offset, + Action(a.time, {'typ': 'ins', 'off': a.offset+offset, 'txt': a.text[offset]})) i += len(a.text) elif actions[i].type == 'remove' and len(actions[i].text) > 1: a = actions.pop(i) for offset in range(len(a.text)): - actions.insert(i, Action('remove', a.time, a.offset+offset, a.text[offset])) + actions.insert(i, + Action(a.time, {'typ': 'rm', 'off': a.offset+offset, 'txt': a.text[offset]})) i += len(a.text) else: i += 1 @@ -159,18 +159,18 @@ Traces Actions and corresponding additional attributes are specified here. Generic: - - open(time) # when opening the problem page, time = Date.now() on client - - close() # when closing the problem page - - ins(off, txt) # offset, text - - rm(off, txt) # offset, text, was rm(off, len) - - plan() - - hint(feedback) # feedback = list of returned hint objects - - test(feedback) # feedback = list of returned hint objects + - open(timestamp) # opened the problem page; timestamp = Date.now() on client + - close() # closed the problem page + - ins(off, txt) # inserted text; off = offset, txt = inserted characters + - rm(off, txt) # deleted text; off = offset, txt = deleted characters + - plan() # clicked "plan" + - hint(feedback) # clicked "hint"; feedback = list of returned hint objects + - test(feedback) # clicked "test"; feedback = list of returned hint objects Prolog: - - prolog_solve(query) # was slv(qry) - - prolog_next() # was nxt - - prolog_end() # was stp + - prolog_solve(query) + - prolog_next() + - prolog_end() Python: - python_run(program) @@ -182,6 +182,5 @@ Robot: - robot_stop() Obsolete actions: - - slva(qry) # "solve all" from tuProlog - - hnt # hint button press - - tst(tot, pas) # test results (total / passed) + - slva(qry) # "solve all" from tuProlog + - hnt # hint button press |