def save(self, commit=True): """ put docstring here """ # was: #output = render_to_string(self.template_name, {'object': self}) context = Context() context.update( { 'object':self, 'date': self.date, } ) output = render_to_string(self.template_name, context_instance = context) if commit == True: tasks_response = POSTResponseBase(object=self, xml=self.process_response(payload=output)).register() self.uid = tasks_response.uid self.lastUpdatedUid = tasks_response.lastUpdatedUid return self
def save(self, commit=True): """ put docstring here """ # was: #output = render_to_string(self.template_name, {'object': self}) context = Context() context.update({ 'object': self, 'date': self.date, }) output = render_to_string(self.template_name, context_instance=context) if commit == True: tasks_response = POSTResponseBase( object=self, xml=self.process_response(payload=output)).register() self.uid = tasks_response.uid self.lastUpdatedUid = tasks_response.lastUpdatedUid return self
def _exec_children(self, top, context=None): print "--> Executing children of %s..." % (top) # store the node we're on as the last-expanded node # we'll use this to allow repeating the last taken action on a poke self.last_expansion = self.tree.getpath(top) self.last_expansion_context = context # first off, clear all pre-existing conditions self.conditions = [] self.instance.timeout_date = None self.instance.save() # construct a default context for this evaluation # and add any parent context info to the dict if context: self.context.update(context) default_context = Context({ 'patient': self.instance.patient, 'participant': self.instance.patient.asapparticipant, 'params': self.params }) default_context.update(self.context) # also copy the prefix attribute (if it exists) into our machine's registered prefix if 'prefix' in top.attrib: self.prefix = self.dispatch.request_prefix(self.instance.patient, top.attrib['prefix']) # pre-step: determine if there are any elements that require a response # that may be siblings to a <message> element. we need to know this # so we know whether to tell them to "type prefix before their response" siblings = [node.tag for node in top] accepts_response = ("response" in siblings or "link" in siblings) # execute all top-level elements for node in top: # depending on the type of the thing, perform some action if node.tag == "message": # if there's a condition supplied, evaluate it using the template language # only proceed if the string evaluates to a non-empty string. # if there's no condition, just send it! if (not "condition" in node.attrib) or (self.templatize(node.attrib['condition'], default_context).strip()): # strip node text and collapse whitespace text = " ".join(node.text.strip().split()) # apply django's template engine to the text and send the resulting message self.send(self.templatize(text, default_context), accepts_response=accepts_response) elif node.tag == "response": # we first check to see if this can apply to us at all by evaluating the condition attribute # it works just like it does for message; if it evaluates to false, this node is skipped entirely if (not "condition" in node.attrib) or (self.templatize(node.attrib['condition'], default_context).strip()): if node.get('type',None) == "date_time": # it's a parsedatetime condition rather than a regex one self.conditions.append(ParseDateTimeCondition(self.tree.getpath(node))) else: # it's a regex condition (FIXME: should be a 'regex' type) # add the condition of the response to the action queue print "--> Adding a regex condition in %s" % (top) # angle brackets are mapped to {@ and @} to get around xml's restrictions pattern = node.attrib["pattern"].replace("{@","<").replace("@}",">") self.conditions.append(RegexCondition(self.tree.getpath(node), pattern)) elif node.tag == "timeout": # gather the duration and offset, if specified try: duration = node.attrib['delay'] offset = node.get('offset', None) # optional; None if not present triggerdate = utilities.parsedt(duration, offset) except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) # FIXME: allows temporary override of the timeout duration for testing try: if settings.FORCE_TIMEOUT_DELAY: triggerdate = utilities.parsedt(settings.FORCE_TIMEOUT_DELAY) except: # we don't need to do anything; we just assume the setting wasn't set pass # add the condition of the response to the action queue print "--> Adding a timeout condition in %s" % (top) self.conditions.append(TimeoutCondition(self.tree.getpath(node), triggerdate)) # and register us as requiring a timeout # only replace the timeout if the new one is more recent than the existing one # this is just a nicety to the task writer, since only one timeout # will ever trigger...thus, the first one will erase all subsequent ones anyway self.instance.timeout_date = triggerdate self.instance.save() elif node.tag == "schedule": # gather the duration and offset, if specified try: tasktemplatename = node.attrib['task'] date = self.templatize(node.attrib['date'], default_context) offset = node.get('offset', None) # optional; None if not present if offset is not None: offset = self.templatize(offset, default_context) schedule_date = utilities.parsedt(offset, utilities.parsedt(date)) else: schedule_date = utilities.parsedt(date) except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) # FIXME: allows temporary override of the timeout duration for testing try: if settings.FORCE_SCHEDULE_DELAY: schedule_date = utilities.parsedt(settings.FORCE_SCHEDULE_DELAY) except: # we don't need to do anything; we just assume the setting wasn't set pass # look up the task template that they specified... template = TaskTemplate.objects.get(name=tasktemplatename) # grab its default arguments to start out new_args = json.loads(template.arguments) # and collect any additional arguments (e.g. params) defined in children of this node in <param key="a">value</param> format for param in [n for n in node if n.tag == "param"]: # process the values and insert them into the new_args, too new_args[param.attrib['key']] = self.templatize(param.text, default_context) # this time we spawn another task rather than continuing execution here self.instance.spawn_task( template.task, schedule_date, name=template.name, update_params=new_args ) elif node.tag == "store": # gather the key and value try: key = node.attrib['key'] value = node.attrib['value'] except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) value = self.templatize(value, default_context) print "--> Storing '%s' to key '%s'" % (value, key) # store these to the persistent params collection and save it p = json.loads(self.instance.params) p[key] = value # and don't forget to update the context, too! default_context['params'] = p # finally, save it all back to the db self.instance.params = json.dumps(p) self.instance.save() elif node.tag == "unstore": # gather the key and value try: key = node.attrib['key'] except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) print "--> Unstoring key '%s'" % (key) # store these to the persistent params collection and save it try: p = json.loads(self.instance.params) del p[key] # and don't forget to update the context, too! default_context['params'] = p # finally, save it all back to the db self.instance.params = json.dumps(p) self.instance.save() except: # not sure what to do here self.dispatch.info("Unable to unstore key '%s' from parameters collection" % (key)) elif node.tag == "alert": # gather the key and value try: name = node.attrib['name'] except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) # collect any params defined in children of this node in <param key="a">value</param> format alert_args = {} for param in [n for n in node if n.tag == "param"]: # process the values and insert them into alert_args alert_args[param.attrib['key']] = self.templatize(param.text, default_context) alert_args['url'] = '/taskmanager/patients/%d/history/#session_%d' % (self.instance.patient.id, self.instance.id) # alert_args.update(default_context) Alert.objects.add_alert(name, arguments=alert_args, patient=self.instance.patient) elif node.tag == "abort": # gather the key and value try: scope = node.attrib['scope'] except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) if scope == "process": # remove all pending tasks belonging to the same process TaskInstance.objects.filter(process=self.instance.process, status="pending").delete() # and immediately conclude execution raise TaskCompleteException() elif scope == "others": # end all other tasks that belong to the same process TaskInstance.objects.filter(process=self.instance.process,status="running").exclude(pk=self.instance.id).update(status="completed") elif node.tag == "scope": # immediately expands when reached # if the condition is present and false,the node is ignored. if ("condition" not in node.attrib) or (self.templatize(node.attrib['condition'], default_context).strip()): self._exec_children(node, context) return # we have to break here as well so we don't die immediately elif node.tag == "link": # immediately expand the link with the id specified by target # but first we have to find it target = self.tree.xpath("//*[@id='" + node.attrib["target"] + "']") if len(target) <= 0: raise Exception("Target node for %s couldn't be found!" % ("//*[@id='" + node.attrib["target"] + "']")) # take the first element that matches the given id # (there should only be one, but we're not validating for that) target = target[0] # check for some obvious problem conditions if target == top or target == node: raise Exception("Aborting, link would lead to infinite recursion") print "--> Following link from %s to %s" % (top, target) # if everything's good, jump immediately to that node # we maintain the context to allow us to pass things that we detected in our present node self._exec_children(target, context) return # we have to break here, too... # if there's nothing left on the condition queue then, once again, we're done if not self.conditions: print "--> Dying in %s on account of having no conditions left" % (top) raise TaskCompleteException()
def _exec_children(self, top, context=None): print "--> Executing children of %s..." % (top) # store the node we're on as the last-expanded node # we'll use this to allow repeating the last taken action on a poke self.last_expansion = self.tree.getpath(top) self.last_expansion_context = context # first off, clear all pre-existing conditions self.conditions = [] self.instance.timeout_date = None self.instance.save() # construct a default context for this evaluation # and add any parent context info to the dict if context: self.context.update(context) default_context = Context({ 'patient': self.instance.patient, 'participant': self.instance.patient.asapparticipant, 'params': self.params }) default_context.update(self.context) # also copy the prefix attribute (if it exists) into our machine's registered prefix if 'prefix' in top.attrib: self.prefix = self.dispatch.request_prefix(self.instance.patient, top.attrib['prefix']) # pre-step: determine if there are any elements that require a response # that may be siblings to a <message> element. we need to know this # so we know whether to tell them to "type prefix before their response" siblings = [node.tag for node in top] accepts_response = ("response" in siblings or "link" in siblings) # execute all top-level elements for node in top: # depending on the type of the thing, perform some action if node.tag == "message": # if there's a condition supplied, evaluate it using the template language # only proceed if the string evaluates to a non-empty string. # if there's no condition, just send it! if (not "condition" in node.attrib) or (self.templatize( node.attrib['condition'], default_context).strip()): # strip node text and collapse whitespace text = " ".join(node.text.strip().split()) # apply django's template engine to the text and send the resulting message self.send(self.templatize(text, default_context), accepts_response=accepts_response) elif node.tag == "response": # we first check to see if this can apply to us at all by evaluating the condition attribute # it works just like it does for message; if it evaluates to false, this node is skipped entirely if (not "condition" in node.attrib) or (self.templatize( node.attrib['condition'], default_context).strip()): if node.get('type', None) == "date_time": # it's a parsedatetime condition rather than a regex one self.conditions.append( ParseDateTimeCondition(self.tree.getpath(node))) else: # it's a regex condition (FIXME: should be a 'regex' type) # add the condition of the response to the action queue print "--> Adding a regex condition in %s" % (top) # angle brackets are mapped to {@ and @} to get around xml's restrictions pattern = node.attrib["pattern"].replace("{@", "<").replace( "@}", ">") self.conditions.append( RegexCondition(self.tree.getpath(node), pattern)) elif node.tag == "timeout": # gather the duration and offset, if specified try: duration = node.attrib['delay'] offset = node.get('offset', None) # optional; None if not present triggerdate = utilities.parsedt(duration, offset) except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) # FIXME: allows temporary override of the timeout duration for testing try: if settings.FORCE_TIMEOUT_DELAY: triggerdate = utilities.parsedt( settings.FORCE_TIMEOUT_DELAY) except: # we don't need to do anything; we just assume the setting wasn't set pass # add the condition of the response to the action queue print "--> Adding a timeout condition in %s" % (top) self.conditions.append( TimeoutCondition(self.tree.getpath(node), triggerdate)) # and register us as requiring a timeout # only replace the timeout if the new one is more recent than the existing one # this is just a nicety to the task writer, since only one timeout # will ever trigger...thus, the first one will erase all subsequent ones anyway self.instance.timeout_date = triggerdate self.instance.save() elif node.tag == "schedule": # gather the duration and offset, if specified try: tasktemplatename = node.attrib['task'] date = self.templatize(node.attrib['date'], default_context) offset = node.get('offset', None) # optional; None if not present if offset is not None: offset = self.templatize(offset, default_context) schedule_date = utilities.parsedt( offset, utilities.parsedt(date)) else: schedule_date = utilities.parsedt(date) except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) # FIXME: allows temporary override of the timeout duration for testing try: if settings.FORCE_SCHEDULE_DELAY: schedule_date = utilities.parsedt( settings.FORCE_SCHEDULE_DELAY) except: # we don't need to do anything; we just assume the setting wasn't set pass # look up the task template that they specified... template = TaskTemplate.objects.get(name=tasktemplatename) # grab its default arguments to start out new_args = json.loads(template.arguments) # and collect any additional arguments (e.g. params) defined in children of this node in <param key="a">value</param> format for param in [n for n in node if n.tag == "param"]: # process the values and insert them into the new_args, too new_args[param.attrib['key']] = self.templatize( param.text, default_context) # this time we spawn another task rather than continuing execution here self.instance.spawn_task(template.task, schedule_date, name=template.name, update_params=new_args) elif node.tag == "store": # gather the key and value try: key = node.attrib['key'] value = node.attrib['value'] except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) value = self.templatize(value, default_context) print "--> Storing '%s' to key '%s'" % (value, key) # store these to the persistent params collection and save it p = json.loads(self.instance.params) p[key] = value # and don't forget to update the context, too! default_context['params'] = p # finally, save it all back to the db self.instance.params = json.dumps(p) self.instance.save() elif node.tag == "unstore": # gather the key and value try: key = node.attrib['key'] except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) print "--> Unstoring key '%s'" % (key) # store these to the persistent params collection and save it try: p = json.loads(self.instance.params) del p[key] # and don't forget to update the context, too! default_context['params'] = p # finally, save it all back to the db self.instance.params = json.dumps(p) self.instance.save() except: # not sure what to do here self.dispatch.info( "Unable to unstore key '%s' from parameters collection" % (key)) elif node.tag == "alert": # gather the key and value try: name = node.attrib['name'] except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) # collect any params defined in children of this node in <param key="a">value</param> format alert_args = {} for param in [n for n in node if n.tag == "param"]: # process the values and insert them into alert_args alert_args[param.attrib['key']] = self.templatize( param.text, default_context) alert_args[ 'url'] = '/taskmanager/patients/%d/history/#session_%d' % ( self.instance.patient.id, self.instance.id) # alert_args.update(default_context) Alert.objects.add_alert(name, arguments=alert_args, patient=self.instance.patient) elif node.tag == "abort": # gather the key and value try: scope = node.attrib['scope'] except KeyError as ex: raise XMLFormatException("%s node expects attribute '%s'" % (node.tag, ex.args[0])) if scope == "process": # remove all pending tasks belonging to the same process TaskInstance.objects.filter(process=self.instance.process, status="pending").delete() # and immediately conclude execution raise TaskCompleteException() elif scope == "others": # end all other tasks that belong to the same process TaskInstance.objects.filter( process=self.instance.process, status="running").exclude(pk=self.instance.id).update( status="completed") elif node.tag == "scope": # immediately expands when reached # if the condition is present and false,the node is ignored. if ("condition" not in node.attrib) or (self.templatize( node.attrib['condition'], default_context).strip()): self._exec_children(node, context) return # we have to break here as well so we don't die immediately elif node.tag == "link": # immediately expand the link with the id specified by target # but first we have to find it target = self.tree.xpath("//*[@id='" + node.attrib["target"] + "']") if len(target) <= 0: raise Exception( "Target node for %s couldn't be found!" % ("//*[@id='" + node.attrib["target"] + "']")) # take the first element that matches the given id # (there should only be one, but we're not validating for that) target = target[0] # check for some obvious problem conditions if target == top or target == node: raise Exception( "Aborting, link would lead to infinite recursion") print "--> Following link from %s to %s" % (top, target) # if everything's good, jump immediately to that node # we maintain the context to allow us to pass things that we detected in our present node self._exec_children(target, context) return # we have to break here, too... # if there's nothing left on the condition queue then, once again, we're done if not self.conditions: print "--> Dying in %s on account of having no conditions left" % ( top) raise TaskCompleteException()