def as_elasticsearch(self, org, prop_map): prop_type, field = prop_map[self.prop] if prop_type == ContactQuery.PROP_FIELD: field_uuid = str(field.uuid) es_query = es_Q("term", **{"fields.field": field_uuid}) if field.value_type == Value.TYPE_TEXT: query_value = self.value.lower() if self.comparator == "=": es_query &= es_Q("term", **{"fields.text": query_value}) elif self.comparator == "!=": es_query &= es_Q("term", **{"fields.text": query_value}) es_query &= es_Q("exists", **{"field": "fields.text"}) # search for the inverse of what was specified return ~es_Q("nested", path="fields", query=es_query) else: raise SearchException(_(f"Unknown text comparator: '{self.comparator}'")) elif field.value_type == Value.TYPE_NUMBER: query_value = str(self._parse_number(self.value)) if self.comparator == "=": es_query &= es_Q("match", **{"fields.number": query_value}) elif self.comparator == ">": es_query &= es_Q("range", **{"fields.number": {"gt": query_value}}) elif self.comparator == ">=": es_query &= es_Q("range", **{"fields.number": {"gte": query_value}}) elif self.comparator == "<": es_query &= es_Q("range", **{"fields.number": {"lt": query_value}}) elif self.comparator == "<=": es_query &= es_Q("range", **{"fields.number": {"lte": query_value}}) else: raise SearchException(_(f"Unknown number comparator: '{self.comparator}'")) elif field.value_type == Value.TYPE_DATETIME: query_value = str_to_date(self.value, field.org.get_dayfirst()) if not query_value: raise SearchException(_(f"Unable to parse the date '{self.value}'")) # datetime contact values are serialized as ISO8601 timestamps in local time on ElasticSearch lower_bound, upper_bound = date_to_day_range_utc(query_value, org) if self.comparator == "=": es_query &= es_Q( "range", **{"fields.datetime": {"gte": lower_bound.isoformat(), "lt": upper_bound.isoformat()}} ) elif self.comparator == ">": es_query &= es_Q("range", **{"fields.datetime": {"gte": upper_bound.isoformat()}}) elif self.comparator == ">=": es_query &= es_Q("range", **{"fields.datetime": {"gte": lower_bound.isoformat()}}) elif self.comparator == "<": es_query &= es_Q("range", **{"fields.datetime": {"lt": lower_bound.isoformat()}}) elif self.comparator == "<=": es_query &= es_Q("range", **{"fields.datetime": {"lt": upper_bound.isoformat()}}) else: raise SearchException(_(f"Unknown datetime comparator: '{self.comparator}'")) elif field.value_type in (Value.TYPE_STATE, Value.TYPE_DISTRICT, Value.TYPE_WARD): query_value = self.value.lower() if field.value_type == Value.TYPE_WARD: field_name = "fields.ward" elif field.value_type == Value.TYPE_DISTRICT: field_name = "fields.district" elif field.value_type == Value.TYPE_STATE: field_name = "fields.state" else: # pragma: no cover raise SearchException(_(f"Unknown location type: '{field.value_type}'")) if self.comparator == "=": field_name += "_keyword" es_query &= es_Q("term", **{field_name: query_value}) elif self.comparator == "!=": field_name += "_keyword" es_query &= es_Q("term", **{field_name: query_value}) es_query &= es_Q("exists", **{"field": field_name}) return ~es_Q("nested", path="fields", query=es_query) else: raise SearchException(_(f"Unsupported comparator '{self.comparator}' for location field")) else: # pragma: no cover raise SearchException(_(f"Unrecognized contact field type '{field.value_type}'")) return es_Q("nested", path="fields", query=es_query) elif prop_type == ContactQuery.PROP_ATTRIBUTE: query_value = self.value.lower() field_key = field.key if field_key == "name": if self.comparator == "=": field_name = "name.keyword" es_query = es_Q("term", **{field_name: query_value}) elif self.comparator == "~": field_name = "name" es_query = es_Q("match", **{field_name: query_value}) elif self.comparator == "!=": field_name = "name.keyword" es_query = ~es_Q("term", **{field_name: query_value}) else: raise SearchException(_(f"Unknown attribute comparator: '{self.comparator}'")) elif field_key == "id": es_query = es_Q("ids", **{"values": [query_value]}) elif field_key == "language": if self.comparator == "=": field_name = "language" es_query = es_Q("term", **{field_name: query_value}) elif self.comparator == "!=": field_name = "language" es_query = ~es_Q("term", **{field_name: query_value}) else: raise SearchException(_(f"Unknown attribute comparator: '{self.comparator}'")) elif field_key == "created_on": query_value = str_to_date(self.value, field.org.get_dayfirst()) if not query_value: raise SearchException(_(f"Unable to parse the date '{self.value}'")) # contact created_on is serialized as ISO8601 timestamp in utc time on ElasticSearch lower_bound, upper_bound = date_to_day_range_utc(query_value, org) if self.comparator == "=": es_query = es_Q( "range", **{"created_on": {"gte": lower_bound.isoformat(), "lt": upper_bound.isoformat()}} ) elif self.comparator == ">": es_query = es_Q("range", **{"created_on": {"gte": upper_bound.isoformat()}}) elif self.comparator == ">=": es_query = es_Q("range", **{"created_on": {"gte": lower_bound.isoformat()}}) elif self.comparator == "<": es_query = es_Q("range", **{"created_on": {"lt": lower_bound.isoformat()}}) elif self.comparator == "<=": es_query = es_Q("range", **{"created_on": {"lt": upper_bound.isoformat()}}) else: raise SearchException(_(f"Unknown created_on comparator: '{self.comparator}'")) else: # pragma: no cover raise SearchException(_(f"Unknown attribute field '{field}'")) return es_query elif prop_type == ContactQuery.PROP_SCHEME: query_value = self.value.lower() es_query = es_Q("term", **{"urns.scheme": field.lower()}) if org.is_anon: return es_Q("ids", **{"values": [-1]}) else: if self.comparator == "=": es_query &= es_Q("term", **{"urns.path.keyword": query_value}) elif self.comparator == "~": es_query &= es_Q("match_phrase", **{"urns.path": query_value}) else: raise SearchException(_(f"Unknown scheme comparator: '{self.comparator}'")) return es_Q("nested", path="urns", query=es_query) else: # pragma: no cover raise SearchException(_(f"Unrecognized contact field type '{prop_type}'"))
def evaluate(self, org, contact_json, prop_map): prop_type, field = prop_map[self.prop] if self.comparator.lower() in self.IS_SET_LOOKUPS: is_set = True elif self.comparator.lower() in self.IS_NOT_SET_LOOKUPS: is_set = False else: # pragma: no cover raise SearchException(_("Invalid operator for empty string comparison")) if prop_type == ContactQuery.PROP_FIELD: field_uuid = str(field.uuid) contact_fields = contact_json.get("fields") contact_field = contact_fields.get(field_uuid) # contact field does not exist if contact_field is None: if is_set: return False else: return True else: if field.value_type == Value.TYPE_TEXT: contact_value = contact_field.get("text") if is_set: if contact_value is not None: return True else: # pragma: can't cover return False else: if contact_value is not None: return False else: # pragma: can't cover return True elif field.value_type == Value.TYPE_NUMBER: try: contact_value = self._parse_number(contact_field.get("decimal", contact_field.get("number"))) except SearchException: contact_value = None if is_set: if contact_value is not None: return True else: return False else: if contact_value is not None: return False else: return True elif field.value_type == Value.TYPE_DATETIME: contact_value = str_to_date(contact_field.get("datetime"), field.org.get_dayfirst()) if is_set: if contact_value is not None: return True else: return False else: if contact_value is not None: return False else: return True elif field.value_type == Value.TYPE_WARD: contact_value = contact_field.get("ward") if is_set: if contact_value is not None: return True else: return False else: if contact_value is not None: return False else: return True elif field.value_type == Value.TYPE_DISTRICT: contact_value = contact_field.get("district") if is_set: if contact_value is not None: return True else: return False else: if contact_value is not None: return False else: return True elif field.value_type == Value.TYPE_STATE: contact_value = contact_field.get("state") if is_set: if contact_value is not None: return True else: return False else: if contact_value is not None: return False else: return True else: # pragma: no cover raise SearchException(_(f"Unrecognized contact field type '{field.value_type}'")) elif prop_type == ContactQuery.PROP_SCHEME: urn_exists = next((urn for urn in contact_json.get("urns") if urn.get("scheme") == field), None) if not urn_exists: if is_set: return False else: return True else: if is_set: return True else: return False elif prop_type == ContactQuery.PROP_ATTRIBUTE: field_key = field.key if field_key == "language": contact_value = contact_json.get("language") if is_set: if contact_value is not None: return True else: return False else: if contact_value is not None: return False else: return True elif field_key == "name": contact_value = contact_json.get("name") if is_set: if contact_value is not None: return True else: return False else: if contact_value is not None: return False else: return True else: # pragma: no cover raise SearchException(_(f"No support for attribute field: '{field}'")) else: # pragma: no cover raise SearchException(_(f"Unrecognized contact field type '{prop_type}'"))
def evaluate(self, org, contact_json, prop_map): prop_type, field = prop_map[self.prop] if prop_type == ContactQuery.PROP_FIELD: field_uuid = str(field.uuid) contact_fields = contact_json.get("fields", {}) if field.value_type == Value.TYPE_TEXT: query_value = self.value.upper() contact_value = contact_fields.get(field_uuid, {"text": ""}).get("text").upper() if self.comparator == "=": return contact_value == query_value elif self.comparator == "!=": return contact_value != query_value else: raise SearchException(_(f"Unknown text comparator: '{self.comparator}'")) elif field.value_type == Value.TYPE_NUMBER: query_value = self._parse_number(self.value) number_value = contact_fields.get(field_uuid, {"number": None}).get( "number", contact_fields.get(field_uuid, {"decimal": None}).get("decimal") ) if number_value is None: return False contact_value = self._parse_number(number_value) if self.comparator == "=": return contact_value == query_value elif self.comparator == ">": return contact_value > query_value elif self.comparator == ">=": return contact_value >= query_value elif self.comparator == "<": return contact_value < query_value elif self.comparator == "<=": return contact_value <= query_value else: raise SearchException(_(f"Unknown number comparator: '{self.comparator}'")) elif field.value_type == Value.TYPE_DATETIME: query_value = str_to_date(self.value, field.org.get_dayfirst()) if not query_value: raise SearchException(_(f"Unable to parse the date '{self.value}'")) lower_bound, upper_bound = date_to_day_range_utc(query_value, org) contact_datetime_value = contact_fields.get(field_uuid, {"datetime": None}).get("datetime") if contact_datetime_value is None: return False # datetime contact values are serialized as ISO8601 timestamps in local time contact_value = str_to_datetime(contact_datetime_value, pytz.UTC, field.org.get_dayfirst()) contact_value_utc = contact_value.astimezone(pytz.UTC) if self.comparator == "=": return contact_value_utc >= lower_bound and contact_value_utc < upper_bound elif self.comparator == ">": return contact_value_utc >= upper_bound elif self.comparator == ">=": return contact_value_utc >= lower_bound elif self.comparator == "<": return contact_value_utc < lower_bound elif self.comparator == "<=": return contact_value_utc < upper_bound else: raise SearchException(_(f"Unknown datetime comparator: '{self.comparator}'")) elif field.value_type in (Value.TYPE_STATE, Value.TYPE_DISTRICT, Value.TYPE_WARD): query_value = self.value.upper() if field.value_type == Value.TYPE_WARD: ward_value = contact_fields.get(field_uuid, {"ward": ""}).get("ward", "") contact_value = ward_value.upper().split(" > ")[-1] elif field.value_type == Value.TYPE_DISTRICT: district_value = contact_fields.get(field_uuid, {"district": ""}).get("district", "") contact_value = district_value.upper().split(" > ")[-1] elif field.value_type == Value.TYPE_STATE: state_value = contact_fields.get(field_uuid, {"state": ""}).get("state", "") contact_value = state_value.upper().split(" > ")[-1] else: # pragma: no cover raise SearchException(_(f"Unknown location type: '{field.value_type}'")) if self.comparator == "=": return contact_value == query_value elif self.comparator == "!=": return contact_value != query_value else: raise SearchException(_(f"Unsupported comparator '{self.comparator}' for location field")) else: # pragma: no cover raise SearchException(_(f"Unrecognized contact field type '{field.value_type}'")) elif prop_type == ContactQuery.PROP_SCHEME: for urn in contact_json.get("urns"): if urn.get("scheme") == field: contact_value = urn.get("path").upper() query_value = self.value.upper() if self.comparator == "=": if contact_value == query_value: return True elif self.comparator == "~": if query_value in contact_value: return True else: raise SearchException(_(f"Unknown urn scheme comparator: '{self.comparator}'")) return False elif prop_type == ContactQuery.PROP_ATTRIBUTE: field_key = field.key if field_key == "language": query_value = self.value.upper() raw_contact_value = contact_json.get("language") if raw_contact_value is None: contact_value = "" else: contact_value = raw_contact_value.upper() if self.comparator == "=": return contact_value == query_value elif self.comparator == "!=": return contact_value != query_value else: raise SearchException(_(f"Unknown language comparator: '{self.comparator}'")) elif field_key == "created_on": query_value = str_to_date(self.value, field.org.get_dayfirst()) if not query_value: raise SearchException(_(f"Unable to parse the date '{self.value}'")) lower_bound, upper_bound = date_to_day_range_utc(query_value, org) # contact created_on is serialized as ISO8601 timestamp in utc time contact_value = str_to_datetime(contact_json.get("created_on"), pytz.UTC, field.org.get_dayfirst()) contact_value_utc = contact_value.astimezone(pytz.UTC) if self.comparator == "=": return contact_value_utc >= lower_bound and contact_value_utc < upper_bound elif self.comparator == ">": return contact_value_utc >= upper_bound elif self.comparator == ">=": return contact_value_utc >= lower_bound elif self.comparator == "<": return contact_value_utc < lower_bound elif self.comparator == "<=": return contact_value_utc < upper_bound else: raise SearchException(_(f"Unknown created_on comparator: '{self.comparator}'")) elif field_key == "name": query_value = self.value.upper() raw_contact_value = contact_json.get("name") if raw_contact_value is None: contact_value = "" else: contact_value = raw_contact_value.upper() if self.comparator == "=": return contact_value == query_value elif self.comparator == "~": return query_value in contact_value elif self.comparator == "!=": return contact_value != query_value else: # pragma: no cover raise SearchException(_(f"Unknown name comparator: '{self.comparator}'")) else: raise SearchException(_(f"No support for attribute field: '{field}'")) else: # pragma: no cover raise SearchException(_(f"Unrecognized contact field type '{prop_type}'"))