Beispiel #1
0
    def build_date_context(container):
        """
        Builds the date context (i.e. @date.now, @date.today, ...)
        """
        as_date = container.now.date()
        as_datetime_str = conversions.to_string(container.now, container)
        as_date_str = conversions.to_string(as_date, container)

        return {
            '*': as_datetime_str,
            'now': as_datetime_str,
            'today': as_date_str,
            'tomorrow': conversions.to_string(as_date + timedelta(days=1), container),
            'yesterday': conversions.to_string(as_date - timedelta(days=1), container)
        }
Beispiel #2
0
    def build_date_context(container):
        """
        Builds the date context (i.e. @date.now, @date.today, ...)
        """
        as_date = container.now.date()
        as_datetime_str = conversions.to_string(container.now, container)
        as_date_str = conversions.to_string(as_date, container)

        return {
            '*': as_datetime_str,
            'now': as_datetime_str,
            'today': as_date_str,
            'tomorrow': conversions.to_string(as_date + timedelta(days=1), container),
            'yesterday': conversions.to_string(as_date - timedelta(days=1), container)
        }
Beispiel #3
0
 def get_value_as_text(self, context):
     """
     Gets the input value as text which can be matched by rules
     :param context: the evaluation context
     :return: the text value
     """
     return conversions.to_string(self.value, context)
Beispiel #4
0
    def build_context(self, run, container):
        """
        Builds the evaluation context for this contact
        :param run: the current run state
        :param container: the containing evaluation context
        :return: the context
        """
        context = {
            '*': self.get_display(run.org, False),
            'name': self.name,
            'first_name': self.get_first_name(run.org),
            'tel_e164': self.get_urn_display(run.org, ContactUrn.Scheme.TEL, True),
            'groups': ",".join(self.groups),
            'uuid': self.uuid,
            'language': self.language
        }

        # add all URNs
        for scheme in ContactUrn.Scheme.__members__.values():
            context[unicode(scheme.name).lower()] = self.get_urn_display(run.org, scheme, False)

        # add all fields
        for key, raw_value in self.fields.iteritems():
            field = run.get_or_create_field(key)

            if field and field.value_type == Field.ValueType.DATETIME:
                as_datetime = conversions.to_datetime(raw_value, container)
                value = conversions.to_string(as_datetime, container)
            else:
                value = raw_value

            context[key] = value

        return context
Beispiel #5
0
def rept(ctx, text, number_times):
    """
    Repeats text a given number of times
    """
    if number_times < 0:
        raise ValueError("Number of times can't be negative")
    return conversions.to_string(text, ctx) * conversions.to_integer(number_times, ctx)
Beispiel #6
0
def rept(ctx, text, number_times):
    """
    Repeats text a given number of times
    """
    if number_times < 0:
        raise ValueError("Number of times can't be negative")
    return conversions.to_string(text, ctx) * conversions.to_integer(number_times, ctx)
Beispiel #7
0
def datedif(ctx, start_date, end_date, unit):
    """
    Calculates the number of days, months, or years between two dates.
    """
    start_date = conversions.to_date(start_date, ctx)
    end_date = conversions.to_date(end_date, ctx)
    unit = conversions.to_string(unit, ctx).lower()

    if start_date > end_date:
        raise ValueError("Start date cannot be after end date")

    if unit == 'y':
        return relativedelta(end_date, start_date).years
    elif unit == 'm':
        delta = relativedelta(end_date, start_date)
        return 12 * delta.years + delta.months
    elif unit == 'd':
        return (end_date - start_date).days
    elif unit == 'md':
        return relativedelta(end_date, start_date).days
    elif unit == 'ym':
        return relativedelta(end_date, start_date).months
    elif unit == 'yd':
        return (end_date - start_date.replace(year=end_date.year)).days

    raise ValueError("Invalid unit value: %s" % unit)
