Example #1
0
class CouchConfig(JsonObject):
    user = StringProperty()
    password = StringProperty()
    host = StringProperty(required=True)
    headers = DefaultProperty()

    def __init__(self, obj=None, **kwargs):
        PREFIX = "CI"

        # Default to settings variables
        if 'user' not in obj:
            obj['user'] = getattr(
                settings, "{prefix}_{host}_USER".format(
                    prefix=PREFIX,
                    host=obj['host'].replace('.', '_').upper(),
                ))

        if 'password' not in obj:
            obj['password'] = getattr(
                settings, "{prefix}_{host}_PASSWORD".format(
                    prefix=PREFIX,
                    host=obj['host'].replace('.', '_').upper(),
                ))

        super(CouchConfig, self).__init__(obj, **kwargs)

    @property
    def uri(self):
        return u"https://{user}:{password}@{host}".format(
            user=self.user, password=self.password, host=self.host)
class CaseProperty(JsonObject):
    name = StringProperty()
    forms = ListProperty(CaseFormMeta)
    short_details = ListProperty(CaseDetailMeta)
    long_details = ListProperty(CaseDetailMeta)
    has_errors = BooleanProperty()
    description = StringProperty()

    def get_form(self, form_id):
        try:
            form = next(form for form in self.forms if form.form_id == form_id)
        except StopIteration:
            form = CaseFormMeta(form_id=form_id)
            self.forms.append(form)
        return form

    def add_load(self, form_id, question):
        form = self.get_form(form_id)
        form.load_questions.append(ConditionalFormQuestionResponse(
            question=question,
            condition=None
        ))

    def add_save(self, form_id, question, condition=None):
        form = self.get_form(form_id)
        form.save_questions.append(ConditionalFormQuestionResponse(
            question=question,
            condition=(condition if condition and condition.type == 'if' else None)
        ))

    def add_detail(self, type_, module_id, header, format):
        {
            "short": self.short_details,
            "long": self.long_details,
        }[type_].append(CaseDetailMeta(module_id=module_id, header=header, format=format))
Example #3
0
class ReportFilter(JsonObject):
    """
    This is a spec class that is just used for validation on a ReportConfiguration object.

    These get converted to FilterSpecs (below) by the FilterFactory.
    """
    # todo: this class is silly and can likely be removed.
    type = StringProperty(required=True)
    slug = StringProperty(required=True)
    field = StringProperty(required=True)
    display = DefaultProperty()
    compare_as_string = BooleanProperty(default=False)

    _class_map = {
        'quarter': QuarterFilterValue,
        'date': DateFilterValue,
        'numeric': NumericFilterValue,
        'pre': PreFilterValue,
        'choice_list': ChoiceListFilterValue,
        'dynamic_choice_list': ChoiceListFilterValue,
        'multi_field_dynamic_choice_list': MultiFieldChoiceListFilterValue,
        'location_drilldown': LocationDrilldownFilterValue,
    }

    def create_filter_value(self, value):
        return self._class_map[self.type](self, value)
Example #4
0
class EQAExpressionSpec(JsonObject):
    type = TypeProperty('eqa_expression')
    question_id = StringProperty()
    display_text = StringProperty()
    xmlns = StringProperty()

    def __call__(self, item, context=None):
        xforms_ids = CaseAccessors(item['domain']).get_case_xform_ids(
            item['_id'])
        forms = FormAccessors(item['domain']).get_forms(xforms_ids)
        f_forms = [f for f in forms if f.xmlns == self.xmlns]
        s_forms = sorted(f_forms, key=lambda x: x.received_on)

        if len(s_forms) >= 2:
            curr_form = s_forms[-1]
            prev_form = s_forms[-2]
        elif len(s_forms) == 1:
            curr_form = s_forms[-1]
            prev_form = None
        else:
            curr_form = None
            prev_form = None

        path_question = 'form/%s' % self.question_id

        curr_ques = get_val(curr_form, path_question, 99)
        prev_ques = get_val(prev_form, path_question, 99)

        return {
            'question_id': self.question_id,
            'display_text': self.display_text,
            'current_submission': get_yes_no(curr_ques),
            'previous_submission': get_yes_no(prev_ques),
            'status': STATUSES.get((curr_ques, prev_ques))
        }
Example #5
0
class Product(JsonObject):
    name = StringProperty()
    units = StringProperty()
    sms_code = StringProperty()
    description = StringProperty()
    is_active = BooleanProperty()
    program = ObjectProperty(item_type=Program)
