Esempio n. 1
0
File: base.py Progetto: MVReddy/TMS
 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
Esempio n. 2
0
 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
Esempio n. 3
0
    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()
Esempio n. 4
0
    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()