def test_unlearn_node(): mo = User({'type': 'google', 'id': 'xPr' }) mo.learn_node('vertex') assert 'vertex' in mo.dict['learned_node_ids'] mo.unlearn_node('vertex') assert 'vertex' not in mo.dict['learned_node_ids']
class SocketHandler (WebSocketHandler): def __init__(self, application, request, **kwargs): WebSocketHandler.__init__(self, application, request, **kwargs) self.user = None def jsend(self, dic): if self.user: user_identifier = self.user.dict['account'] dic['identifier'] = user_identifier else: dic['identifier'] = None self.write_message(dic) def open(self): # this happens BEFORE first-steps self.jsend({ 'command': 'populate-oauth-urls', 'url_dict': auth.auth_url_dict(host=self.request.host), }) def on_message(self, message): log.debug('got message: ' + message+"\n") ball = json.loads(message) if ball['command'] == 'print': print(ball['message']) elif ball['command'] == 'first-steps': self.user = User(ball['identifier']) self.jsend({'command': 'update-user'}) if self.ids(ball): self.send_graph(ball) else: # they've learned nothing yet self.jsend({ 'command': 'prompt-starting-nodes', }) elif ball['command'] == 'get-starting-nodes': subject = ball['subject'] self.user.set_pref({'subject': subject}) self.send_graph(ball) elif ball['command'] == 'get-curriculum': goal = ball['goal'] elif ball['command'] == 'learn-node': command = self.user.learn_node(ball['node_id']) if command is not None: self.jsend(command) if ball['mode'] == 'learn': self.send_graph(ball) else: raise Exception('mode is not learn') elif ball['command'] == 'unlearn-node': self.user.unlearn_node(ball['node_id']) elif ball['command'] == 'set-pref': self.user.set_pref(ball['pref_dict']) elif ball['command'] == 'save-node': # hopefully this can handle both new nodes and changes to nodes node_dict = ball['node_dict'] if 'importance' in node_dict.keys(): node_dict['importance'] = int(node_dict['importance']) try: node_obj = node.create_appropriate_node(node_dict) log.debug('\nnode made. looks like: '+str(node_obj)+'. Now time to put it into the DB...\n') # take a look at the dependencies now # TODO if the node is brand new (mongo can't find it), then let previous_dep_ids = [] previous_dependency_ids = [node.reduce_string(dependency) for dependency in list(our_mongo.find({"_id": node_obj.id}))[0]["_dependencies"]] # if this works, try using set() instead of list and elimination the set()s below log.debug('prev deps are: '+str(previous_dependency_ids)) current_dependency_ids = node_obj.dependency_ids log.debug('curr deps are: '+str(current_dependency_ids)) new_dependency_ids = set(current_dependency_ids) - set(previous_dependency_ids) removed_dependency_ids = set(previous_dependency_ids) - set(current_dependency_ids) # VERIFY THAT THE GRAPH WITH THESE NEW ARCS IS STILL ACYCLIC: H = our_MG.copy() for new_dependency_id in new_dependency_ids: print('from '+str(our_MG.n(new_dependency_id))+' to '+str(node_obj)) H.add_edge(new_dependency_id, node_obj.id) H.validate(node_obj.name + ' cannot depend on ' + our_MG.n(new_dependency_id).name + ' because ' + our_MG.n(new_dependency_id).name + ' already depends on ' + node_obj.name + '!') our_mongo.upsert({ "_id": node_obj.id }, node_obj.__dict__) update_our_MG() # send an update of the graph to the user if there are new dependencies: self.request_nodes(new_dependency_ids, ball) self.remove_client_edges(node_obj.id, removed_dependency_ids) except Exception as error: # stuff didn't work, send error back to user log.warning('ERROR: '+str(error)) self.jsend({ 'command': 'display-error', 'message': str(error), }) elif ball['command'] == 're-center-graph': # We get the 5th nearest neighbors neighbors = our_MG.single_source_shortest_anydirectional_path_length(ball['central_node_id'], 1) # can just use digraph.anydirectional_neighbors H = our_MG.subgraph(list(neighbors.keys())) dict_graph = H.as_js_ready_dict() self.jsend({ 'command': 'load-graph', 'new_graph': dict_graph, }) elif ball['command'] == 'request-node': self.request_nodes([ball['node_id']], ball) elif ball['command'] == 'search': search_results = our_mongo.find({'$text':{'$search':ball['search_term']}},{'score':{'$meta':"textScore"}}) self.jsend({ 'command': 'search-results', 'results': list(search_results.sort([('score', {'$meta': 'textScore'})]).limit(10)), }) elif ball['command'] == 'get-goal-suggestion': goal_id = our_MG.choose_goal(self.user) goal_node = our_MG.n(goal_id) self.jsend({ 'command': 'suggest-goal', 'goal': goal_node.__dict__, }) elif ball['command'] == 'set-goal': goal_id = ball['goal_id'] goal_node = our_MG.n(goal_id) self.user.set_pref({'goal_id': goal_id}) self.send_graph(ball) self.jsend({ 'command': 'highlight-goal', 'goal': goal_node.__dict__, }) elif ball['command'] == 'get-pregoal-suggestion': pregoal_id = our_MG.choose_learnable_pregoals(self.user, number=1)[0] pregoal_node = our_MG.n(pregoal_id) self.jsend({ 'command': 'suggest-pregoal', 'pregoal': pregoal_node.__dict__, }) elif ball['command'] == 'set-pregoal': pregoal_id = ball['pregoal_id'] pregoal_node = our_MG.n(pregoal_id) self.user.set_pref({'requested_pregoal_id': pregoal_id}) self.send_graph(ball) self.jsend({ 'command': 'highlight-pregoal', 'pregoal': pregoal_node.__dict__, }) def request_nodes(self, node_ids, ball): # user manually requests a node. So we want to preserve client_node_ids as not to change anything for them. We only want to ADD the requested nodes additionally. for node_id in node_ids: if node_id not in our_MG.nodes(): raise ValueError('The node_id "'+node_id+'" does not exist.') ids = set(self.ids(ball)).union(set(node_ids)) H = our_MG.subgraph(ids) dict_graph = H.as_js_ready_dict() self.jsend({ 'command': 'load-graph', 'new_graph': dict_graph, }) def ids(self, ball): learned_ids = self.user.dict['learned_node_ids'] return list(set(learned_ids).union(set(ball['client_node_ids']))) def remove_client_edges(self, node_id, dependency_ids): self.jsend({ 'command': 'remove-edges', 'node_id': node_id, 'dependency_ids': list(dependency_ids), }) def send_graph(self, ball): subject = self.user.dict['prefs']['subject'] log.debug('SUBJECT IS: ' + str(subject)) log.debug('LOGGED IN AS: ' + str(self.user.identifier)) nodes_to_send = our_MG.nodes_to_send(self.user, client_node_ids=ball['client_node_ids']) subgraph_to_send = our_MG.subgraph(nodes_to_send) dict_graph = subgraph_to_send.as_js_ready_dict() self.jsend({ 'command': 'load-graph', 'new_graph': dict_graph, }) def starting_nodes(self, subject): return config.starting_nodes[subject] def on_close(self): print('A websocket has been closed.')