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)
Exemple #2
0
    def test_export_xlsx_empty(self):
        officer = OfficerFactory(id=1)
        writer = AccusedXlsxWriter(officer, self.test_output_dir)
        writer.export_xlsx()

        self.covert_xlsx_to_csv('accused.xlsx')
        self.assert_csv_files_equal('empty', [
            'Allegation', 'Coaccused Officer', 'Beat', 'Police Witness',
            'Victim'
        ])
 def test_complainant_gender_aggregation_with_duplicated_allegation(self):
     unit = PoliceUnitFactory()
     officer1 = OfficerFactory()
     officer2 = OfficerFactory()
     allegation = AllegationFactory()
     OfficerHistoryFactory(officer=officer1, unit=unit)
     OfficerHistoryFactory(officer=officer2, unit=unit)
     OfficerAllegationFactory(officer=officer1,
                              allegation=allegation,
                              final_finding='SU')
     OfficerAllegationFactory(officer=officer2,
                              allegation=allegation,
                              final_finding='SU')
     ComplainantFactory(allegation=allegation, gender='F')
     expect(unit.complainant_gender_aggregation).to.eq([{
         'name': 'Female',
         'count': 1,
         'sustained_count': 1
     }])
    def test_relevant_complaints_via_police_witnesses(self):
        pinned_officer_1 = OfficerFactory(id=1)
        pinned_officer_2 = OfficerFactory(id=2)
        not_relevant_officer = OfficerFactory(id=999)
        relevant_allegation_11 = AllegationFactory(crid='11',
                                                   incident_date=datetime(
                                                       2002,
                                                       2,
                                                       21,
                                                       tzinfo=pytz.utc))
        relevant_allegation_12 = AllegationFactory(crid='12',
                                                   incident_date=datetime(
                                                       2002,
                                                       2,
                                                       22,
                                                       tzinfo=pytz.utc))
        relevant_allegation_21 = AllegationFactory(crid='21',
                                                   incident_date=datetime(
                                                       2002,
                                                       2,
                                                       23,
                                                       tzinfo=pytz.utc))
        not_relevant_allegation = AllegationFactory(crid='999')
        pinboard = PinboardFactory(
            title='Test pinboard',
            description='Test description',
        )
        pinboard.officers.set([pinned_officer_1, pinned_officer_2])
        PoliceWitnessFactory(allegation=relevant_allegation_11,
                             officer=pinned_officer_1)
        PoliceWitnessFactory(allegation=relevant_allegation_12,
                             officer=pinned_officer_1)
        PoliceWitnessFactory(allegation=relevant_allegation_21,
                             officer=pinned_officer_2)
        PoliceWitnessFactory(allegation=not_relevant_allegation,
                             officer=not_relevant_officer)

        relevant_complaints = list(pinboard.relevant_complaints)

        expect(relevant_complaints).to.have.length(3)
        expect(relevant_complaints[0].crid).to.eq('21')
        expect(relevant_complaints[1].crid).to.eq('12')
        expect(relevant_complaints[2].crid).to.eq('11')
    def test_extract_datum(self):
        officer = OfficerFactory(rank='Police Officer', active=ACTIVE_YES_CHOICE)
        SalaryFactory(rank='Police Officer', officer=officer)

        expect(RankIndexer().extract_datum('Police Officer')).to.eq({
            'rank': 'Police Officer',
            'tags': ['rank'],
            'active_officers_count': 1,
            'officers_most_complaints': []
        })