Beispiel #8
0
    def visit(self, runner, run, step, input):
        if logger.isEnabledFor(logging.DEBUG):
            logger.debug("Visiting rule set %s with input %s from contact %s"
                         % (self.uuid, unicode(input), run.contact.uuid))

        input.consume()

        context = run.build_context(runner, input)

        match = self.find_matching_rule(runner, run, context)
        if not match:
            return None

        rule, test_result = match

        # get category in the flow base language
        category = rule.category.get_localized_by_preferred([run.flow.base_language], "")

        value_as_str = conversions.to_string(test_result.value, context)
        result = RuleSet.Result(rule, value_as_str, category, input.get_value_as_text(context))
        step.rule_result = result

        run.update_value(self, result, input.time)

        return rule.destination
Beispiel #9
0
def word_slice(ctx, text, start, stop=0, by_spaces=False):
    """
    Extracts a substring spanning from start up to but not-including stop
    """
    text = conversions.to_string(text, ctx)
    start = conversions.to_integer(start, ctx)
    stop = conversions.to_integer(stop, ctx)
    by_spaces = conversions.to_boolean(by_spaces, ctx)

    if start == 0:
        raise ValueError("Start word cannot be zero")
    elif start > 0:
        start -= 1  # convert to a zero-based offset

    if stop == 0:  # zero is treated as no end
        stop = None
    elif stop > 0:
        stop -= 1  # convert to a zero-based offset

    words = __get_words(text, by_spaces)

    selection = operator.getitem(words, slice(start, stop))

    # re-combine selected words with a single space
    return " ".join(selection)
Beispiel #10
0
def remove_first_word(ctx, text):
    """
    Removes the first word from the given text string
    """
    text = conversions.to_string(text, ctx).lstrip()
    first = first_word(ctx, text)
    return text[len(first):].lstrip() if first else ''
Beispiel #11
0
def datedif(ctx, start_date, end_date, unit):
    """
    Calculates the number of days, months, or years between two dates.
    """
    start_date = conversions.to_date(start_date, ctx)
    end_date = conversions.to_date(end_date, ctx)
    unit = conversions.to_string(unit, ctx).lower()

    if start_date > end_date:
        raise ValueError("Start date cannot be after end date")

    if unit == 'y':
        return relativedelta(end_date, start_date).years
    elif unit == 'm':
        delta = relativedelta(end_date, start_date)
        return 12 * delta.years + delta.months
    elif unit == 'd':
        return (end_date - start_date).days
    elif unit == 'md':
        return relativedelta(end_date, start_date).days
    elif unit == 'ym':
        return relativedelta(end_date, start_date).months
    elif unit == 'yd':
        return (end_date - start_date.replace(year=end_date.year)).days

    raise ValueError("Invalid unit value: %s" % unit)
Beispiel #12
0
 def get_value_as_text(self, context):
     """
     Gets the input value as text which can be matched by rules
     :param context: the evaluation context
     :return: the text value
     """
     return conversions.to_string(self.value, context)
Beispiel #13
0
def word_slice(ctx, text, start, stop=0, by_spaces=False):
    """
    Extracts a substring spanning from start up to but not-including stop
    """
    text = conversions.to_string(text, ctx)
    start = conversions.to_integer(start, ctx)
    stop = conversions.to_integer(stop, ctx)
    by_spaces = conversions.to_boolean(by_spaces, ctx)

    if start == 0:
        raise ValueError("Start word cannot be zero")
    elif start > 0:
        start -= 1  # convert to a zero-based offset

    if stop == 0:  # zero is treated as no end
        stop = None
    elif stop > 0:
        stop -= 1  # convert to a zero-based offset

    words = __get_words(text, by_spaces)

    selection = operator.getitem(words, slice(start, stop))

    # re-combine selected words with a single space
    return ' '.join(selection)