Example #6
0
class EQAPercentExpression(JsonObject):
    type = TypeProperty('eqa_percent_expression')
    question_id = StringProperty()
    display_text = StringProperty()
    xmlns = StringProperty()

    def __call__(self, item, context=None):
        curr_form, prev_form = get_two_last_forms(item, self.xmlns)

        path_question = 'form/%s' % self.question_id

        curr_ques = get_val(curr_form, path_question, -1, float)
        prev_ques = get_val(prev_form, path_question, -1, float)

        if curr_ques == -1 or prev_ques == -1:
            status = "N/A"
        elif curr_ques > prev_ques:
            status = "Improved"
        elif curr_ques < prev_ques:
            status = "Declined"
        else:
            status = "Satisfactory"

        return {
            'question_id': self.question_id,
            'display_text': self.display_text,
            'current_submission':
            "%.2f%%" % curr_ques if curr_ques != -1 else "N/A",
            'previous_submission':
            "%.2f%%" % prev_ques if prev_ques != -1 else "N/A",
            'status': status
        }
Example #7
0
class ArrayAggLastValueReportColumn(ReportColumn):
    type = TypeProperty('array_agg_last_value')
    field = StringProperty(required=True)
    order_by_col = StringProperty(required=False)
    _agg_column_type = ArrayAggColumn

    def get_column_config(self, data_source_config, lang):
        def _last_value(array):
            return array[-1] if array else None

        return ColumnConfig(columns=[
            DatabaseColumn(
                header=self.get_header(lang),
                agg_column=self._agg_column_type(
                    key=self.field,
                    order_by_col=self.order_by_col,
                    alias=self.column_id,
                ),
                format_fn=_last_value,
                data_slug=self.column_id,
                help_text=self.description,
                visible=self.visible,
                sortable=False,
            )
        ])
class CaseTypeMeta(JsonObject):
    name = StringProperty(required=True)
    relationships = DictProperty()  # relationship name -> case type
    properties = ListProperty(CaseProperty)  # property -> CaseProperty
    opened_by = DictProperty(ConditionList)  # form_ids -> [FormActionCondition, ...]
    closed_by = DictProperty(ConditionList)  # form_ids -> [FormActionCondition, ...]
    error = StringProperty()
    has_errors = BooleanProperty()

    def get_property(self, name, allow_parent=False):
        if not allow_parent:
            assert '/' not in name, "Add parent properties to the correct case type"
        try:
            prop = next(prop for prop in self.properties if prop.name == name)
        except StopIteration:
            prop = CaseProperty(name=name)
            self.properties.append(prop)
        return prop

    def add_opener(self, form_id, condition):
        openers = self.opened_by.get(form_id, ConditionList())
        if condition.type == 'if':
            # only add optional conditions
            openers.conditions.append(condition)
        self.opened_by[form_id] = openers

    def add_closer(self, form_id, condition):
        closers = self.closed_by.get(form_id, ConditionList())
        if condition.type == 'if':
            # only add optional conditions
            closers.conditions.append(condition)
        self.closed_by[form_id] = closers
Example #9
0
class SumWhenTemplateSpec(JsonObject):
    type = StringProperty(required=True)
    expression = StringProperty(required=True)
    binds = ListProperty()
    then = IntegerProperty()

    def bind_count(self):
        return len(re.sub(r'[^?]', '', self.expression))
Example #10
0
class StockTransaction(JsonObject):
    beginning_balance = DecimalProperty()
    date = StringProperty()
    ending_balance = DecimalProperty()
    product = StringProperty()
    quantity = DecimalProperty()
    report_type = StringProperty()
    supply_point = IntegerProperty()
Example #11
0
class ChartSpec(JsonObject):
    type = StringProperty(required=True)
    title = StringProperty()
    chart_id = StringProperty()

    @classmethod
    def wrap(cls, obj):
        if obj.get('chart_id') is None:
            obj['chart_id'] = (obj.get('title') or '') + str(hash(json.dumps(sorted(obj.items()))))
        return super(ChartSpec, cls).wrap(obj)
Example #12
0
class GraphDisplayColumn(JsonObject):
    column_id = StringProperty(required=True)
    display = StringProperty(required=True)

    @classmethod
    def wrap(cls, obj):
        # automap column_id to display if display isn't set
        if isinstance(obj, dict) and 'column_id' in obj and 'display' not in obj:
            obj['display'] = obj['column_id']
        return super(GraphDisplayColumn, cls).wrap(obj)
