def test_compute_allegation_metric(self): officer = OfficerFactory(id=1, appointed_date=date(2010, 3, 14)) OfficerAllegationFactory( officer=officer, allegation__incident_date=datetime(2014, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory.create_batch( 3, officer=officer, allegation__incident_date=datetime(2016, 1, 16, tzinfo=pytz.utc), ) OfficerAllegationFactory( officer=officer, allegation__incident_date=datetime(2016, 7, 2, tzinfo=pytz.utc), ) officers = officer_percentile._compute_metric( 2016, PERCENTILE_ALLEGATION_GROUP) expect(officers).to.have.length(1) validate_object( officers[0], { 'officer_id': 1, 'year': 2016, 'start_date': date(2015, 1, 1), 'end_date': date(2016, 7, 1), 'service_year': 1.4986, 'num_allegation': 3, 'metric_allegation': 2.0019, })
def test_extract_datum_most_common_category(self): allegation = AllegationFactory() cat1 = AllegationCategoryFactory( category='Use Of Forces', allegation_name='Sub Force' ) cat2 = AllegationCategoryFactory( category='Traffic', allegation_name='Sub traffic' ) OfficerAllegationFactory.create_batch( 2, allegation=allegation, allegation_category=cat1 ) OfficerAllegationFactory( allegation=allegation, allegation_category=cat2 ) rows = self.extract_data() expect(rows).to.have.length(1) expect(rows[0]['most_common_category']).to.eq({ 'category': 'Use Of Forces', 'allegation_name': 'Sub Force' })
def test_coaccused_count(self): allegation_1 = AllegationFactory() allegation_2 = AllegationFactory() OfficerAllegationFactory.create_batch(6, allegation=allegation_1) allegation_cache_manager.cache_data() allegation_1.refresh_from_db() allegation_2.refresh_from_db() expect(allegation_1.coaccused_count).to.eq(6) expect(allegation_2.coaccused_count).to.eq(0)
def test_coaccused_counts(self): officer = OfficerFactory() OfficerAllegationFactory.create_batch(2, officer=officer, final_finding='SU') OfficerAllegationFactory(officer=officer, final_finding='NS') rows = self.extract_data() expect(rows).to.have.length(3) for row in rows: expect(row['coaccused']).to.have.length(1) expect(row['coaccused'][0]['allegation_count']).to.eq(3) expect(row['coaccused'][0]['sustained_count']).to.eq(2)
def test_new_timeline_item_no_join(self): officer = OfficerFactory(id=123, appointed_date=None, rank='Police Officer') unit = PoliceUnitFactory(unit_name='001', description='unit_001') OfficerHistoryFactory(officer=officer, unit=unit, effective_date=date(2010, 1, 1), end_date=date(2011, 12, 31)) allegation = AllegationFactory( crid='123456', coaccused_count=4, incident_date=datetime(2011, 8, 23, tzinfo=pytz.utc) ) OfficerAllegationFactory( final_finding='UN', final_outcome='Unknown', officer=officer, allegation=allegation, allegation_category=AllegationCategoryFactory(category='category', allegation_name='sub category') ) OfficerAllegationFactory.create_batch(3, allegation=allegation) SalaryFactory(officer=officer, year=2001, rank='Police Officer', spp_date=date(2001, 9, 23)) cache_managers.cache_all() response = self.client.get(reverse('api-v2:officers-new-timeline-items', kwargs={'pk': 123})) expect(response.status_code).to.eq(status.HTTP_200_OK) expect(response.data).to.eq([ { 'date': '2011-08-23', 'kind': 'CR', 'crid': '123456', 'category': 'category', 'subcategory': 'sub category', 'finding': 'Unfounded', 'outcome': 'Unknown', 'coaccused': 4, 'unit_name': '001', 'unit_description': 'unit_001', 'rank': 'Police Officer', 'victims': [], 'attachments': [], }, { 'date': '2010-01-01', 'kind': 'UNIT_CHANGE', 'unit_name': '001', 'unit_description': 'unit_001', 'rank': 'Police Officer', }, { 'date': '2001-09-23', 'kind': 'RANK_CHANGE', 'unit_name': '', 'unit_description': '', 'rank': 'Police Officer', } ])
def test_allegation_count(self): officer_1 = OfficerFactory() officer_2 = OfficerFactory() OfficerAllegationFactory.create_batch(2, final_finding='NS', officer=officer_1) OfficerAllegationFactory(final_finding='SU', officer=officer_1) officer_cache_manager.build_cached_columns() officer_1.refresh_from_db() officer_2.refresh_from_db() expect(officer_1.allegation_count).to.eq(3) expect(officer_2.allegation_count).to.eq(0)
def test_discipline_count(self): officer_1 = OfficerFactory() officer_2 = OfficerFactory() OfficerAllegationFactory.create_batch(2, officer=officer_1, disciplined=True) OfficerAllegationFactory(officer=officer_1, disciplined=False) OfficerAllegationFactory(officer=officer_2, disciplined=False) officer_cache_manager.build_cached_columns() officer_1.refresh_from_db() officer_2.refresh_from_db() expect(officer_1.discipline_count).to.eq(2) expect(officer_2.discipline_count).to.eq(0)
def test_police_witness_counts(self): officer = OfficerFactory() OfficerAllegationFactory.create_batch(2, officer=officer, final_finding='SU') OfficerAllegationFactory(officer=officer, final_finding='NS') allegation = AllegationFactory() PoliceWitnessFactory(officer=officer, allegation=allegation) rows = self.extract_data() expect(rows).to.have.length(4) expect({len(row['involvements']) for row in rows}).to.eq({0, 1}) for row in rows: if len(row['involvements']) == 1: expect(row['involvements'][0]['allegation_count']).to.eq(3) expect(row['involvements'][0]['sustained_count']).to.eq(2)
def test_most_common_category(self): allegation = AllegationFactory() category1, category2 = AllegationCategoryFactory.create_batch(2) OfficerAllegationFactory(allegation=allegation, allegation_category=category2) OfficerAllegationFactory.create_batch(2, allegation=allegation, allegation_category=category1) OfficerAllegationFactory.create_batch(3, allegation=allegation, allegation_category=None) allegation_cache_manager.cache_data() allegation.refresh_from_db() expect(allegation.most_common_category).to.eq(category1)
def test_get_coaccused(self, coaccused_serializer_mock): allegation = AllegationFactory() officer_allegations = OfficerAllegationFactory.create_batch(2, allegation=allegation) result = CRSerializer(allegation).data officer_allegations_arg = coaccused_serializer_mock.call_args[0][0] expect(set(officer_allegations_arg)).to.eq(set(officer_allegations)) expect(result['coaccused']).to.eq([{'id': 1}, {'id': 2}])
def test_count(self): officer = OfficerFactory() OfficerAllegationFactory.create_batch(2, officer=officer, final_finding='NS') OfficerAllegationFactory.create_batch(2, officer=officer, final_finding='SU') subquery = OfficerAllegation.objects.filter( officer=models.OuterRef('officer_id')) results = list(OfficerAllegation.objects.all().annotate( allegation_count=SQCount(subquery.values('id'))).annotate( sustained_count=SQCount( subquery.filter(final_finding='SU').values('id'))).values( 'allegation_count', 'sustained_count')) expect(results).to.have.length(4) for obj in results: expect(obj['allegation_count']).to.eq(4) expect(obj['sustained_count']).to.eq(2)
def setUp(self): violation_category = AllegationCategoryFactory(category='Operation/Personnel Violations') use_of_force_category = AllegationCategoryFactory(category='Use Of Force') illegal_search_category = AllegationCategoryFactory(category='Illegal Search') false_arrest_category = AllegationCategoryFactory(category='False Arrest') before_min_date = datetime(1987, 12, 31, tzinfo=pytz.utc) valid_date = datetime(1988, 12, 31, tzinfo=pytz.utc) today = datetime.now(pytz.utc) for date in [before_min_date, valid_date, today]: OfficerAllegationFactory.create_batch( 4, allegation_category=violation_category, allegation__incident_date=date, disciplined=True) OfficerAllegationFactory.create_batch( 3, allegation_category=use_of_force_category, allegation__incident_date=date, disciplined=True) OfficerAllegationFactory.create_batch( 2, allegation_category=illegal_search_category, allegation__incident_date=date, disciplined=False) OfficerAllegationFactory( allegation_category=false_arrest_category, allegation__incident_date=date, final_outcome='900')
def test_extract_datum_percentiles(self): officer1 = OfficerFactory(id=1, appointed_date=date(2013, 1, 1)) officer2 = OfficerFactory(id=2, appointed_date=date(2015, 3, 14)) OfficerFactory(id=3, appointed_date=date(2014, 3, 1), resignation_date=date(2015, 4, 14)) OfficerAllegationFactory(officer=officer1, allegation__incident_date=datetime( 2015, 1, 1, tzinfo=pytz.utc), start_date=date(2015, 1, 1), allegation__is_officer_complaint=False) OfficerAllegationFactory(officer=officer1, start_date=date(2015, 1, 1), allegation__incident_date=datetime( 2015, 1, 1, tzinfo=pytz.utc), allegation__is_officer_complaint=False) OfficerAllegationFactory(officer=officer1, start_date=date(2016, 1, 22), allegation__incident_date=datetime( 2016, 1, 1, tzinfo=pytz.utc), allegation__is_officer_complaint=False) OfficerAllegationFactory.create_batch( 2, officer=officer2, start_date=date(2017, 10, 19), allegation__incident_date=datetime(2016, 1, 16, tzinfo=pytz.utc), allegation__is_officer_complaint=False) OfficerAllegationFactory(officer=officer2, start_date=date(2017, 10, 19), allegation__incident_date=datetime( 2016, 3, 15, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory(officer=officer2, start_date=date(2017, 10, 19), allegation__incident_date=datetime( 2017, 3, 15, tzinfo=pytz.utc), allegation__is_officer_complaint=True) TRRFactory( officer=officer1, trr_datetime=datetime(2017, 3, 15, tzinfo=pytz.utc), ) TRRFactory( officer=officer1, trr_datetime=datetime(2016, 3, 15, tzinfo=pytz.utc), ) rows = sorted(self.extract_data(), key=itemgetter('id')) expect(rows).to.have.length(3) expect(rows[0]['current_allegation_percentile']).to.eq('33.3333') expect(rows[0]['percentiles']).to.eq([{ 'id': 1, 'year': 2014, 'percentile_allegation': '0.0000', 'percentile_allegation_civilian': '0.0000', 'percentile_allegation_internal': '0.0000', 'percentile_trr': '0.0000' }, { 'id': 1, 'year': 2015, 'percentile_allegation': '50.0000', 'percentile_allegation_civilian': '50.0000', 'percentile_allegation_internal': '0.0000', 'percentile_trr': '0.0000' }, { 'id': 1, 'year': 2016, 'percentile_allegation': '33.3333', 'percentile_allegation_civilian': '33.3333', 'percentile_allegation_internal': '0.0000', 'percentile_trr': '66.6667' }]) expect(rows[1]['current_allegation_percentile']).to.eq('66.6667') expect(rows[1]['percentiles']).to.eq([{ 'id': 2, 'year': 2016, 'percentile_allegation': '66.6667', 'percentile_allegation_civilian': '66.6667', 'percentile_allegation_internal': '66.6667', 'percentile_trr': '0.0000' }]) expect(rows[2]['current_allegation_percentile']).to.eq('0.0000') expect(rows[2]['percentiles']).to.eq([{ 'id': 3, 'year': 2015, 'percentile_allegation': '0.0000', 'percentile_allegation_civilian': '0.0000', 'percentile_allegation_internal': '0.0000', 'percentile_trr': '0.0000' }])
def test_extract_datum(self): allegation = AllegationFactory( crid='123456', incident_date=datetime(2017, 7, 27, tzinfo=pytz.utc), summary='abc', add1='3000', add2='Michigan Ave', city='Chicago IL' ) officer = OfficerFactory( id=10, first_name='Luke', last_name='Skywalker', allegation_count=4, complaint_percentile='99.70', trr_percentile='99.88', civilian_allegation_percentile='77.66', internal_allegation_percentile='66.55' ) officer2 = OfficerFactory( id=11, first_name='John', last_name='Doe', allegation_count=2, complaint_percentile='70.33', trr_percentile='66.88', civilian_allegation_percentile='33.66', internal_allegation_percentile='22.55' ) OfficerAllegationFactory(allegation=allegation, officer=officer) category1 = AllegationCategoryFactory( category='Operation/Personnel Violations', allegation_name='Secondary/Special Employment' ) category2 = AllegationCategoryFactory(category='Use of Force', allegation_name='sub category') OfficerAllegationFactory(allegation=allegation, allegation_category=category2, officer=officer2) OfficerAllegationFactory.create_batch(2, allegation=allegation, allegation_category=category1, officer=None) OfficerAllegationFactory.create_batch(3, allegation=allegation, allegation_category=None, officer=None) VictimFactory(allegation=allegation, gender='F', race='Black', age=25) VictimFactory(allegation=allegation, gender='', race='Black', age=25) VictimFactory(allegation=allegation, gender='F', race='Black', age=None) AttachmentFileFactory(id=1, allegation=allegation, text_content='') AttachmentFileFactory( id=2, allegation=allegation, show=False, text_content="CHICAGO POLICE DEPARTMENT RD I HT334604" ) AttachmentFileFactory(id=3, allegation=allegation, text_content='CHICAGO POLICE DEPARTMENT RD I HT334604') setattr(allegation, 'investigator_names', ['Jerome Finnigan']) allegation_cache_manager.cache_data() allegation.refresh_from_db() datum = CrIndexer().extract_datum(allegation) datum['victims'] = sorted( datum['victims'], key=lambda victim: (victim['gender'], victim['race'], victim.get('age', 0)) ) expect(datum).to.eq({ 'crid': '123456', 'category': 'Operation/Personnel Violations', 'sub_category': 'Secondary/Special Employment', 'incident_date': '2017-07-27', 'address': '3000 Michigan Ave, Chicago IL', 'summary': 'abc', 'to': '/complaint/123456/', 'investigator_names': ['Jerome Finnigan'], 'victims': [ {'gender': '', 'race': 'Black', 'age': 25}, {'gender': 'Female', 'race': 'Black'}, {'gender': 'Female', 'race': 'Black', 'age': 25}, ], 'coaccused': [ { 'id': 10, 'full_name': 'Luke Skywalker', 'allegation_count': 4, 'percentile_allegation': '99.7000', 'percentile_allegation_civilian': '77.6600', 'percentile_allegation_internal': '66.5500', 'percentile_trr': '99.8800', }, { 'id': 11, 'full_name': 'John Doe', 'allegation_count': 2, 'percentile_allegation': '70.3300', 'percentile_allegation_civilian': '33.6600', 'percentile_allegation_internal': '22.5500', 'percentile_trr': '66.8800', } ], 'attachment_files': [ {'id': 3, 'text_content': 'CHICAGO POLICE DEPARTMENT RD I HT334604'} ] })
def test_latest_year_percentile(self): officer1 = OfficerFactory(id=1, appointed_date=date(1990, 3, 14)) officer2 = OfficerFactory(id=2, appointed_date=date(1990, 3, 14), resignation_date=date(2014, 7, 1)) officer3 = OfficerFactory(id=3, appointed_date=date(1990, 3, 14)) officer4 = OfficerFactory(id=4, appointed_date=date(1990, 3, 14), resignation_date=date(2010, 7, 1)) # officer1 have all data OfficerAllegationFactory.create_batch( 2, officer=officer1, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory.create_batch( 3, officer=officer1, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory.create_batch( 4, officer=officer1, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) TRRFactory.create_batch(2, officer=officer1, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) AwardFactory(officer=officer1, award_type='Complimentary Letter', start_date=datetime(2013, 1, 1)) AwardFactory.create_batch(3, officer=officer1, award_type='Honorable Mention', start_date=datetime(2015, 1, 1)) # officer2 don't have trr OfficerAllegationFactory( officer=officer2, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory(officer=officer2, allegation__incident_date=datetime( 2014, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory(officer=officer2, allegation__incident_date=datetime( 2014, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) AwardFactory(officer=officer2, award_type='Complimentary Letter', start_date=datetime(2013, 1, 1)) AwardFactory.create_batch(3, officer=officer2, award_type='Honorable Mention', start_date=datetime(2014, 1, 1)) # officer3 don't have allegation in ALLEGATION_MIN - ALLEGATION_MAX OfficerAllegationFactory.create_batch( 2, officer=officer3, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory.create_batch( 3, officer=officer3, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) TRRFactory.create_batch(3, officer=officer3, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) # officer4 have all data but is out of data range OfficerAllegationFactory.create_batch( 2, officer=officer4, allegation__incident_date=datetime(2009, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory.create_batch( 3, officer=officer4, allegation__incident_date=datetime(2009, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory.create_batch( 4, officer=officer4, allegation__incident_date=datetime(2009, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) TRRFactory.create_batch(2, officer=officer4, trr_datetime=datetime(2009, 2, 1, tzinfo=pytz.utc)) expected_dict = { 1: { 'officer_id': 1, 'year': 2017, 'metric_allegation': 3.0, 'metric_allegation_civilian': 2.0, 'metric_allegation_internal': 1.0, 'metric_trr': 0.6667, 'metric_honorable_mention': 1.0, 'percentile_allegation': 66.6667, 'percentile_allegation_civilian': 66.6667, 'percentile_allegation_internal': 66.6667, 'percentile_trr': 33.3333, 'percentile_honorable_mention': 33.3333, }, 2: { 'officer_id': 2, 'year': 2014, 'metric_allegation': 2.0055, 'metric_allegation_civilian': 1.337, 'metric_allegation_internal': 0.6685, 'metric_trr': 0.0, 'metric_honorable_mention': 2.0055, 'percentile_allegation': 66.6667, 'percentile_allegation_civilian': 66.6667, 'percentile_allegation_internal': 66.6667, 'percentile_trr': 0.0, 'percentile_honorable_mention': 66.6667 }, 3: { 'officer_id': 3, 'year': 2017, 'metric_allegation': 1.6667, 'metric_allegation_civilian': 1.0, 'metric_allegation_internal': 0.6667, 'metric_trr': 1.0, 'metric_honorable_mention': 0.0, 'percentile_allegation': 0.0, 'percentile_allegation_civilian': 0.0, 'percentile_allegation_internal': 0.0, 'percentile_trr': 66.6667, 'percentile_honorable_mention': 0.0 } } officers = officer_percentile.latest_year_percentile() expect(officers).to.have.length(3) for officer in officers: validate_object(officer, expected_dict[officer.id])
def test_top_percentile_not_enough_service_year(self): officer1 = OfficerFactory(id=1, appointed_date=date(1990, 3, 14)) officer2 = OfficerFactory(id=2, appointed_date=date(1990, 3, 14), resignation_date=date(2014, 5, 1)) officer3 = OfficerFactory(id=3, appointed_date=date(2013, 3, 14)) officer4 = OfficerFactory(id=4, appointed_date=date(1990, 3, 14), resignation_date=date(2015, 5, 1)) # officer1 have all data OfficerAllegationFactory.create_batch( 2, officer=officer1, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory.create_batch( 3, officer=officer1, allegation__incident_date=datetime(2014, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory.create_batch( 4, officer=officer1, allegation__incident_date=datetime(2014, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) TRRFactory.create_batch(2, officer=officer1, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) # officer2 don't have trr OfficerAllegationFactory( officer=officer2, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory(officer=officer2, allegation__incident_date=datetime( 2014, 2, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory(officer=officer2, allegation__incident_date=datetime( 2014, 3, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) # officer3 don't have allegation in ALLEGATION_MIN - ALLEGATION_MAX OfficerAllegationFactory.create_batch( 2, officer=officer3, allegation__incident_date=datetime(2014, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory.create_batch( 3, officer=officer3, allegation__incident_date=datetime(2014, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) TRRFactory.create_batch(3, officer=officer3, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) # officer4 don't have allegation in INTERNAL_CIVILIAN_ALLEGATION year range OfficerAllegationFactory.create_batch( 5, officer=officer4, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) TRRFactory.create_batch(6, officer=officer4, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) expected_dict = { 1: { 'officer_id': 1, 'year': 2016, 'metric_allegation': 2, 'metric_allegation_civilian': 4, 'metric_allegation_internal': 3, 'metric_trr': 2, 'percentile_allegation': 33.3333, 'percentile_allegation_civilian': 66.6667, 'percentile_allegation_internal': 66.6667, 'percentile_trr': 0.0 }, 2: { 'officer_id': 2, 'year': 2016, 'metric_allegation': 1, 'metric_allegation_civilian': None, 'metric_allegation_internal': None, 'metric_trr': None, 'percentile_allegation': 0.0, 'percentile_allegation_civilian': None, 'percentile_allegation_internal': None, 'percentile_trr': None }, 3: { 'officer_id': 3, 'year': 2016, 'metric_allegation': None, 'metric_allegation_civilian': 3, 'metric_allegation_internal': 2, 'metric_trr': 3, 'percentile_allegation': None, 'percentile_allegation_civilian': 33.3333, 'percentile_allegation_internal': 33.3333, 'percentile_trr': 50.0 }, 4: { 'officer_id': 4, 'year': 2016, 'metric_allegation': 5, 'metric_allegation_civilian': 0.0, 'metric_allegation_internal': 0.0, 'metric_trr': None, 'percentile_allegation': 66.6667, 'percentile_allegation_civilian': 0.0, 'percentile_allegation_internal': 0.0, 'percentile_trr': None } } officers = officer_percentile.top_percentile(2016) for officer in officers: validate_object(officer, expected_dict[officer.id])
def test_top_percentile_with_types(self): officer1 = OfficerFactory(id=1, appointed_date=date(1990, 3, 14)) officer2 = OfficerFactory(id=2, appointed_date=date(1990, 3, 14)) officer3 = OfficerFactory(id=3, appointed_date=date(1990, 3, 14)) officer4 = OfficerFactory(id=4, appointed_date=date(1990, 3, 14)) # officer1 have all data OfficerAllegationFactory.create_batch( 2, officer=officer1, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory.create_batch( 3, officer=officer1, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory.create_batch( 4, officer=officer1, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) TRRFactory.create_batch(2, officer=officer1, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) # officer2 don't have trr OfficerAllegationFactory( officer=officer2, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory(officer=officer2, allegation__incident_date=datetime( 2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory(officer=officer2, allegation__incident_date=datetime( 2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) # officer3 don't have allegation in ALLEGATION_MIN - ALLEGATION_MAX OfficerAllegationFactory.create_batch( 2, officer=officer3, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory.create_batch( 3, officer=officer3, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) TRRFactory.create_batch(3, officer=officer3, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) # officer4 don't have allegation in INTERNAL_CIVILIAN_ALLEGATION year range OfficerAllegationFactory.create_batch( 5, officer=officer4, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) TRRFactory.create_batch(6, officer=officer4, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) expected_dict = { 1: { 'officer_id': 1, 'year': 2016, 'metric_allegation_civilian': 4, 'metric_allegation_internal': 3, 'metric_trr': 2, 'percentile_allegation': None, 'percentile_allegation_civilian': 75.0, 'percentile_allegation_internal': 75.0, 'percentile_trr': 25.0 }, 2: { 'officer_id': 2, 'year': 2016, 'metric_allegation_civilian': 1, 'metric_allegation_internal': 1, 'metric_trr': 0.0, 'percentile_allegation': None, 'percentile_allegation_civilian': 25.0, 'percentile_allegation_internal': 25.0, 'percentile_trr': 0.0 }, 3: { 'officer_id': 3, 'year': 2016, 'metric_allegation_civilian': 3, 'metric_allegation_internal': 2, 'metric_trr': 3, 'percentile_allegation': None, 'percentile_allegation_civilian': 50.0, 'percentile_allegation_internal': 50.0, 'percentile_trr': 50.0 }, 4: { 'officer_id': 4, 'year': 2016, 'metric_allegation_civilian': 0.0, 'metric_allegation_internal': 0.0, 'metric_trr': 6, 'percentile_allegation': None, 'percentile_allegation_civilian': 0.0, 'percentile_allegation_internal': 0.0, 'percentile_trr': 75.0 } } visual_token_percentile_groups = [ PERCENTILE_ALLEGATION_INTERNAL_CIVILIAN_GROUP, PERCENTILE_TRR_GROUP ] officers = officer_percentile.top_percentile( 2016, percentile_groups=visual_token_percentile_groups) for officer in officers: validate_object(officer, expected_dict[officer.id])
def test_top_percentile(self): officer1 = OfficerFactory(id=1, appointed_date=date(1990, 3, 14)) officer2 = OfficerFactory(id=2, appointed_date=date(1990, 3, 14)) officer3 = OfficerFactory(id=3, appointed_date=date(1990, 3, 14)) # officer1 have all data OfficerAllegationFactory.create_batch( 2, officer=officer1, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory.create_batch( 3, officer=officer1, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory.create_batch( 4, officer=officer1, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) TRRFactory.create_batch(2, officer=officer1, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) # officer2 don't have trr OfficerAllegationFactory( officer=officer2, allegation__incident_date=datetime(2013, 12, 31, tzinfo=pytz.utc), ) OfficerAllegationFactory(officer=officer2, allegation__incident_date=datetime( 2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory(officer=officer2, allegation__incident_date=datetime( 2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) # officer3 don't have allegation in ALLEGATION_MIN - ALLEGATION_MAX OfficerAllegationFactory.create_batch( 2, officer=officer3, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=True) OfficerAllegationFactory.create_batch( 3, officer=officer3, allegation__incident_date=datetime(2015, 7, 2, tzinfo=pytz.utc), allegation__is_officer_complaint=False) TRRFactory.create_batch(3, officer=officer3, trr_datetime=datetime(2015, 2, 1, tzinfo=pytz.utc)) expected_dict = { 1: { 'officer_id': 1, 'year': 2016, 'metric_allegation': 2, 'metric_allegation_civilian': 4, 'metric_allegation_internal': 3, 'metric_trr': 2, 'percentile_allegation': 66.6667, 'percentile_allegation_civilian': 66.6667, 'percentile_allegation_internal': 66.6667, 'percentile_trr': 33.3333 }, 2: { 'officer_id': 2, 'year': 2016, 'metric_allegation': 1, 'metric_allegation_civilian': 1, 'metric_allegation_internal': 1, 'metric_trr': 0.0, 'percentile_allegation': 33.3333, 'percentile_allegation_civilian': 0.0000, 'percentile_allegation_internal': 0.0000, 'percentile_trr': 0.0 }, 3: { 'officer_id': 3, 'year': 2016, 'metric_allegation': 0.0, 'metric_allegation_civilian': 3, 'metric_allegation_internal': 2, 'metric_trr': 3, 'percentile_allegation': 0.0, 'percentile_allegation_civilian': 33.3333, 'percentile_allegation_internal': 33.3333, 'percentile_trr': 66.6667, } } officers = officer_percentile.top_percentile(2016) for officer in officers: validate_object(officer, expected_dict[officer.id])
def test_build_cached_yearly_percentiles(self): officer_1 = OfficerFactory(id=1, appointed_date=date(2013, 1, 1)) officer_2 = OfficerFactory(id=2, appointed_date=date(2015, 3, 14)) officer_3 = OfficerFactory(id=3, appointed_date=date(2014, 3, 1), resignation_date=date(2015, 4, 14)) OfficerAllegationFactory( officer=officer_1, allegation__incident_date=datetime(2015, 1, 1, tzinfo=pytz.utc), start_date=datetime(2015, 1, 1), allegation__is_officer_complaint=False) OfficerAllegationFactory( officer=officer_1, start_date=date(2015, 1, 1), allegation__incident_date=datetime(2015, 1, 1, tzinfo=pytz.utc), allegation__is_officer_complaint=False) OfficerAllegationFactory( officer=officer_1, start_date=date(2016, 1, 22), allegation__incident_date=datetime(2016, 1, 1, tzinfo=pytz.utc), allegation__is_officer_complaint=False) OfficerAllegationFactory.create_batch( 2, officer=officer_2, start_date=date(2017, 10, 19), allegation__incident_date=datetime(2016, 1, 16, tzinfo=pytz.utc), allegation__is_officer_complaint=False ) OfficerAllegationFactory( officer=officer_2, start_date=date(2017, 10, 19), allegation__incident_date=datetime(2016, 3, 15, tzinfo=pytz.utc), allegation__is_officer_complaint=True ) OfficerAllegationFactory( officer=officer_2, start_date=date(2017, 10, 19), allegation__incident_date=datetime(2017, 3, 15, tzinfo=pytz.utc), allegation__is_officer_complaint=True ) TRRFactory( officer=officer_1, trr_datetime=datetime(2017, 3, 15, tzinfo=pytz.utc), ) TRRFactory( officer=officer_1, trr_datetime=datetime(2016, 3, 15, tzinfo=pytz.utc), ) officer_cache_manager.build_cached_yearly_percentiles() expected_officer_yearly_percentiles = { officer_1.id: { 2014: { 'percentile_allegation': Decimal(0.0), 'percentile_allegation_civilian': Decimal(0.0), 'percentile_allegation_internal': Decimal(0.0), 'percentile_trr': Decimal(0.0), }, 2015: { 'percentile_allegation': Decimal(50.0), 'percentile_allegation_civilian': Decimal(50.0), 'percentile_allegation_internal': Decimal(0.0), 'percentile_trr': Decimal(0.0), }, 2016: { 'percentile_allegation': Decimal(33.3333), 'percentile_allegation_civilian': Decimal(33.3333), 'percentile_allegation_internal': Decimal(0.0), 'percentile_trr': Decimal(66.6667), } }, officer_2.id: { 2016: { 'percentile_allegation': Decimal(66.6667), 'percentile_allegation_civilian': Decimal(66.6667), 'percentile_allegation_internal': Decimal(66.6667), 'percentile_trr': Decimal(0.0), } }, officer_3.id: { 2015: { 'percentile_allegation': Decimal(0.0), 'percentile_allegation_civilian': Decimal(0.0), 'percentile_allegation_internal': Decimal(0.0), 'percentile_trr': Decimal(0.0), } } } for officer_id, expected_yearly_percentiles in expected_officer_yearly_percentiles.items(): yearly_percentiles = OfficerYearlyPercentile.objects.filter(officer_id=officer_id) expect(yearly_percentiles.count()).to.eq(len(expected_yearly_percentiles.keys())) for year, expected_percentile in expected_yearly_percentiles.items(): percentile = yearly_percentiles.get(year=year) for attr, value in expected_percentile.items(): expect(f'{getattr(percentile, attr):.2f}').to.eq(f'{value:.2f}')
def test_new_timeline_item(self): officer = OfficerFactory(id=123, appointed_date=date(2000, 1, 1), rank='Police Officer') unit1 = PoliceUnitFactory(unit_name='001', description='unit_001') unit2 = PoliceUnitFactory(unit_name='002', description='unit_002') OfficerHistoryFactory(officer=officer, unit=unit1, effective_date=date(2010, 1, 1), end_date=date(2011, 12, 31)) OfficerHistoryFactory(officer=officer, unit=unit2, effective_date=date(2012, 1, 1), end_date=None) AwardFactory(officer=officer, start_date=date(2011, 3, 23), award_type='Honorable Mention') AwardFactory(officer=officer, start_date=date(2015, 3, 23), award_type='Complimentary Letter') AwardFactory(officer=officer, start_date=date(2011, 3, 23), award_type='Life Saving Award') allegation = AllegationFactory( crid='123456', coaccused_count=4, incident_date=datetime(2011, 8, 23, tzinfo=pytz.utc) ) VictimFactory(allegation=allegation, gender='M', race='White', age=34) OfficerAllegationFactory( final_finding='UN', final_outcome='Unknown', officer=officer, allegation=allegation, allegation_category=AllegationCategoryFactory(category='category', allegation_name='sub category') ) OfficerAllegationFactory.create_batch(3, allegation=allegation) allegation2 = AllegationFactory( crid='654321', point=Point(35.5, 68.9), coaccused_count=1, incident_date=datetime(2015, 8, 23, tzinfo=pytz.utc) ) OfficerAllegationFactory( final_finding='UN', final_outcome='9 Day Suspension', officer=officer, allegation=allegation2, allegation_category=AllegationCategoryFactory(category='Use of Force', allegation_name='sub category') ) trr2011 = TRRFactory( officer=officer, trr_datetime=datetime(2011, 9, 23, tzinfo=pytz.utc), taser=True, firearm_used=False ) trr2015 = TRRFactory( officer=officer, trr_datetime=datetime(2015, 9, 23, tzinfo=pytz.utc), taser=False, firearm_used=False ) SalaryFactory(officer=officer, year=2001, rank='Police Officer', spp_date=date(2001, 9, 23)) SalaryFactory(officer=officer, year=2000, rank='Junior Police Officer', spp_date=date(2000, 1, 1)) cache_managers.cache_all() response = self.client.get(reverse('api-v2:officers-new-timeline-items', kwargs={'pk': 123})) expect(response.status_code).to.eq(status.HTTP_200_OK) expect(response.data).to.eq([ { 'trr_id': trr2015.id, 'date': '2015-09-23', 'kind': 'FORCE', 'taser': False, 'firearm_used': False, 'unit_name': '002', 'unit_description': 'unit_002', 'rank': 'Police Officer', }, { 'date': '2015-08-23', 'kind': 'CR', 'crid': '654321', 'category': 'Use of Force', 'subcategory': 'sub category', 'finding': 'Unfounded', 'outcome': '9 Day Suspension', 'coaccused': 1, 'unit_name': '002', 'unit_description': 'unit_002', 'rank': 'Police Officer', 'point': { 'lon': 35.5, 'lat': 68.9 }, 'victims': [], 'attachments': [] }, { 'date': '2012-01-01', 'kind': 'UNIT_CHANGE', 'unit_name': '002', 'unit_description': 'unit_002', 'rank': 'Police Officer', }, { 'trr_id': trr2011.id, 'date': '2011-09-23', 'kind': 'FORCE', 'taser': True, 'firearm_used': False, 'unit_name': '001', 'unit_description': 'unit_001', 'rank': 'Police Officer', }, { 'date': '2011-08-23', 'kind': 'CR', 'crid': '123456', 'category': 'category', 'subcategory': 'sub category', 'finding': 'Unfounded', 'outcome': 'Unknown', 'coaccused': 4, 'unit_name': '001', 'unit_description': 'unit_001', 'rank': 'Police Officer', 'victims': [ { 'race': 'White', 'age': 34, 'gender': 'Male', } ], 'attachments': [], }, { 'date': '2011-03-23', 'kind': 'AWARD', 'award_type': 'Life Saving Award', 'unit_name': '001', 'unit_description': 'unit_001', 'rank': 'Police Officer', }, { 'date': '2010-01-01', 'kind': 'UNIT_CHANGE', 'unit_name': '001', 'unit_description': 'unit_001', 'rank': 'Police Officer', }, { 'date': '2001-09-23', 'kind': 'RANK_CHANGE', 'unit_name': '', 'unit_description': '', 'rank': 'Police Officer', }, { 'date': '2000-01-01', 'kind': 'JOINED', 'unit_name': '', 'unit_description': '', 'rank': 'Junior Police Officer', }, ])
def test_list_item_content(self): officer1 = OfficerFactory( first_name='Jerome', last_name='Finnigan', birth_year=1950, race='Asian', gender='M', appointed_date=datetime(2011, 1, 1, tzinfo=pytz.utc), complaint_percentile=50.0, allegation_count=6, sustained_count=2, rank='Police Officer', ) officer2 = OfficerFactory( first_name='Raymond', last_name='Piwinicki', birth_year=1960, race='White', gender='M', appointed_date=datetime(2012, 1, 1, tzinfo=pytz.utc), complaint_percentile=0.0, allegation_count=1, sustained_count=1, rank='Police Officer', ) allegation = AllegationFactory(incident_date=datetime(2014, 1, 1, tzinfo=pytz.utc)) OfficerAllegationFactory( officer=officer1, allegation=allegation, final_finding='SU', start_date=date(2014, 1, 1), ) OfficerAllegationFactory( officer=officer2, allegation=allegation, final_finding='SU', start_date=date(2014, 1, 1), ) OfficerAllegationFactory( officer=officer1, final_finding='SU', allegation__incident_date=datetime(2016, 1, 1, tzinfo=pytz.utc), start_date=date(2016, 1, 1) ) OfficerAllegationFactory.create_batch( 4, officer=officer1, final_finding='NS', start_date=date(2015, 1, 1), allegation__incident_date=datetime(2015, 2, 20, tzinfo=pytz.utc) ) ActivityCardFactory(officer=officer1, last_activity=datetime(2018, 12, 22, tzinfo=pytz.utc)) ActivityCardFactory(officer=officer2, last_activity=datetime(2018, 10, 15, tzinfo=pytz.utc)) ActivityPairCardFactory( officer1=officer1, officer2=officer2, last_activity=datetime(2018, 5, 20, tzinfo=pytz.utc) ) cache_all() url = reverse('api-v2:activity-grid-list') response = self.client.get(url) expect(response.status_code).to.eq(status.HTTP_200_OK) expect(response.data).to.eq([{ 'id': officer1.id, 'full_name': 'Jerome Finnigan', 'complaint_count': 6, 'sustained_count': 2, 'birth_year': 1950, 'race': 'Asian', 'gender': 'Male', 'rank': 'Police Officer', 'percentile_trr': '0.0000', 'percentile_allegation_internal': '0.0000', 'percentile_allegation_civilian': '50.0000', 'percentile_allegation': '50.0000', 'kind': 'single_officer', }, { 'id': officer2.id, 'full_name': 'Raymond Piwinicki', 'complaint_count': 1, 'sustained_count': 1, 'birth_year': 1960, 'race': 'White', 'gender': 'Male', 'rank': 'Police Officer', 'percentile_trr': '0.0000', 'percentile_allegation_internal': '0.0000', 'percentile_allegation_civilian': '0.0000', 'percentile_allegation': '0.0000', 'kind': 'single_officer', }, { 'officer1': { 'id': officer1.id, 'full_name': 'Jerome Finnigan', 'birth_year': 1950, 'race': 'Asian', 'gender': 'Male', 'rank': 'Police Officer', 'percentile_trr': '0.0000', 'percentile_allegation_internal': '0.0000', 'percentile_allegation_civilian': '50.0000', 'percentile_allegation': '50.0000', 'complaint_count': 6, 'sustained_count': 2, }, 'officer2': { 'id': officer2.id, 'full_name': 'Raymond Piwinicki', 'birth_year': 1960, 'race': 'White', 'gender': 'Male', 'rank': 'Police Officer', 'percentile_trr': '0.0000', 'percentile_allegation_internal': '0.0000', 'percentile_allegation_civilian': '0.0000', 'percentile_allegation': '0.0000', 'complaint_count': 1, 'sustained_count': 1, }, 'coaccusal_count': 1, 'kind': 'coaccused_pair', }])