Ejemplo n.º 1
0
def relative_date(date_string): 
    """
    Return a relative date string corresponding to date_string, parsed using parsedatetime.

    Returned string will be "today @ hh:mm p" if it's today,
    "tomorrow @ hh:mm p" if it's tomorrow,
    or "<day of week> @hh:mm p" if it's within a week.
    Anything else is "mm/dd/yyyy" (note the lack of a time).
    """

    try:
        # attempt to parse it in the super-wonky way that django passes
        # dates to filters (e.g. not as nice text, sadly)
        thedate = datetime.datetime.strptime(date_string.partition('.')[0], "%Y-%m-%d %H:%M:%S")
    except ValueError:
        # now that we've exhausted our best effort, let's try the next one
        thedate = parsedt(date_string)
        
    try:
        # actually gets the time it was this morning
        rightnow = datetime.datetime.combine(datetime.datetime.now(), datetime.time.min)
        # and compute the difference so we can give relative dates
        diff = thedate - rightnow
    except:
        # any exception here should return nothing
        return None

    if diff.days == 0: # Today
        return 'today @' + thedate.strftime("%-I:%M %p (%m/%d/%Y)") ## at 05:45 PM
    elif diff.days == 1: # Tomorrow
        return 'tomorrow @' + thedate.strftime("%-I:%M %p (%m/%d/%Y)") ## at 05:45 PM Tomorrow
    elif diff.days < 7: # one week from now
        return thedate.strftime("%A @%-I:%M %p (%m/%d/%Y)") ## at 05:45 PM Tuesday
    else:
        return 'on ' + thedate.strftime("%m/%d/%Y") ## on 10/03/1980
Ejemplo n.º 2
0
def pdtest(request):
    fieldvars = {}

    if 'incoming_datetime' in request.POST:
        # attempt to parse it
        fieldvars['incoming_datetime_out'] = request.POST['incoming_datetime']
        fieldvars['parsed_datetime'] = parsedt(request.POST['incoming_datetime'])
    
    return render_to_response('pdtest.html', fieldvars, context_instance=RequestContext(request))
Ejemplo n.º 3
0
def pdtest(request):
    fieldvars = {}

    if 'incoming_datetime' in request.POST:
        # attempt to parse it
        fieldvars['incoming_datetime_out'] = request.POST['incoming_datetime']
        fieldvars['parsed_datetime'] = parsedt(
            request.POST['incoming_datetime'])

    return render_to_response('pdtest.html',
                              fieldvars,
                              context_instance=RequestContext(request))
Ejemplo n.º 4
0
    def satisfied(self, **kwargs):
        try:
            # see if we have the necessary values
            # if not, this check definitely doesn't apply
            msg = kwargs['msg']
        except KeyError:
            return False

        try:
            result = utilities.parsedt(msg.text, verify_complete=True)
        except ValueError:
            # it was not parseable as a datetime :(
            return False
        
        # it matched! stuff our context with useful data and return true
        self.context['message'] = msg.text
        self.context['parsed_datetime'] = result
        return True
Ejemplo n.º 5
0
    def satisfied(self, **kwargs):
        try:
            # see if we have the necessary values
            # if not, this check definitely doesn't apply
            msg = kwargs['msg']
        except KeyError:
            return False

        try:
            result = utilities.parsedt(msg.text, verify_complete=True)
        except ValueError:
            # it was not parseable as a datetime :(
            return False

        # it matched! stuff our context with useful data and return true
        self.context['message'] = msg.text
        self.context['parsed_datetime'] = result
        return True
Ejemplo n.º 6
0
def relative_date(date_string):
    """
    Return a relative date string corresponding to date_string, parsed using parsedatetime.

    Returned string will be "today @ hh:mm p" if it's today,
    "tomorrow @ hh:mm p" if it's tomorrow,
    or "<day of week> @hh:mm p" if it's within a week.
    Anything else is "mm/dd/yyyy" (note the lack of a time).
    """

    try:
        # attempt to parse it in the super-wonky way that django passes
        # dates to filters (e.g. not as nice text, sadly)
        thedate = datetime.datetime.strptime(
            date_string.partition('.')[0], "%Y-%m-%d %H:%M:%S")
    except ValueError:
        # now that we've exhausted our best effort, let's try the next one
        thedate = parsedt(date_string)

    try:
        # actually gets the time it was this morning
        rightnow = datetime.datetime.combine(datetime.datetime.now(),
                                             datetime.time.min)
        # and compute the difference so we can give relative dates
        diff = thedate - rightnow
    except:
        # any exception here should return nothing
        return None

    if diff.days == 0:  # Today
        return 'today @' + thedate.strftime(
            "%-I:%M %p (%m/%d/%Y)")  ## at 05:45 PM
    elif diff.days == 1:  # Tomorrow
        return 'tomorrow @' + thedate.strftime(
            "%-I:%M %p (%m/%d/%Y)")  ## at 05:45 PM Tomorrow
    elif diff.days < 7:  # one week from now
        return thedate.strftime(
            "%A @%-I:%M %p (%m/%d/%Y)")  ## at 05:45 PM Tuesday
    else:
        return 'on ' + thedate.strftime("%m/%d/%Y")  ## on 10/03/1980