Beispiel #14
0
    def build_context(self, run, container):
        """
        Builds the evaluation context for this contact
        :param run: the current run state
        :param container: the containing evaluation context
        :return: the context
        """
        context = {
            '*': self.get_display(run.org, False),
            'name': self.name,
            'first_name': self.get_first_name(run.org),
            'tel_e164': self.get_urn_display(run.org, ContactUrn.Scheme.TEL, True),
            'groups': ",".join(self.groups),
            'uuid': self.uuid,
            'language': self.language
        }

        # add all URNs
        for scheme in ContactUrn.Scheme.__members__.values():
            context[unicode(scheme.name).lower()] = self.get_urn_display(run.org, scheme, False)

        # add all fields
        for key, raw_value in self.fields.iteritems():
            field = run.get_or_create_field(key)

            if field and field.value_type == Field.ValueType.DATETIME:
                as_datetime = conversions.to_datetime(raw_value, container)
                value = conversions.to_string(as_datetime, container)
            else:
                value = raw_value

            context[key] = value

        return context
Beispiel #15
0
def word_count(ctx, text, by_spaces=False):
    """
    Returns the number of words in the given text string
    """
    text = conversions.to_string(text, ctx)
    by_spaces = conversions.to_boolean(by_spaces, ctx)
    return len(__get_words(text, by_spaces))
Beispiel #16
0
Datei: flow.py Projekt: pld/flows
    def visit(self, runner, run, step, input):
        if logger.isEnabledFor(logging.DEBUG):
            logger.debug("Visiting rule set %s with input %s from contact %s" %
                         (self.uuid, unicode(input), run.contact.uuid))
        input.consume()

        context = run.build_context(runner, input)

        match = self.find_matching_rule(runner, run, context)
        if not match:
            return None

        rule, test_result = match

        # get category in the flow base language
        category = rule.category.get_localized_by_preferred(
            [run.get_flow().base_language], "")

        value_as_str = conversions.to_string(test_result.value, context)
        result = RuleSet.Result(step.get_flow(), rule, value_as_str, category,
                                input.get_value_as_text(context), input.media)
        step.rule_result = result

        run.update_value(self, result, input.time)

        return rule.destination
Beispiel #17
0
def word_count(ctx, text, by_spaces=False):
    """
    Returns the number of words in the given text string
    """
    text = conversions.to_string(text, ctx)
    by_spaces = conversions.to_boolean(by_spaces, ctx)
    return len(__get_words(text, by_spaces))
Beispiel #18
0
def remove_first_word(ctx, text):
    """
    Removes the first word from the given text string
    """
    text = conversions.to_string(text, ctx).lstrip()
    first = first_word(ctx, text)
    return text[len(first) :].lstrip() if first else ""
Beispiel #19
0
def _unicode(ctx, text):
    """
    Returns a numeric code for the first character in a text string
    """
    text = conversions.to_string(text, ctx)
    if len(text) == 0:
        raise ValueError("Text can't be empty")
    return ord(text[0])
Beispiel #20
0
def left(ctx, text, num_chars):
    """
    Returns the first characters in a text string
    """
    num_chars = conversions.to_integer(num_chars, ctx)
    if num_chars < 0:
        raise ValueError("Number of chars can't be negative")
    return conversions.to_string(text, ctx)[0:num_chars]
Beispiel #21
0
def concatenate(ctx, *text):
    """
    Joins text strings into one text string
    """
    result = ''
    for arg in text:
        result += conversions.to_string(arg, ctx)
    return result
Beispiel #22
0
 def build_context(self, container):
     return {
         '*': self.value,
         'value': self.value,
         'category': self.category,
         'text': self.text,
         'time': conversions.to_string(self.time, container)
     }
Beispiel #23
0
def concatenate(ctx, *text):
    """
    Joins text strings into one text string
    """
    result = ''
    for arg in text:
        result += conversions.to_string(arg, ctx)
    return result
