from psycopg2 import sql from .filter import Filter class Location(Filter): def validate(self): if self.values: # lon, lat, distance, text_label if len(self.values) != 4: super().validate() # all values should be convertible to float try: for v in self.values[1:]: float(v) except ValueError: super().validate() # radius must not be negative - always returns 0 profiles if float(self.values[3]) < 0: super().validate() self._validated = True return True def prepare_options(self): return self def get_options(self): options = [ { "__typename": "LocationFilter", "id": "location", "minDistance": self.get_param("min_distance"), "maxDistance": self.get_param("max_distance"), "radiusPlaceholder": self.get_param("radius_placeholder"), "searchPlaceholder": self.get_param("search_placeholder"), "searchInputDelay": self.get_param("search_input_delay"), "searchLayers": self.get_param("search_layers"), "searchSize": self.get_param("search_size"), } ] return options def extra_sql_fragments(self, schema, set_collection_ids_for_values): return { "longitudes": f"SELECT fa.collection_id, fa.row_id, fa.value lon " f"FROM {schema}.fan_attribute fa " f"WHERE fa.collection_id IN ({set_collection_ids_for_values}) " f"AND fa.attribute_id = 58 " f"AND fa.value != 'unknown'", "latitudes": f"SELECT fa.fan_id, fa.collection_id, fa.row_id, fa.value lat " f"FROM {schema}.fan_attribute fa " f"WHERE fa.collection_id IN ({set_collection_ids_for_values}) " f"AND fa.attribute_id = 59 " f"AND fa.value != 'unknown'", } def fragment_sql(self, schema, parent_collection_join, context): sql_condition = self.sql_condition(context) return ( f"SELECT DISTINCT latitudes.fan_id FROM latitudes " f"JOIN longitudes ON latitudes.collection_id = longitudes.collection_id " f"AND latitudes.row_id = longitudes.row_id " f"WHERE {sql_condition}" ) def sql_condition(self, context): """Call validate first!""" if self._validated: lon, lat, radius = self.values[1:] if lat is not None and lon is not None and radius is not None: return ( f" earth_box(ll_to_earth({sql.Literal(lat).as_string(context)}, {sql.Literal(lon).as_string(context)}), {sql.Literal(radius).as_string(context)} * 1000) @>" f" ll_to_earth(latitudes.lat::float, longitudes.lon::float) " ) else: return " True " else: RuntimeError("Call .validate() first!")