Exemple #6
0
    def test_lastmod(self):
        officer = OfficerFactory(id=123)
        with freeze_time(datetime(2018, 4, 4, 12, 0, 1, tzinfo=pytz.utc)):
            officer.allegation_count = 2
            officer.save()

        officer.refresh_from_db()

        expect(OfficerSitemap().lastmod(officer)).to.eq(
            datetime(2018, 4, 4, 12, 0, 1, tzinfo=pytz.utc))
    def test_update_cr_attachment_request_to_foia_with_valid_airtable_id_with_error(
            self, airtable_mock):
        airtable_mock.update.return_value = {'id': 'airtable_id'}

        allegation = AllegationFactory(crid='123456',
                                       incident_date=datetime(2010,
                                                              1,
                                                              1,
                                                              tzinfo=pytz.utc))
        attachment_request = AttachmentRequestFactory(
            allegation=allegation,
            email='*****@*****.**',
            airtable_id='airtable_id')
        officer_1 = OfficerFactory(id=1, first_name='Marry', last_name='Jane')
        officer_2 = OfficerFactory(id=2, first_name='John', last_name='Henry')
        OfficerAllegationFactory(allegation=allegation, officer=officer_1)
        OfficerAllegationFactory(allegation=allegation, officer=officer_2)

        expected_airtable_data = {
            'Explanation':
            'Officers: John Henry(ID 2), Marry Jane(ID 1)',
            'Project': ['CPDP'],
            'Agency': ['COPA_AGENCY_ID'],
            'Requested For':
            'CR 123456',
            'Requestor': [{
                'id': 'usrGiZFcyZ6wHTYWd',
                'email': '*****@*****.**',
                'name': 'Rajiv Sinclair'
            }],
            'Date requested by user':
            attachment_request.created_at.strftime('%Y-%m-%d'),
            'Requester Email':
            '*****@*****.**'
        }

        CRRequestAirTableUploader.upload(update_all_records=True)
        attachment_request.refresh_from_db()

        airtable_mock.update.assert_called_with('airtable_id',
                                                expected_airtable_data)
        expect(attachment_request.airtable_id).to.be.eq('airtable_id')
Exemple #8
0
 def test_process_single_officer_response(self):
     officer = OfficerFactory()
     updater = ActivityGridUpdater()
     response = {
         'entity': {'id': officer.id},
         'type': 'single_officer'
     }
     self.refresh_index()
     updater.process(response)
     expect(ActivityCard.objects.get(officer=officer).last_activity).to.eq(
         datetime.datetime(2017, 9, 14, 12, 0, 1, tzinfo=pytz.utc))
    def test_member_age_aggregation_in_case_officer_left_and_rejoin(self):
        unit1 = PoliceUnitFactory()
        unit2 = PoliceUnitFactory()
        officer = OfficerFactory(birth_year='1980')
        OfficerHistoryFactory(officer=officer, unit=unit1)
        OfficerHistoryFactory(officer=officer, unit=unit2)
        OfficerHistoryFactory(officer=officer, unit=unit1)
        OfficerHistoryFactory(officer=OfficerFactory(birth_year='1985'), unit=unit1)
        OfficerHistoryFactory(officer=OfficerFactory(birth_year=None), unit=unit1)

        expect(sorted(unit1.member_age_aggregation, key=itemgetter('name'))).to.eq([
            {
                'name': '31-40',
                'count': 2
            },
            {
                'name': 'Unknown',
                'count': 1
            }
        ])
    def test_member_race_aggregation_in_case_officer_left_and_rejoin(self):
        unit1 = PoliceUnitFactory()
        unit2 = PoliceUnitFactory()
        officer = OfficerFactory(race='White')
        OfficerHistoryFactory(officer=officer, unit=unit1)
        OfficerHistoryFactory(officer=officer, unit=unit2)
        OfficerHistoryFactory(officer=officer, unit=unit1)
        OfficerHistoryFactory(officer=OfficerFactory(race='White'), unit=unit1)
        OfficerHistoryFactory(officer=OfficerFactory(race=''), unit=unit1)

        expect(sorted(unit1.member_race_aggregation, key=itemgetter('name'))).to.eq([
            {
                'name': 'Unknown',
                'count': 1
            },
            {
                'name': 'White',
                'count': 2
            }
        ])
Exemple #11
0
    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',
            }
        ])