Beispiel #24
0
 def build_context(self, container):
     return {
         '*': self.value,
         'value': self.value,
         'category': self.category,
         'text': self.text,
         'time': conversions.to_string(self.time, container)
     }
Beispiel #25
0
def left(ctx, text, num_chars):
    """
    Returns the first characters in a text string
    """
    num_chars = conversions.to_integer(num_chars, ctx)
    if num_chars < 0:
        raise ValueError("Number of chars can't be negative")
    return conversions.to_string(text, ctx)[0:num_chars]
Beispiel #26
0
def _unicode(ctx, text):
    """
    Returns a numeric code for the first character in a text string
    """
    text = conversions.to_string(text, ctx)
    if len(text) == 0:
        raise ValueError("Text can't be empty")
    return ord(text[0])
Beispiel #27
0
    def invoke_function(self, ctx, name, arguments):
        """
        Invokes the given function
        :param ctx: the evaluation context
        :param name: the function name (case insensitive)
        :param arguments: the arguments to be passed to the function
        :return: the function return value
        """
        from temba_expressions import EvaluationError, conversions

        # find function with given name
        func = self.get_function(name)
        if func is None:
            raise EvaluationError("Undefined function: %s" % name)

        args, varargs, defaults = self._get_arg_spec(func)

        call_args = []
        passed_args = list(arguments)

        for arg in args:
            if arg == 'ctx':
                call_args.append(ctx)
            elif passed_args:
                call_args.append(passed_args.pop(0))
            elif arg in defaults:
                call_args.append(defaults[arg])
            else:
                raise EvaluationError(
                    "Too few arguments provided for function %s" % name)

        if varargs is not None:
            call_args.extend(passed_args)
            passed_args = []

        # any unused arguments?
        if passed_args:
            raise EvaluationError(
                "Too many arguments provided for function %s" % name)

        try:
            return func(*call_args)
        except Exception as e:
            pretty_args = []
            for arg in arguments:
                if isinstance(arg, str):
                    pretty = '"%s"' % arg
                else:
                    try:
                        pretty = conversions.to_string(arg, ctx)
                    except EvaluationError:
                        pretty = str(arg)
                pretty_args.append(pretty)

            raise EvaluationError(
                "Error calling function %s with arguments %s" %
                (name, ', '.join(pretty_args)), e)
Beispiel #28
0
def regex_group(ctx, text, pattern, group_num):
    """
    Tries to match the text with the given pattern and returns the value of matching group
    """
    text = conversions.to_string(text, ctx)
    pattern = conversions.to_string(pattern, ctx)
    group_num = conversions.to_integer(group_num, ctx)

    expression = regex.compile(
        pattern, regex.UNICODE | regex.IGNORECASE | regex.MULTILINE | regex.V0)
    match = expression.search(text)

    if not match:
        return ""

    if group_num < 0 or group_num > len(match.groups()):
        raise ValueError("No such matching group %d" % group_num)

    return match.group(group_num)
Beispiel #29
0
def substitute(ctx, text, old_text, new_text, instance_num=-1):
    """
    Substitutes new_text for old_text in a text string
    """
    text = conversions.to_string(text, ctx)
    old_text = conversions.to_string(old_text, ctx)
    new_text = conversions.to_string(new_text, ctx)

    if instance_num < 0:
        return text.replace(old_text, new_text)
    else:
        splits = text.split(old_text)
        output = splits[0]
        instance = 1
        for split in splits[1:]:
            sep = new_text if instance == instance_num else old_text
            output += sep + split
            instance += 1
        return output
Beispiel #30
0
def substitute(ctx, text, old_text, new_text, instance_num=-1):
    """
    Substitutes new_text for old_text in a text string
    """
    text = conversions.to_string(text, ctx)
    old_text = conversions.to_string(old_text, ctx)
    new_text = conversions.to_string(new_text, ctx)

    if instance_num < 0:
        return text.replace(old_text, new_text)
    else:
        splits = text.split(old_text)
        output = splits[0]
        instance = 1
        for split in splits[1:]:
            sep = new_text if instance == instance_num else old_text
            output += sep + split
            instance += 1
        return output