Example #13
0
class ReportsForm(JsonObject):
    time = DateTimeProperty()
    completion_time = DateTimeProperty()
    start_time = DateTimeProperty()
    duration = IntegerProperty()
    submission_time = DateTimeProperty()
    xmlns = StringProperty()
    app_id = StringProperty()
    user_id = StringProperty()
    username = StringProperty()
Example #14
0
class ResumableIteratorState(JsonObject):
    doc_type = "ResumableIteratorState"
    _id = StringProperty()
    name = StringProperty()
    timestamp = DateTimeProperty()
    args = ListProperty()
    kwargs = DictProperty()
    retry = DictProperty()
    progress = DictProperty()
    complete = BooleanProperty(default=False)
Example #15
0
class Location(JsonObject):
    id = IntegerProperty()
    name = StringProperty()
    type = StringProperty()
    parent_id = IntegerProperty()
    latitude = StringProperty()
    longitude = StringProperty()
    code = StringProperty()
    groups = ListProperty()
    historical_groups = DictProperty()
Example #16
0
class EQAActionItemSpec(JsonObject):
    type = TypeProperty('cqi_action_item')
    xmlns = StringProperty()
    section = StringProperty()
    question_id = StringProperty()

    def __call__(self, item, context=None):
        xforms_ids = CommCareCase.objects.get_case_xform_ids(item['_id'])
        forms = XFormInstance.objects.get_forms(xforms_ids, item['domain'])
        f_forms = [f for f in forms if f.xmlns == self.xmlns]
        s_forms = sorted(f_forms, key=lambda x: x.received_on)

        if len(s_forms) > 0:
            latest_form = s_forms[-1]
        else:
            latest_form = None
        path_to_action_plan = 'form/action_plan/%s/action_plan' % self.section

        if latest_form:
            action_plans = latest_form.get_data(path_to_action_plan)
            if action_plans:
                action_plan_for_question = None
                for action_plan in action_plans:
                    if action_plan.get('incorrect_questions', '') == self.question_id:
                        action_plan_for_question = action_plan
                        break
                if action_plan_for_question:
                    incorrect_question = action_plan_for_question.get('incorrect_questions', '')
                    responsible = ', '.join(
                        [
                            item.get(x.strip(), '---') for x in
                            action_plan_for_question.get('action_plan_input', {}).get('responsible', '').split(',')
                        ]
                    )
                    support = ', '.join(
                        [
                            item.get(x.strip(), '---') for x in
                            action_plan_for_question.get('action_plan_input', {}).get('support', '').split(',')
                        ]
                    )
                    application = Application.get(latest_form.app_id)
                    form = application.get_forms_by_xmlns(self.xmlns)[0]
                    question_list = application.get_questions(self.xmlns)
                    questions = {x['value']: x for x in question_list}
                    return {
                        'form_name': form.name['en'],
                        'section': self.section,
                        'timeEnd': latest_form.metadata.timeEnd,
                        'gap': questions.get('data/code_to_text/%s' % incorrect_question, {}).get('label', '---'),
                        'intervention_action': action_plan_for_question.get('intervention_action', '---'),
                        'responsible': responsible,
                        'support': support,
                        'deadline': action_plan_for_question.get('DEADLINE', '---'),
                        'notes': action_plan_for_question.get('notes', '---'),
                    }
Example #17
0
class ResumableIteratorState(JsonObject):
    doc_type = "ResumableIteratorState"
    _id = StringProperty()
    name = StringProperty()
    timestamp = DateTimeProperty()
    args = ListProperty()
    kwargs = DictProperty()
    progress = DictProperty()

    def is_resume(self):
        return bool(getattr(self, '_rev', None))
Example #18
0
class Location(JsonObject):
    id = IntegerProperty()
    name = StringProperty()
    type = StringProperty()
    parent_id = IntegerProperty()
    latitude = StringProperty()
    longitude = StringProperty()
    code = StringProperty()
    groups = ListProperty()
    supervised_by = IntegerProperty()
    supply_points = ListProperty(item_type=SupplyPoint)
    is_active = BooleanProperty()
Example #19
0
class MigrationConfig(JsonObject):
    from_db_postfix = StringProperty()
    to_db_postfix = StringProperty()
    doc_types = ListProperty(required=True)
    couch_views = ListProperty()

    @property
    def source_db(self):
        return get_db(self.from_db_postfix)

    @property
    def dest_db(self):
        return get_db(self.to_db_postfix)