Exemple #12
0
    def test_search_date_trr_result(self):
        officer = OfficerFactory(
            id=123456,
            rank='Sergeant of Police',
            first_name='Jesse',
            last_name='Pinkman',
            complaint_percentile=4.4,
            civilian_allegation_percentile=1.1,
            internal_allegation_percentile=2.2,
            trr_percentile=3.3,
            allegation_count=1,
            resignation_date=date(2015, 4, 14)
        )
        TRRFactory(
            id='123',
            trr_datetime=datetime(2007, 12, 27, tzinfo=pytz.utc),
        )
        TRRFactory(
            id='456',
            trr_datetime=datetime(2008, 12, 27, tzinfo=pytz.utc),
            block='3000',
            street='Michigan Ave',
            taser=False,
            firearm_used=True,
            officer=officer
        )

        self.rebuild_index()
        self.refresh_index()

        url = reverse('api:suggestion-list')
        response = self.client.get(url, {
            'term': '2008-12-27',
        })

        results = response.data['DATE > TRR']
        expect(results).to.have.length(1)
        expect(results[0]['id']).to.eq('456')
        expect(results[0]).to.eq({
            'id': '456',
            'trr_datetime': '2008-12-27',
            'to': '/trr/456/',
            'category': 'Firearm',
            'address': '3000 Michigan Ave',
            'officer': {
                'id': 123456,
                'full_name': 'Jesse Pinkman',
                'percentile_allegation': '4.4000',
                'percentile_trr': '3.3000',
                'percentile_allegation_civilian': '1.1000',
                'percentile_allegation_internal': '2.2000',
                'allegation_count': 1
            }
        })
    def test_extract_datum_investigator_officer_name(self):
        InvestigatorAllegationFactory(
            investigator__officer=OfficerFactory(first_name='Jerome', last_name='Finnigan'),
            investigator__first_name='German',
            investigator__last_name='Lauren'
        )

        rows = self.extract_data()
        expect(rows).to.have.length(1)
        expect(rows[0]['involvements'][0]['abbr_name']).to.eq('J. Finnigan')
        expect(rows[0]['involvements'][0]['full_name']).to.eq('Jerome Finnigan')