Beispiel #31
0
    def evaluate(self, runner, run, context, text):
        matches = []
        for test in self.tests:
            result = test.evaluate(runner, run, context, text)
            if result.matched:
                matches.append(conversions.to_string(result.value, context))
            else:
                return Test.Result.NO_MATCH

        # all came out true, we are true
        return Test.Result.match(" ".join(matches))
Beispiel #32
0
def right(ctx, text, num_chars):
    """
    Returns the last characters in a text string
    """
    num_chars = conversions.to_integer(num_chars, ctx)
    if num_chars < 0:
        raise ValueError("Number of chars can't be negative")
    elif num_chars == 0:
        return ''
    else:
        return conversions.to_string(text, ctx)[-num_chars:]
Beispiel #33
0
    def evaluate(self, runner, run, context, text):
        matches = []
        for test in self.tests:
            result = test.evaluate(runner, run, context, text)
            if result.matched:
                matches.append(conversions.to_string(result.value, context))
            else:
                return Test.Result.NO_MATCH

        # all came out true, we are true
        return Test.Result.match(" ".join(matches))
Beispiel #34
0
def right(ctx, text, num_chars):
    """
    Returns the last characters in a text string
    """
    num_chars = conversions.to_integer(num_chars, ctx)
    if num_chars < 0:
        raise ValueError("Number of chars can't be negative")
    elif num_chars == 0:
        return ''
    else:
        return conversions.to_string(text, ctx)[-num_chars:]
Beispiel #35
0
    def invoke_function(self, ctx, name, arguments):
        """
        Invokes the given function
        :param ctx: the evaluation context
        :param name: the function name (case insensitive)
        :param arguments: the arguments to be passed to the function
        :return: the function return value
        """
        from temba_expressions import EvaluationError, conversions

        # find function with given name
        func = self.get_function(name)
        if func is None:
            raise EvaluationError("Undefined function: %s" % name)

        args, varargs, defaults = self._get_arg_spec(func)

        call_args = []
        passed_args = list(arguments)

        for arg in args:
            if arg == 'ctx':
                call_args.append(ctx)
            elif passed_args:
                call_args.append(passed_args.pop(0))
            elif arg in defaults:
                call_args.append(defaults[arg])
            else:
                raise EvaluationError("Too few arguments provided for function %s" % name)

        if varargs is not None:
            call_args.extend(passed_args)
            passed_args = []

        # any unused arguments?
        if passed_args:
            raise EvaluationError("Too many arguments provided for function %s" % name)

        try:
            return func(*call_args)
        except Exception, e:
            pretty_args = []
            for arg in arguments:
                if isinstance(arg, basestring):
                    pretty = '"%s"' % arg
                else:
                    try:
                        pretty = conversions.to_string(arg, ctx)
                    except EvaluationError:
                        pretty = unicode(arg)
                pretty_args.append(pretty)

            raise EvaluationError("Error calling function %s with arguments %s" % (name, ', '.join(pretty_args)), e)
Beispiel #36
0
    def build_context(self, container, contact_context):
        """
        Builds the evaluation context for this input
        :param container: the evaluation context
        :param contact_context: the context
        :return:
        """
        as_text = self.get_value_as_text(container)

        return {
            '*': as_text,
            'value': as_text,
            'time': conversions.to_string(self.time, container),
            'contact': contact_context
        }
Beispiel #37
0
    def build_context(self, container, contact_context):
        """
        Builds the evaluation context for this input
        :param container: the evaluation context
        :param contact_context: the context
        :return:
        """
        as_text = self.get_value_as_text(container)

        return {
            '*': as_text,
            'value': as_text,
            'time': conversions.to_string(self.time, container),
            'contact': contact_context
        }
