diff --git a/crime_data/common/base.py b/crime_data/common/base.py index fbd9d56..37ecb59 100644 --- a/crime_data/common/base.py +++ b/crime_data/common/base.py @@ -93,63 +93,6 @@ def get_bind(self, mapper=None, clause=None): return super().get_bind(mapper=mapper, clause=clause) -class QueryTraits(object): - @classmethod - def get_fields(cls, agg_fields, fields): - """Builds the query's SELECT clause. - Returns list of fields. - """ - requested_fields = [] - for field in fields: - if field in cls.get_filter_map(): - requested_fields.append(cls.get_filter_map()[field]) - - requested_fields += agg_fields - return requested_fields - - @classmethod - def apply_group_by(cls, query, group_bys): - """ Builds the query's GROUP BY clause. - For Aggregations, the group by clause will - contain all output fields. Returns query object. - """ - for group in group_bys: - if group in cls.get_filter_map(): - query = (query.group_by(cls.get_filter_map()[group]) - .order_by(cls.get_filter_map()[group])) - return query - - @classmethod - def apply_filters(cls, query, filters, parsed): - """ Apply All query filters. - Returns query object. - """ - - def _is_string(col): - return issubclass(col.type.python_type, str) - - # Apply any inequality filters. - for (col_name, comparitor, values) in filters: - if col_name in cls.get_filter_map(): - col = cls.get_filter_map()[col_name] - if _is_string(col): - col = func.lower(col) - values = [val.lower() for val in values] - query = query.filter(or_(col.ilike('%' + val + '%') - for val in values)) - else: - operation = getattr(col, comparitor) - query = query.filter(or_(operation(val) for val in values)) - - # Apply all other filters. - for filter, value in parsed.items(): - if filter in cls.get_filter_map(): - col = cls.get_filter_map()[filter] - query = query.filter(col.ilike('%' + value + '%')) - - return query - - class Fields(object): @staticmethod def get_db_column_names(): diff --git a/crime_data/common/cdemodels.py b/crime_data/common/cdemodels.py index d06fb8a..f29eb94 100644 --- a/crime_data/common/cdemodels.py +++ b/crime_data/common/cdemodels.py @@ -11,7 +11,7 @@ from crime_data.common import models, newmodels from crime_data.common.models import RefState, RefCounty -from crime_data.common.base import QueryTraits, Fields, ExplorerOffenseMapping +from crime_data.common.base import Fields, ExplorerOffenseMapping from crime_data.extensions import db session = db.session @@ -340,180 +340,12 @@ class CdeNibrsLocationType(models.NibrsLocationType): pass -class CdeNibrsIncident(models.NibrsIncident, QueryTraits): - """ - Extends models.NibrsIncident. - """ - - over_count = True - - offender_ethnicity = aliased(CdeNibrsEthnicity, name='offender_ethnicity') - victim_ethnicity = aliased(CdeNibrsEthnicity, name='victim_ethnicity') - victim_race = aliased(CdeRefRace, name='victim_race') - victim_age = aliased(CdeNibrsAge, name='victim_age') - offender_race = aliased(CdeRefRace, name='offender_race') - offender_age = aliased(CdeNibrsAge, name='offender_age') - arrestee_race = aliased(CdeRefRace, name='arrestee_race') - arrestee_age = aliased(CdeNibrsAge, name='arrestee_age') - arrestee_ethnicity = aliased(CdeNibrsEthnicity, name='arrestee_ethnicity') - - # Maps API filter to DB column name. - @staticmethod - def get_filter_map(): - return { - 'state': CdeRefState.state_abbr.label('state'), - 'city': CdeRefCity.city_name.label('city'), - 'month': CdeNibrsMonth.month_num, - 'year': CdeNibrsMonth.data_year, - 'ori': CdeRefAgency.ori, - 'offense': CdeNibrsOffenseType.offense_name, - 'offense.location': CdeNibrsLocationType.location_name, - 'victim.ethnicity': - CdeNibrsIncident.victim_ethnicity.ethnicity_name.label( - 'victim.ethnicity'), - 'offender.ethnicity': - CdeNibrsIncident.offender_ethnicity.ethnicity_name.label( - 'offender.ethnicity'), - 'victim.race_code': - CdeNibrsIncident.victim_race.race_code.label('victim.race_code'), - 'victim.age_code': - CdeNibrsIncident.victim_age.age_code.label('victim.age_code'), - 'offender.race_code': - CdeNibrsIncident.offender_race.race_code.label( - 'offender.race_code'), - 'offender.age_code': - CdeNibrsIncident.offender_age.age_code.label('offender.age_code'), - 'arrestee.race_code': - CdeNibrsIncident.arrestee_race.race_code.label( - 'arestee.race_code'), - 'arrestee.age_code': - CdeNibrsIncident.arrestee_age.age_code.label('arestee.age_code'), - 'arrestee.ethnicity': - CdeNibrsIncident.arrestee_ethnicity.ethnicity_name.label( - 'arrestee.ethnicity'), - } - - @staticmethod - def get_nibrs_incident_by_ori(ori=None, filters=None, by=None, args=None): - ''''' - Returns Query for RETA counts by Agency/ORI - Monthly Sums. - ''' '' - - agg_fields = [ - func.count(CdeNibrsIncident.incident_id).label('incident_count'), - ] - - fields = CdeNibrsIncident.get_fields(agg_fields, by) - - # Always group by ORI - fields.append(CdeRefAgency.ori) - by.append('ori') - - # Base Query - query = CdeNibrsIncident.query - - # Apply JOINS. - query = ( - query.join(CdeNibrsOffense).join(CdeNibrsLocationType) - .outerjoin(CdeNibrsOffenseType) - .outerjoin(CdeNibrsMonth).outerjoin(CdeRefAgency) - .outerjoin(CdeRefCity).outerjoin(CdeRefState) - .outerjoin(CdeNibrsOffender).outerjoin(CdeNibrsWeapon) - .outerjoin(CdeNibrsWeaponType).outerjoin(models.NibrsAge) - .outerjoin( - CdeNibrsIncident.arrestee_age, - CdeNibrsAge.age_id == CdeNibrsIncident.arrestee_age.age_id) - .outerjoin( - CdeNibrsIncident.victim_age, - CdeNibrsAge.age_id == CdeNibrsIncident.victim_age.age_id) - .outerjoin( - CdeNibrsIncident.offender_age, - CdeNibrsAge.age_id == CdeNibrsIncident.offender_age.age_id) - .outerjoin(models.RefRace).outerjoin( - CdeNibrsIncident.arrestee_race, - CdeRefRace.race_id == CdeNibrsIncident.arrestee_race.race_id) - .outerjoin( - CdeNibrsIncident.victim_race, - CdeRefRace.race_id == CdeNibrsIncident.victim_race.race_id) - .outerjoin( - CdeNibrsIncident.offender_race, - CdeRefRace.race_id == CdeNibrsIncident.offender_race.race_id) - .outerjoin(CdeNibrsEthnicity).outerjoin( - CdeNibrsIncident.victim_ethnicity, - CdeNibrsOffender.ethnicity_id == - CdeNibrsIncident.victim_ethnicity.ethnicity_id).outerjoin( - CdeNibrsIncident.offender_ethnicity, - CdeNibrsOffender.ethnicity_id == - CdeNibrsIncident.offender_ethnicity.ethnicity_id)) - - # Apply field selections. - query = query.with_entities(*fields) - - # Apply group by. - query = CdeNibrsIncident.apply_group_by(query, by) - - # Apply all filters - query = CdeNibrsIncident.apply_filters(query, filters, args) - - return query - - -class CdeRetaMonth(models.RetaMonth, QueryTraits): - - # Maps API filter to DB column name. - @staticmethod - def get_filter_map(): - return { - 'state': CdeRefState.state_abbr.label('state'), - 'offense': CdeRetaOffense.offense_name, - 'ori': CdeRefAgency.ori, - 'subcategory': CdeRetaOffenseSubcat.offense_subcat_name, - 'agency_name': - CdeRefAgency.pub_agency_name, # Assuming Public Agency Name is the best one. - 'city': CdeRefCity.city_name.label('city'), - 'year': CdeRetaMonth.data_year, - 'month': CdeRetaMonth.month_num - } - - @staticmethod - def get_reta_by_ori(ori=None, filters=None, by=None, args=None): - ''''' - Returns Query for RETA counts by Agency/ORI - Monthly Sums. - ''' '' - - agg_fields = [ - func.sum(CdeRetaMonthOffenseSubcat.actual_count).label( - 'actual_count'), - func.sum(CdeRetaMonthOffenseSubcat.reported_count).label( - 'reported_count'), - func.sum(CdeRetaMonthOffenseSubcat.unfounded_count).label( - 'unfounded_count'), - func.sum(CdeRetaMonthOffenseSubcat.cleared_count).label( - 'cleared_count'), - func.sum(CdeRetaMonthOffenseSubcat.juvenile_cleared_count).label( - 'juvenile_cleared_count'), - ] - - fields = CdeRetaMonth.get_fields(agg_fields, by) - - # Base Query - query = CdeRetaMonth.query - - # Apply JOINS. - query = (query.join(CdeRetaMonthOffenseSubcat).outerjoin(CdeRefAgency) - .outerjoin(CdeRefCity).outerjoin(CdeRefState) - .join(CdeRetaOffenseSubcat).join(CdeRetaOffense)) - - # Apply field selections. - query = query.with_entities(*fields) - - # Apply group by. - query = CdeRetaMonth.apply_group_by(query, by) +class CdeNibrsIncident(models.NibrsIncident): + pass - # Apply all filters - query = CdeRetaMonth.apply_filters(query, filters, args) - return query +class CdeRetaMonth(models.RetaMonth): + pass class CdeCrimeType(models.CrimeType):