Exemple #14
0
    def test_export_xlsx_empty(self):
        officer = OfficerFactory(id=1)
        writer = InvestigatorXlsxWriter(officer, self.test_output_dir)
        writer.export_xlsx()

        self.covert_xlsx_to_csv('investigator.xlsx')
        self.assert_csv_files_equal(expectation_dir='empty',
                                    sheet_names=[
                                        'Allegation', 'Accused Officer',
                                        'Beat', 'Police Witness', 'Victim'
                                    ])
    def test_check_zip_file_exist_return_false(self, aws_mock):
        exception = botocore.exceptions.ClientError(
            error_response={'Error': {
                'Code': 'NoSuchKey'
            }},
            operation_name='get_object')
        aws_mock.s3.get_object.side_effect = exception
        officer = OfficerFactory(first_name='Jerome', last_name='Finnigan')

        expect(officer.check_zip_file_exist(with_docs=False)).to.be.false()
        expect(officer.check_zip_file_exist(with_docs=True)).to.be.false()
    def test_coaccusals(self):
        officer0 = OfficerFactory()
        officer1 = OfficerFactory()
        officer2 = OfficerFactory()
        allegation0 = AllegationFactory()
        allegation1 = AllegationFactory()
        allegation2 = AllegationFactory()
        OfficerAllegationFactory(officer=officer0, allegation=allegation0)
        OfficerAllegationFactory(officer=officer0, allegation=allegation1)
        OfficerAllegationFactory(officer=officer0, allegation=allegation2)
        OfficerAllegationFactory(officer=officer1, allegation=allegation0)
        OfficerAllegationFactory(officer=officer1, allegation=allegation1)
        OfficerAllegationFactory(officer=officer2, allegation=allegation2)

        coaccusals = list(officer0.coaccusals)
        expect(coaccusals).to.have.length(2)
        expect(coaccusals).to.contain(officer1)
        expect(coaccusals).to.contain(officer2)

        expect(coaccusals[coaccusals.index(officer1)].coaccusal_count).to.eq(2)
        expect(coaccusals[coaccusals.index(officer2)].coaccusal_count).to.eq(1)
    def test_retrieve_missing_percentile(self):
        officer = OfficerFactory(
            civilian_allegation_percentile=None,
            internal_allegation_percentile=None,
            trr_percentile=None
        )
        trr = TRRFactory(officer=officer)

        self.refresh_index()

        response = self.client.get(reverse('api-v2:trr-detail', kwargs={'pk': trr.id}))
        expect(response.status_code).to.eq(status.HTTP_200_OK)
    def test_extract_info_coaccusals(self):
        officer = OfficerFactory(id=1101)
        other_officer = OfficerFactory(id=1102)
        allegation1 = AllegationFactory()
        OfficerAllegationFactory(officer=officer, allegation=allegation1)
        OfficerAllegationFactory(officer=other_officer, allegation=allegation1)
        allegation2 = AllegationFactory()
        OfficerAllegationFactory(officer=officer, allegation=allegation2)
        OfficerAllegationFactory(officer=other_officer, allegation=allegation2)

        rows = sorted(self.extract_data(), key=itemgetter('id'))

        expect(rows).to.have.length(2)
        expect(rows[0]['coaccusals']).to.eq([{
            'id': 1102,
            'coaccusal_count': 2
        }])
        expect(rows[1]['coaccusals']).to.eq([{
            'id': 1101,
            'coaccusal_count': 2
        }])
 def test_historic_units(self):
     officer = OfficerFactory()
     unithistory1 = OfficerHistoryFactory(officer=officer,
                                          unit__unit_name='1',
                                          unit__description='Unit 1',
                                          effective_date=date(2000, 1, 1))
     unithistory2 = OfficerHistoryFactory(officer=officer,
                                          unit__unit_name='2',
                                          unit__description='Unit 2',
                                          effective_date=date(2000, 1, 2))
     expect(officer.historic_units).to.eq(
         [unithistory2.unit, unithistory1.unit])
    def test_last_unit(self):
        officer = OfficerFactory()
        expect(officer.last_unit).to.equal(None)

        last_unit = PoliceUnitFactory(unit_name='BDCH')

        OfficerHistoryFactory(officer=officer, unit=PoliceUnitFactory(unit_name='CAND'), end_date=date(2000, 1, 1))
        OfficerHistoryFactory(officer=officer, unit=last_unit, end_date=date(2002, 1, 1))
        officer_cache_manager.build_cached_columns()
        officer.refresh_from_db()

        expect(officer.last_unit).to.eq(last_unit)
 def test_complainant_race_aggregation(self):
     unit = PoliceUnitFactory()
     officer = OfficerFactory()
     allegation = AllegationFactory()
     OfficerHistoryFactory(unit=unit, officer=officer)
     OfficerAllegationFactory(officer=officer, allegation=allegation, final_finding='NS')
     ComplainantFactory(allegation=allegation, race='White')
     expect(unit.complainant_race_aggregation).to.eq([{
         'name': 'White',
         'count': 1,
         'sustained_count': 0
     }])
Exemple #22
0
    def test_search_investigator_cr_results(self):
        allegation_1 = AllegationFactory(crid='123456',
                                         incident_date=datetime(
                                             2002, 2, 3, tzinfo=pytz.utc))
        allegation_2 = AllegationFactory(crid='654321',
                                         incident_date=datetime(
                                             2005, 2, 3, tzinfo=pytz.utc))
        officer = OfficerFactory(id=123, first_name='Edward', last_name='May')
        investigator_1 = InvestigatorFactory(first_name='Jerome',
                                             last_name='Finnigan')
        investigator_2 = InvestigatorFactory(officer=officer)
        InvestigatorAllegationFactory(investigator=investigator_1,
                                      allegation=allegation_1)
        InvestigatorAllegationFactory(investigator=investigator_2,
                                      allegation=allegation_1)
        InvestigatorAllegationFactory(investigator=investigator_1,
                                      allegation=allegation_2)
        OfficerAllegationFactory(
            allegation=allegation_1,
            allegation_category__category='Illegal Search')
        OfficerAllegationFactory(allegation=allegation_2,
                                 allegation_category__category='')

        allegation_cache_manager.cache_data()
        self.rebuild_index()
        self.refresh_index()

        url = reverse('api-v2:search-mobile-list')
        response = self.client.get(url, {
            'term': 'Jerome',
        })

        results = response.data['INVESTIGATOR > CR']
        expect(results).to.have.length(2)

        expected_results = {
            '123456': {
                'id': '123456',
                'crid': '123456',
                'category': 'Illegal Search',
                'incident_date': '2002-02-03'
            },
            '654321': {
                'id': '654321',
                'crid': '654321',
                'category': 'Unknown',
                'incident_date': '2005-02-03'
            }
        }

        for cr_data in results:
            expect(cr_data).to.eq(expected_results[cr_data['id']])
 def test_complaint_category_aggregation(self):
     unit = PoliceUnitFactory()
     officer = OfficerFactory()
     OfficerHistoryFactory(unit=unit, officer=officer)
     OfficerAllegationFactory(officer=officer,
                              allegation_category=AllegationCategoryFactory(
                                  category='Use of Force'),
                              final_finding='NS')
     expect(unit.complaint_category_aggregation).to.eq([{
         'name': 'Use of Force',
         'count': 1,
         'sustained_count': 0
     }])