Beispiel #38
0
def read_digits(ctx, text):
    """
    Formats digits in text for reading in TTS
    """
    def chunk(value, chunk_size):
        return [
            value[i:i + chunk_size] for i in range(0, len(value), chunk_size)
        ]

    text = conversions.to_string(text, ctx).strip()
    if not text:
        return ''

    # trim off the plus for phone numbers
    if text[0] == '+':
        text = text[1:]

    length = len(text)

    # ssn
    if length == 9:
        result = ' '.join(text[:3])
        result += ' , ' + ' '.join(text[3:5])
        result += ' , ' + ' '.join(text[5:])
        return result

    # triplets, most international phone numbers
    if length % 3 == 0 and length > 3:
        chunks = chunk(text, 3)
        return ' '.join(','.join(chunks))

    # quads, credit cards
    if length % 4 == 0:
        chunks = chunk(text, 4)
        return ' '.join(','.join(chunks))

    # otherwise, just put a comma between each number
    return ','.join(text)
Beispiel #39
0
def read_digits(ctx, text):
    """
    Formats digits in text for reading in TTS
    """

    def chunk(value, chunk_size):
        return [value[i : i + chunk_size] for i in range(0, len(value), chunk_size)]

    text = conversions.to_string(text, ctx).strip()
    if not text:
        return ""

    # trim off the plus for phone numbers
    if text[0] == "+":
        text = text[1:]

    length = len(text)

    # ssn
    if length == 9:
        result = " ".join(text[:3])
        result += " , " + " ".join(text[3:5])
        result += " , " + " ".join(text[5:])
        return result

    # triplets, most international phone numbers
    if length % 3 == 0 and length > 3:
        chunks = chunk(text, 3)
        return " ".join(",".join(chunks))

    # quads, credit cards
    if length % 4 == 0:
        chunks = chunk(text, 4)
        return " ".join(",".join(chunks))

    # otherwise, just put a comma between each number
    return ",".join(text)
Beispiel #40
0
def proper(ctx, text):
    """
    Capitalizes the first letter of every word in a text string
    """
    return conversions.to_string(text, ctx).title()
Beispiel #41
0
def lower(ctx, text):
    """
    Converts a text string to lowercase
    """
    return conversions.to_string(text, ctx).lower()
Beispiel #42
0
def _len(ctx, text):
    """
    Returns the number of characters in a text string
    """
    return len(conversions.to_string(text, ctx))
Beispiel #43
0
def format_location(ctx, text):
    """
    Takes a single parameter (administrative boundary as a string) and returns the name of the leaf boundary
    """
    text = conversions.to_string(text, ctx)
    return text.split(">")[-1].strip()
Beispiel #44
0
def clean(ctx, text):
    """
    Removes all non-printable characters from a text string
    """
    text = conversions.to_string(text, ctx)
    return ''.join([c for c in text if ord(c) >= 32])
Beispiel #45
0
def upper(ctx, text):
    """
    Converts a text string to uppercase
    """
    return conversions.to_string(text, ctx).upper()
Beispiel #46
0
def upper(ctx, text):
    """
    Converts a text string to uppercase
    """
    return conversions.to_string(text, ctx).upper()
Beispiel #47
0
def clean(ctx, text):
    """
    Removes all non-printable characters from a text string
    """
    text = conversions.to_string(text, ctx)
    return ''.join([c for c in text if ord(c) >= 32])
Beispiel #48
0
def _len(ctx, text):
    """
    Returns the number of characters in a text string
    """
    return len(conversions.to_string(text, ctx))
Beispiel #49
0
def lower(ctx, text):
    """
    Converts a text string to lowercase
    """
    return conversions.to_string(text, ctx).lower()
Beispiel #50
0
def proper(ctx, text):
    """
    Capitalizes the first letter of every word in a text string
    """
    return conversions.to_string(text, ctx).title()