Ejemplo n.º 7
0
def signupform(request):
    if request.method == 'POST':  # If the form has been submitted...
        # form = ASAPParticipantForm(request.POST) # A form bound to the POST data
        # if form.is_valid(): # All validation rules pass
        #    subject = form.cleaned_data['subject']

        processed_phone = request.POST['cellphone']

        # ensure that their phone number is in the correct format
        if not processed_phone.startswith("+1"):
            processed_phone = "+1" + processed_phone

        # create a Patient for them, too
        try:
            np = Patient(address=processed_phone,
                         email=request.POST['email'],
                         contact_pref='sms',
                         first_name=request.POST['firstname'],
                         last_name=request.POST['lastname'])
            np.save()
        except IntegrityError as ex:
            # either their address or email address is already in use...what to do?
            raise

        # we're assigning all ASAP signups to the ASAP Admin account
        np.clinicians.add(
            Clinician.objects.get(user=User.objects.get(
                username='******')))

        # also create an ASAPParticipant and put all the form data into their instance
        participant = ASAPParticipant(
            patient=np,
            firstname=request.POST['firstname'],
            lastname=request.POST['lastname'],
            cellphone=request.POST['cellphone'],
            email=request.POST['email'],
            age=request.POST['age']
            if is_number(request.POST['age']) else None,
            zipcode=request.POST['zipcode']
            if is_number(request.POST['zipcode']) else None,
            questionnaire_pref=request.POST['questionnaire_pref'],
            other_diagnosis=request.POST['diagnosis_other_description'])
        participant.save()

        # enumerate their diagnoses and associate them with the user
        for d in request.POST.getlist('diagnoses'):
            participant.diagnoses.add(Diagnosis.objects.get(proper_name=d))

        # finally, schedule all of their tasks to run at various times...
        start_date = utilities.parsedt(settings.ASAP_INITIAL_GOAL_DELAY)

        for goalid in [
                id for id in request.POST['goals_list_hidden'].split(',')
        ]:
            try:
                goalid = int(goalid)
            except ValueError:
                # probably a 'no goal' entry, just continue
                continue

            # get the goal
            goal = ASAPGoal.objects.get(pk=goalid)
            # add to the patient's list of goals
            participant.goals.add(goal)
            # then create a taskinstance for this template
            TaskInstance.objects.create_task(
                patient=np,
                task=goal.tasktemplate.task,
                params=goal.tasktemplate.arguments,
                schedule_date=start_date,
                creator="asap_admin",
                name=goal.tasktemplate.name)
            # increment the start date by 2 weeks
            start_date = utilities.parsedt(settings.ASAP_BETWEEN_GOALS_DELAY,
                                           start_date)

        return HttpResponseRedirect('/ASAP/thanks/')  # Redirect after POST
    else:
        form = ASAPParticipantForm()  # An unbound form

    return render_to_response('signup.html', {
        'form': form,
        'goal_categories': ASAPGoalCategory.objects.all()
    },
                              context_instance=RequestContext(request))
Ejemplo n.º 8
0
def signupform(request):
    if request.method == 'POST': # If the form has been submitted...
        # form = ASAPParticipantForm(request.POST) # A form bound to the POST data
        # if form.is_valid(): # All validation rules pass
        #    subject = form.cleaned_data['subject']

        processed_phone = request.POST['cellphone']

        # ensure that their phone number is in the correct format
        if not processed_phone.startswith("+1"):
            processed_phone = "+1" + processed_phone
            
        # create a Patient for them, too
        try:
            np = Patient(
                address = processed_phone,
                email = request.POST['email'],
                contact_pref = 'sms',
                first_name = request.POST['firstname'],
                last_name = request.POST['lastname']
                )
            np.save()
	except IntegrityError as ex:
            # either their address or email address is already in use...what to do?
            raise
		
        
        # we're assigning all ASAP signups to the ASAP Admin account
        np.clinicians.add(Clinician.objects.get(user=User.objects.get(username='******')))

        # also create an ASAPParticipant and put all the form data into their instance
        participant = ASAPParticipant(
            patient=np,
            firstname=request.POST['firstname'],
            lastname=request.POST['lastname'],
            cellphone=request.POST['cellphone'],
            email=request.POST['email'],
            age=request.POST['age'] if is_number(request.POST['age']) else None,
            zipcode=request.POST['zipcode'] if is_number(request.POST['zipcode']) else None,
            questionnaire_pref=request.POST['questionnaire_pref'],
            other_diagnosis=request.POST['diagnosis_other_description']
            )
        participant.save()

        # enumerate their diagnoses and associate them with the user
        for d in request.POST.getlist('diagnoses'):
            participant.diagnoses.add(Diagnosis.objects.get(proper_name=d))
        
        # finally, schedule all of their tasks to run at various times...
        start_date = utilities.parsedt(settings.ASAP_INITIAL_GOAL_DELAY)
        
        for goalid in [id for id in request.POST['goals_list_hidden'].split(',')]:
            try:
                goalid = int(goalid)
            except ValueError:
                # probably a 'no goal' entry, just continue
                continue
            
            # get the goal
            goal = ASAPGoal.objects.get(pk=goalid)
            # add to the patient's list of goals
            participant.goals.add(goal)
            # then create a taskinstance for this template
            TaskInstance.objects.create_task(
                patient=np,
                task=goal.tasktemplate.task,
                params=goal.tasktemplate.arguments,
                schedule_date=start_date,
                creator="asap_admin",
                name=goal.tasktemplate.name
                )
            # increment the start date by 2 weeks
            start_date = utilities.parsedt(settings.ASAP_BETWEEN_GOALS_DELAY, start_date)
            
        return HttpResponseRedirect('/ASAP/thanks/') # Redirect after POST
    else:
        form = ASAPParticipantForm() # An unbound form

    return render_to_response('signup.html', {
        'form': form,
        'goal_categories': ASAPGoalCategory.objects.all()
    }, context_instance=RequestContext(request))
Ejemplo n.º 9
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()
Ejemplo n.º 10
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()