Exemple #24
0
 def test_top_percentile_type_not_found(self):
     officer = OfficerFactory(id=1, appointed_date=date(2016, 1, 1))
     OfficerAllegationFactory(officer=officer,
                              allegation__incident_date=datetime(
                                  2013, 1, 1, tzinfo=pytz.utc),
                              start_date=datetime(2014,
                                                  1,
                                                  1,
                                                  tzinfo=pytz.utc),
                              allegation__is_officer_complaint=False)
     with self.assertRaisesRegex(ValueError, 'group is invalid'):
         officer_percentile.top_percentile(2017,
                                           percentile_groups=['not_exist'])
 def test_complaint_category_aggregation_with_duplicated_allegation(self):
     unit = PoliceUnitFactory()
     officer1 = OfficerFactory()
     officer2 = OfficerFactory()
     allegation = AllegationFactory()
     allegation_category = AllegationCategoryFactory(
         category='Use of Force')
     OfficerHistoryFactory(officer=officer1, unit=unit)
     OfficerHistoryFactory(officer=officer2, unit=unit)
     OfficerAllegationFactory(officer=officer1,
                              allegation=allegation,
                              allegation_category=allegation_category,
                              final_finding='NS')
     OfficerAllegationFactory(officer=officer2,
                              allegation=allegation,
                              allegation_category=allegation_category,
                              final_finding='NS')
     expect(unit.complaint_category_aggregation).to.eq([{
         'name': 'Use of Force',
         'count': 1,
         'sustained_count': 0
     }])
 def test_active_member_count(self):
     unit1 = PoliceUnitFactory()
     unit2 = PoliceUnitFactory()
     officer = OfficerFactory()
     OfficerHistoryFactory(officer=officer,
                           unit=unit1,
                           end_date=datetime(2011, 1, 1, tzinfo=pytz.utc))
     OfficerHistoryFactory(officer=officer,
                           unit=unit2,
                           end_date=datetime(2011, 2, 1, tzinfo=pytz.utc))
     OfficerHistoryFactory(officer=officer, unit=unit1, end_date=None)
     expect(unit1.active_member_count).to.eq(1)
     expect(unit2.active_member_count).to.eq(0)
    def test_rank_change_timeline_no_officer_appointed_date(self, rank_change_new_timeline_serializer_mock):
        officer = OfficerFactory(id=123, appointed_date=None)

        salary = SalaryFactory(
            year=2001, rank='Police Officer', officer=officer, rank_changed=True, spp_date=date(2001, 5, 3)
        )

        expect(OfficerTimelineQuery(officer)._rank_change_timeline).to.eq([{'id': 1}])

        rank_change_timeline_queryset_arg = rank_change_new_timeline_serializer_mock.call_args[0][0]

        salary_arg, = rank_change_timeline_queryset_arg
        expect(salary_arg.id).to.eq(salary.id)
    def test_generate_presigned_zip_url_without_docs(self, aws_mock):
        aws_mock.s3.generate_presigned_url.return_value = 'presigned_url'

        officer = OfficerFactory(first_name='Jerome', last_name='Finnigan')

        expect(officer.generate_presigned_zip_url(
            with_docs=False)).to.eq('presigned_url')
        expect(aws_mock.s3.generate_presigned_url).to.be.called_with(
            ClientMethod='get_object',
            Params={
                'Bucket': 'officer_content_bucket',
                'Key': 'zip/Jerome_Finnigan.zip',
            })
    def test_cr_timeline(self, cr_new_timeline_serializer_mock):
        officer = OfficerFactory(id=123)
        OfficerAllegationFactory(id=1, officer=officer, allegation__incident_date=datetime(2002, 2, 3, tzinfo=pytz.utc))
        OfficerAllegationFactory(id=2, officer=officer, allegation__incident_date=datetime(2003, 1, 5, tzinfo=pytz.utc))
        OfficerAllegationFactory(id=3, officer=officer, allegation__incident_date=None)

        unit_1 = PoliceUnitFactory(unit_name='001', description='District 001')
        unit_2 = PoliceUnitFactory(unit_name='002', description='District 002')
        OfficerHistoryFactory(
            officer=officer, unit=unit_1, effective_date=date(2002, 1, 3), end_date=date(2003, 1, 2)
        )
        OfficerHistoryFactory(
            officer=officer, unit=unit_2, effective_date=date(2003, 1, 3), end_date=date(2018, 1, 3)
        )
        SalaryFactory(
            year=2001, rank='Police Officer', officer=officer, rank_changed=True, spp_date=date(2001, 5, 3)
        )
        SalaryFactory(
            year=2002, rank='Senior Police Officer', officer=officer, rank_changed=True, spp_date=date(2002, 5, 3)
        )

        other_officer = OfficerFactory(id=456)
        OfficerAllegationFactory(id=4, officer=other_officer, start_date=date(2003, 1, 5))

        expect(OfficerTimelineQuery(officer)._cr_timeline).to.eq([{'id': 1}, {'id': 2}])

        cr_timeline_queryset_arg = cr_new_timeline_serializer_mock.call_args[0][0]
        officer_allegation_1_arg, officer_allegation_2_arg = sorted(cr_timeline_queryset_arg, key=attrgetter('id'))

        expect(officer_allegation_1_arg.id).to.eq(1)
        expect(officer_allegation_1_arg.unit_name).to.eq('001')
        expect(officer_allegation_1_arg.unit_description).to.eq('District 001')
        expect(officer_allegation_1_arg.rank_name).to.eq('Police Officer')

        expect(officer_allegation_2_arg.id).to.eq(2)
        expect(officer_allegation_2_arg.unit_name).to.eq('002')
        expect(officer_allegation_2_arg.unit_description).to.eq('District 002')
        expect(officer_allegation_2_arg.rank_name).to.eq('Senior Police Officer')