Example #20
0
class FilterSpec(JsonObject):
    """
    This is the spec for a report filter - a thing that should show up as a UI filter element
    in a report (like a date picker or a select list).
    """
    type = StringProperty(required=True, choices=['date', 'numeric', 'pre', 'choice_list', 'dynamic_choice_list'])
    # this shows up as the ID in the filter HTML.
    slug = StringProperty(required=True)
    field = StringProperty(required=True)  # this is the actual column that is queried
    display = DefaultProperty()
    datatype = DataTypeProperty(default='string')

    def get_display(self):
        return self.display or self.slug
Example #21
0
class SupplyPoint(JsonObject):
    id = IntegerProperty()
    active = BooleanProperty()
    code = StringProperty()
    groups = ListProperty()
    last_reported = StringProperty()
    name = StringProperty()
    primary_reporter = IntegerProperty()
    supervised_by = IntegerProperty()
    supplied_by = IntegerProperty()
    type = StringProperty()
    location_id = IntegerProperty()
    products = ListProperty()
    incharges = ListProperty()
Example #22
0
class ReportFilter(JsonObject):
    type = StringProperty(required=True)
    slug = StringProperty(required=True)
    field = StringProperty(required=True)
    display = DefaultProperty()
    compare_as_string = BooleanProperty(default=False)

    def create_filter_value(self, value):
        return {
            'date': DateFilterValue,
            'numeric': NumericFilterValue,
            'choice_list': ChoiceListFilterValue,
            'dynamic_choice_list': ChoiceListFilterValue,
        }[self.type](self, value)
Example #23
0
class FieldColumn(ReportColumn):
    type = TypeProperty('field')
    field = StringProperty(required=True)
    aggregation = StringProperty(
        choices=SQLAGG_COLUMN_MAP.keys(),
        required=True,
    )
    format = StringProperty(default='default', choices=[
        'default',
        'percent_of_total',
    ])
    sortable = BooleanProperty(default=False)

    @classmethod
    def wrap(cls, obj):
        # lazy migrations for legacy data.
        # todo: remove once all reports are on new format
        # 1. set column_id to alias, or field if no alias found
        _add_column_id_if_missing(obj)
        # 2. if aggregation='expand' convert to ExpandedColumn
        if obj.get('aggregation') == 'expand':
            del obj['aggregation']
            obj['type'] = 'expanded'
            return ExpandedColumn.wrap(obj)

        return super(FieldColumn, cls).wrap(obj)

    def format_data(self, data):
        if self.format == 'percent_of_total':
            column_name = self.column_id
            total = sum(row[column_name] for row in data)
            for row in data:
                row[column_name] = '{:.0%}'.format(
                    float(row[column_name]) / total
                )

    def get_sql_column_config(self, data_source_config, lang):
        return SqlColumnConfig(columns=[
            DatabaseColumn(
                header=self.get_header(lang),
                agg_column=SQLAGG_COLUMN_MAP[self.aggregation](self.field, alias=self.column_id),
                sortable=self.sortable,
                data_slug=self.column_id,
                format_fn=self.get_format_fn(),
                help_text=self.description
            )
        ])

    def get_query_column_ids(self):
        return [self.column_id]
Example #24
0
class ReportColumn(JsonObject):
    type = StringProperty(required=True)
    column_id = StringProperty(required=True)
    display = DefaultProperty()
    description = StringProperty()
    transform = DictProperty()
    calculate_total = BooleanProperty(default=False)

    @classmethod
    def wrap(cls, obj):
        if 'display' not in obj and 'column_id' in obj:
            obj['display'] = obj['column_id']
        return super(ReportColumn, cls).wrap(obj)

    def format_data(self, data):
        """
        Subclasses can apply formatting to the entire dataset.
        """
        pass

    def get_sql_column_config(self, data_source_config, lang):
        raise NotImplementedError('subclasses must override this')

    def get_format_fn(self):
        """
        A function that gets applied to the data just in time before the report is rendered.
        """
        if self.transform:
            return TransformFactory.get_transform(self.transform).get_transform_function()
        return None

    def get_query_column_ids(self):
        """
        Gets column IDs associated with a query. These could be different from
        the normal column_ids if the same column ends up in multiple columns in
        the query (e.g. an aggregate date splitting into year and month)
        """
        raise InvalidQueryColumn(_("You can't query on columns of type {}".format(self.type)))

    def get_header(self, lang):
        return localize(self.display, lang)

    def get_column_ids(self):
        """
        Used as an abstraction layer for columns that can contain more than one data column
        (for example, PercentageColumns).
        """
        return [self.column_id]
