class OpenDisabilityTypeSpec(SumWhenTemplateSpec): type = TypeProperty("open_disability_type") expression = "closed_on IS NULL AND disability_type ~ ?"
class UnderXMonthsTemplateSpec(SumWhenTemplateSpec): type = TypeProperty("under_x_months") expression = "age_at_registration < ?"
class AdultFemaleResidentDeathSpec(SumWhenTemplateSpec): type = TypeProperty("adult_female_resident_death") expression = "sex = 'F' AND resident = 1 AND age_at_death_yrs >= 11"
class OpenPregnantResidentSpec(SumWhenTemplateSpec): type = TypeProperty("open_pregnant_resident") expression = "closed_on IS NULL AND is_pregnant = 1 and sex = 'F' AND resident = 1"
class ReferralHealthProblem5ProblemsSpec(SumWhenTemplateSpec): type = TypeProperty("referral_health_problem_5_problems") expression = "referral_health_problem ~ ? OR referral_health_problem ~ ? OR referral_health_problem ~ ? OR referral_health_problem ~ ? OR referral_health_problem ~ ?"
class OpenMaleHHCasteNotSpec(SumWhenTemplateSpec): type = TypeProperty("open_male_hh_caste_not") expression = "closed_on IS NULL AND sex in ('M', 'O') and hh_caste NOT IN (?, ?)"
class OpenMaleMigrantDistinctFromSpec(SumWhenTemplateSpec): type = TypeProperty("open_male_migrant_distinct_from") expression = "closed_on IS NULL AND sex IN ('M', 'O') AND resident IS DISTINCT FROM 1"
class ChoiceListFilterSpec(FilterSpec): type = TypeProperty('choice_list') show_all = BooleanProperty(default=True) datatype = DataTypeProperty(default='string') choices = ListProperty(FilterChoice)
class FormsInDateExpressionSpec(JsonObject): type = TypeProperty('icds_get_case_forms_in_date') case_id_expression = DefaultProperty(required=True) xmlns = ListProperty(required=False) from_date_expression = DictProperty(required=False) to_date_expression = DictProperty(required=False) count = BooleanProperty(default=False) def configure(self, case_id_expression, from_date_expression=None, to_date_expression=None): self._case_id_expression = case_id_expression self._from_date_expression = from_date_expression self._to_date_expression = to_date_expression def __call__(self, item, context=None): case_id = self._case_id_expression(item, context) if self._from_date_expression: from_date = self._from_date_expression(item, context) else: from_date = None if self._to_date_expression: to_date = self._to_date_expression(item, context) else: to_date = None if not case_id: return [] assert context.root_doc['domain'] return self._get_forms(case_id, from_date, to_date, context) def _get_forms(self, case_id, from_date, to_date, context): domain = context.root_doc['domain'] xmlns_tuple = tuple(self.xmlns) cache_key = (self.__class__.__name__, case_id, self.count, from_date, to_date, xmlns_tuple) if context.get_cache_value(cache_key) is not None: return context.get_cache_value(cache_key) xform_ids = FormsInDateExpressionSpec._get_case_form_ids( case_id, context) # TODO(Emord) this will eventually break down when cases have a lot of # forms associated with them. perhaps change to intersecting two sets xforms = FormsInDateExpressionSpec._get_filtered_forms_from_es( case_id, xform_ids, context) if self.xmlns: xforms = [x for x in xforms if x['xmlns'] in xmlns_tuple] if from_date: xforms = [x for x in xforms if x['timeEnd'] >= from_date] if to_date: xforms = [x for x in xforms if x['timeEnd'] <= to_date] if self.count: count = len(xforms) context.set_cache_value(cache_key, count) return count if not ICDS_UCR_ELASTICSEARCH_DOC_LOADING.enabled( case_id, NAMESPACE_OTHER): form_ids = [x['_id'] for x in xforms] xforms = FormAccessors(domain).get_forms(form_ids) xforms = FormsInDateExpressionSpec._get_form_json_list( case_id, xforms, context, domain) context.set_cache_value(cache_key, xforms) return xforms @staticmethod def _get_filtered_forms_from_es(case_id, xform_ids, context): es_toggle_enabled = ICDS_UCR_ELASTICSEARCH_DOC_LOADING.enabled( case_id, NAMESPACE_OTHER) cache_key = (FormsInDateExpressionSpec.__name__, 'es_helper', case_id, tuple(xform_ids), es_toggle_enabled) if context.get_cache_value(cache_key) is not None: return context.get_cache_value(cache_key) source = True if es_toggle_enabled else [ 'form.meta.timeEnd', 'xmlns', '_id' ] forms = FormsInDateExpressionSpec._bulk_get_forms_from_elasticsearch( xform_ids, source) context.set_cache_value(cache_key, forms) return forms @staticmethod def _get_case_form_ids(case_id, context): cache_key = (FormsInDateExpressionSpec.__name__, 'helper', case_id) if context.get_cache_value(cache_key) is not None: return context.get_cache_value(cache_key) domain = context.root_doc['domain'] xform_ids = CaseAccessors(domain).get_case_xform_ids(case_id) context.set_cache_value(cache_key, xform_ids) return xform_ids @staticmethod def _get_form_json_list(case_id, xforms, context, domain): domain_filtered_forms = [f for f in xforms if f.domain == domain] return [ FormsInDateExpressionSpec._get_form_json(f, context) for f in domain_filtered_forms ] @staticmethod def _get_form_json(form, context): cached_form = FormsInDateExpressionSpec._get_cached_form_json( form, context) if cached_form is not None: return cached_form form_json = form.to_json() FormsInDateExpressionSpec._set_cached_form_json( form, form_json, context) return form_json @staticmethod def _bulk_get_form_json_from_es(forms): form_ids = [form.form_id for form in forms] es_forms = FormsInDateExpressionSpec._bulk_get_forms_from_elasticsearch( form_ids, source=True) return {f['_id']: f for f in es_forms} @staticmethod def _get_cached_form_json(form, context): return context.get_cache_value( FormsInDateExpressionSpec._get_form_json_cache_key(form)) @staticmethod def _set_cached_form_json(form, form_json, context): context.set_cache_value( FormsInDateExpressionSpec._get_form_json_cache_key(form), form_json) @staticmethod def _get_form_json_cache_key(form): return (XFORM_CACHE_KEY_PREFIX, form.form_id) @staticmethod def _bulk_get_forms_from_elasticsearch(form_ids, source): forms = mget_query('forms', form_ids, source) return list( filter(None, [ FormsInDateExpressionSpec. _transform_time_end_and_filter_bad_data(f) for f in forms ])) @staticmethod def _transform_time_end_and_filter_bad_data(xform): xform = xform.get('_source', {}) if not xform.get('xmlns', None): return None try: time = xform['form']['meta']['timeEnd'] except KeyError: return None xform['timeEnd'] = datetime.strptime(time, '%Y-%m-%dT%H:%M:%S.%fZ').date() return xform def __str__(self): value = "case_forms[{case_id}]".format( case_id=self._case_id_expression) if self.from_date_expression or self.to_date_expression: value = "{value}[date={start} to {end}]".format( value=value, start=self._from_date_expression, end=self._to_date_expression) if self.xmlns: value = "{value}[xmlns=\n{xmlns}\n]".format( value=value, xmlns=add_tabbed_text("\n".join(self.xmlns))) if self.count: value = "count({value})".format(value=value) return value
class NumericFilterSpec(FilterSpec): type = TypeProperty('numeric')
class QuarterFilterSpec(FilterSpec): type = TypeProperty('quarter') show_all = BooleanProperty(default=False)
class PreFilterSpec(FilterSpec): type = TypeProperty('pre') pre_value = DefaultProperty(required=True) pre_operator = StringProperty(default=None, required=False)
class LocationDrilldownFilterSpec(FilterSpec): type = TypeProperty('location_drilldown') include_descendants = BooleanProperty(default=False) # default to some random high number '99' max_drilldown_levels = IntegerProperty(default=99)
class MultiFieldDynamicChoiceFilterSpec(DynamicChoiceListFilterSpec): type = TypeProperty('multi_field_dynamic_choice_list') fields = ListProperty(default=[])
class OpenMaleDisabledSpec(SumWhenTemplateSpec): type = TypeProperty("open_male_disabled") expression = "closed_on IS NULL AND sex IN ('M', 'O') and disabled = 1"
class BooleanChoiceQuestion(JsonObject): type = TypeProperty('icds_boolean') boolean_property = DefaultProperty(required=True) true_values = ListProperty(required=True) false_values = ListProperty(required=True) nullable = BooleanProperty(default=True)
class OpenMaleHHCasteSpec(SumWhenTemplateSpec): type = TypeProperty("open_male_hh_caste") expression = "closed_on IS NULL AND sex IN ('M', 'O') and hh_caste = ?"
class RootPropertyNameSpec(JsonObject): type = TypeProperty('ext_root_property_name') property_name = StringProperty(required=True) datatype = StringProperty(required=False)
class OpenMaleHHMinoritySpec(SumWhenTemplateSpec): type = TypeProperty("open_male_hh_minority") expression = "closed_on IS NULL AND sex in ('M', 'O') and hh_minority = 1"
class LocationParentIdSpec(JsonObject): type = TypeProperty('location_parent_id') location_id_expression = DictProperty(required=True)
class OpenMaleResidentSpec(SumWhenTemplateSpec): type = TypeProperty("open_male_resident") expression = "closed_on IS NULL AND sex IN ('M', 'O') AND resident = 1"
class OpenFemaleDisabledSpec(SumWhenTemplateSpec): type = TypeProperty("open_female_disabled") expression = "closed_on IS NULL AND sex = 'F' and disabled = 1"
class ReachedReferralHealthProblem5ProblemsSpec(SumWhenTemplateSpec): type = TypeProperty("reached_referral_health_problem_5_problems") expression = "referral_reached_facility = ? AND (referral_health_problem ~ ? OR referral_health_problem ~ ? OR referral_health_problem ~ ? OR referral_health_problem ~ ? OR referral_health_problem ~ ?)"
class OpenFemaleHHCasteNotSpec(SumWhenTemplateSpec): type = TypeProperty("open_female_hh_caste_not") expression = "closed_on IS NULL AND sex = 'F' and hh_caste NOT IN (?, ?)"
class YearRangeTemplateSpec(SumWhenTemplateSpec): type = TypeProperty('year_range') expression = "year >= ? and year < ?"
class OpenFemaleHHMinoritySpec(SumWhenTemplateSpec): type = TypeProperty("open_female_hh_minority") expression = "closed_on IS NULL AND sex = 'F' and hh_minority = 1"
class AdultFemaleMigrantDeathSpec(SumWhenTemplateSpec): type = TypeProperty("adult_female_migrant_death") expression = "sex = 'F' AND resident IS DISTINCT FROM 1 AND age_at_death_yrs >= 11"
class OpenFemaleResidentSpec(SumWhenTemplateSpec): type = TypeProperty("open_female_resident") expression = "closed_on IS NULL AND sex = 'F' AND resident = 1"
class AgeAtDeathRangeMigrantSpec(SumWhenTemplateSpec): type = TypeProperty("age_at_death_range_migrant") expression = "sex = ? AND resident IS DISTINCT FROM 1 AND date_death - dob BETWEEN ? AND ?"
class FemaleDeathTypeResidentSpec(SumWhenTemplateSpec): type = TypeProperty("female_death_type_resident") expression = "female_death_type = ? AND resident = 1"