Exemple #30
0
    def test_clone(self):
        officer_1 = OfficerFactory(id=1)
        officer_2 = OfficerFactory(id=2)

        allegation_1 = AllegationFactory(crid='123abc')
        allegation_2 = AllegationFactory(crid='456def')

        trr_1 = TRRFactory(id=1, officer=OfficerFactory(id=3))
        trr_2 = TRRFactory(id=2, officer=OfficerFactory(id=4))

        pinboard = PinboardFactory(
            title='Pinboard title',
            description='Pinboard title',
            officers=(officer_1, officer_2),
            allegations=(allegation_1, allegation_2),
            trrs=(trr_1, trr_2),
        )
        cloned_pinboard = pinboard.clone()
        cloned_pinboard.refresh_from_db()

        officers = set(pinboard.officers.all().values_list('id', flat=True))
        allegations = set(pinboard.allegations.all().values_list('crid',
                                                                 flat=True))
        trrs = set(pinboard.trrs.all().values_list('id', flat=True))

        cloned_officers = set(cloned_pinboard.officers.all().values_list(
            'id', flat=True))
        cloned_allegations = set(cloned_pinboard.allegations.all().values_list(
            'crid', flat=True))
        cloned_trrs = set(cloned_pinboard.trrs.all().values_list('id',
                                                                 flat=True))

        expect(pinboard.title).to.eq(cloned_pinboard.title)
        expect(pinboard.description).to.eq(cloned_pinboard.description)
        expect(officers).to.eq(cloned_officers)
        expect(allegations).to.eq(cloned_allegations)
        expect(trrs).to.eq(cloned_trrs)
        expect(cloned_pinboard.source_pinboard).to.eq(pinboard)