Example #25
0
class BaseReportColumn(JsonObject):
    type = StringProperty(required=True)
    column_id = StringProperty(required=True)
    display = DefaultProperty()
    description = StringProperty()
    visible = BooleanProperty(default=True)

    @classmethod
    def wrap(cls, obj):
        if 'display' not in obj and 'column_id' in obj:
            obj['display'] = obj['column_id']
        return super(BaseReportColumn, cls).wrap(obj)

    def get_header(self, lang):
        return localize(self.display, lang)

    def get_column_ids(self):
        """
        Used as an abstraction layer for columns that can contain more than one data column
        (for example, PercentageColumns).
        """
        return [self.column_id]

    def get_column_config(self, data_source_config, lang):
        raise NotImplementedError('subclasses must override this')

    def aggregations(self, data_source_config, lang):
        """
        Returns a list of aggregations to be used in an ES query
        """
        raise NotImplementedError('subclasses must override this')

    def get_es_data(self,
                    row,
                    data_source_config,
                    lang,
                    from_aggregation=True):
        """
        Returns a dictionary of the data of this column from an ES query
        """
        raise NotImplementedError('subclasses must override this')

    def get_fields(self, data_source_config=None, lang=None):
        """
        Get database fields associated with this column. Could be one, or more
        if a column is a function of two values in the DB (e.g. PercentageColumn)
        """
        raise NotImplementedError('subclasses must override this')
Example #26
0
def TypeProperty(value):
    """
    Shortcut for making a required property and restricting it to a single specified
    value. This adds additional validation that the objects are being wrapped as expected
    according to the type.
    """
    return StringProperty(required=True, choices=[value])
Example #27
0
class LocationColumn(ReportColumn):
    type = TypeProperty('location')
    field = StringProperty(required=True)
    sortable = BooleanProperty(default=False)

    def format_data(self, data):
        column_name = self.column_id
        for row in data:
            try:
                row[column_name] = '{g.latitude} {g.longitude} {g.altitude} {g.accuracy}'.format(
                    g=GeoPointProperty().wrap(row[column_name]))
            except BadValueError:
                row[column_name] = u'{} ({})'.format(row[column_name],
                                                     _('Invalid Location'))

    def get_column_config(self, data_source_config, lang):
        return ColumnConfig(columns=[
            DatabaseColumn(header=self.get_header(lang),
                           agg_column=SimpleColumn(self.field,
                                                   alias=self.column_id),
                           sortable=self.sortable,
                           data_slug=self.column_id,
                           format_fn=self.get_format_fn(),
                           help_text=self.description)
        ])
Example #28
0
class GetLastCasePropertyUpdateSpec(JsonObject):
    type = TypeProperty('icds_get_last_case_property_update')
    case_property = StringProperty(required=True)
    case_id_expression = DefaultProperty(required=False)
    start_date = DefaultProperty(required=False)
    end_date = DefaultProperty(required=False)
    filter = DefaultProperty(required=False)
Example #29
0
class PillowResetConfig(JsonObject):
    seq = StringProperty(required=True)
    pillow_names = ListProperty(required=True)

    @property
    def pillows(self):
        return [get_pillow_by_name(name) for name in self.pillow_names]
Example #30
0
class SortItemsExpressionSpec(NoPropertyTypeCoercionMixIn, JsonObject):
    ASC, DESC = "ASC", "DESC"
    type = TypeProperty('sort_items')
    items_expression = DefaultProperty(required=True)
    sort_expression = DictProperty(required=True)
    order = StringProperty(choices=[ASC, DESC], default=ASC)

    def configure(self, items_expression, sort_expression):
        self._items_expression = items_expression
        self._sort_expression = sort_expression

    def __call__(self, doc, context=None):
        items = _evaluate_items_expression(self._items_expression, doc,
                                           context)

        try:
            return sorted(items,
                          key=lambda i: self._sort_expression(i, context),
                          reverse=True if self.order == self.DESC else False)
        except TypeError:
            return []

    def __str__(self):
        return "sort:\n{items}\n{order} on:\n{sort}".format(
            items=add_tabbed_text(str(self._items_expression)),
            order=self.order,
            sort=add_tabbed_text(str(self._sort_expression)))