def get_next_message(request, timeout=30): msg = None try: rc = get_redis_connection() mailbox_id = get_mailbox_id(request) if 0 == timeout: response = rc.lpop(mailbox_id) if response is not None: msg = parse_tutalk_msg(response.decode()) else: response = rc.blpop(mailbox_id, timeout) if response is None: raise exc.MailboxTimeoutException() else: msg = parse_tutalk_msg(response[1].decode()) except exc.NoUidInSessionException as nuide: msg = msgs.mk_no_uid_error_msg(request, None) except exc.InvalidMessageException as ime: msg = msgs.mk_invalid_message_error(request, 'tutalk', response) except exc.MsgMissingTypeException as mmte: msg = msgs.mk_missing_msg_type_error_msg(request, 'tutalk', response) except exc.MailboxTimeoutException as mte: msg = msgs.mk_mailbox_timeout_error_msg(request) return msg
def push_message(request, msg): rc = get_redis_connection() mailbox_id = get_mailbox_id(request) if isinstance(msg, str): rc.lpush(mailbox_id, msg) else: rc.lpush(mailbox_id, json.dumps(msg))
def force_logout_participant(request, exptr_name=None, scen_name=None, part_id=None): rc = get_redis_connection() logout_msg = {"uid": part_id, "gid": "group-%s" % part_id, "type": "logout", "payload": {"text": "logout"}} routing_key = "%s.%s.%s.logout" % (get_tutalk_host(), exptr_name, scen_name) ret_val = rc.publish(routing_key, json.dumps(logout_msg)) time.sleep(2) # allow tutalk to logout user before re-displaying page return redirect("scenario-runner:index")
def store_reload_msgs(request, messages): """stores messages for problem reload in a separate queue sets expire for 30 secs, so it doesn't stick around """ rc = get_redis_connection() reload_id = get_reload_id(request) for msg in messages: rc.rpush(reload_id, json.dumps(msg)) rc.expire(reload_id, 30)
def delete_mailbox(request): """deletes the user's mailbox, if it exists""" mailbox_id = get_mailbox_id(request) if mailbox_id is not None: if not mailbox_exists(mailbox_id): # no-op, mailbox doesn't exist. Unfortuantely, it's unknown if this # is because it is merely empty, has been already deleted, or if # it never existed in the first place. this means that we shouldn't # treat this as an error pass else: rc = get_redis_connection() rc.delete(mailbox_id)
def fetch_all_messages(request): rc = get_redis_connection() mailbox_id = get_mailbox_id(request) messages = [] num_msgs = rc.llen(mailbox_id) for idx in range(0, num_msgs): msg = get_next_message(request, timeout=0) if msg is not None: msg_type = msg.get("type", None) if msg_type is not None: if msg_type not in ["tutor-logout", "error"]: messages.append(msg) return messages
def publish_message(request, tutalk_message): """sends a message to tutalk via redis pub/sub not all 'Rimac' messages are valid 'TuTalk' messages. The message types in SHOULDNT_PUBLISH_MESSAGE_TYPES should merely be logged. also adds the uid and gid fields, so the client doesn't need to pass them FIXME? I suppose ideally, rimac system should only deal with 'Rimac' messages, and there should be a rimac<->tutalk message translation layer. rimac-event-listener partially does this for messages originating from tutalk, but it would probably be better if it instead translated them into rimac-specific message types rather than simply adding more information to the payload """ msg_type = tutalk_message.get('type', None) if msg_type is None: raise exc.MsgMissingTypeException() try: add_uid_gid_fields(request, tutalk_message) except Exception: raise exc.NoUidInSessionException() db.log_message(request, tutalk_message) if should_publish_message(msg_type): if "logout" == msg_type: # we don't want to recieve any messages other than the # corresponding tutor-logout delete_mailbox(request) rc = get_redis_connection() tutalk_host = encode_tutalk_host(request.session['tutalk_host']) routing_key = '%s.%s.%s.%s' % (tutalk_host, request.session['experimenter'], request.session['scenario'], msg_type) msg = json.dumps(tutalk_message) LOGGER.debug("attempting to publish %s", routing_key) LOGGER.debug("message: %s", msg) success = rc.publish(routing_key, msg) if 0 == success: raise exc.ScenarioNotRunningException()
def logged_in_to_tutalk(request): """checks the redis database that the user is logged in. FIXME? should I be double-checking with tutalk itself? """ found = False tutalk_host = encode_tutalk_host(request.session['tutalk_host']) experimenter = request.session['experimenter'] scenario = request.session['scenario'] top_level_key = '%s.scenarios' % tutalk_host groups_key = '%s.%s.groups' % (experimenter, scenario) rc = get_redis_connection() val = rc.hget(top_level_key, groups_key) if val is not None: groups = json.loads(val) for group in groups: for agent in group['agents']: if agent['uid'] == request.session['tutalk_uid']: found = True break return found
def stop_scenario(request, exptr_name=None, scen_name=None): get_object_or_404(models.Experimenter, name=exptr_name) exptr_scens = get_scenarios() if exptr_name in exptr_scens: if scen_name in exptr_scens[exptr_name]: if "pid" in exptr_scens[exptr_name][scen_name]: pid = exptr_scens[exptr_name][scen_name]["pid"] if check_pid(pid): try: os.kill(pid, signal.SIGINT) time.sleep(1) if check_pid(pid): os.kill(pid, signal.SIGKILL) pfx = "%s.%s" % (exptr_name, scen_name) rc = get_redis_connection() rc.hdel( "%s.scenarios" % get_tutalk_host(), "%s.status" % pfx, "%s.pid" % pfx, "%s.groups" % pfx ) time.sleep(2) except OSError as ose: LOGGER.error(ose) return redirect("scenario-runner:index")
def get_running_scenarios(): rc = get_redis_connection() scenarios_key = "%s.scenarios" % get_tutalk_host() dct = rc.hgetall(scenarios_key) return dct
def mailbox_exists(mailbox_id): rc = get_redis_connection() return rc.exists(mailbox_id)