Skip to content

Commit 46951b4

Browse files
Merge branch 'develop' into feature/143a-upload-copa-pdf-files-to-documentcloud
2 parents 8923d69 + 3a6cbb7 commit 46951b4

File tree

147 files changed

+3062
-1851
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

147 files changed

+3062
-1851
lines changed

.pre-commit-config.yaml

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
---
12
repos:
2-
- repo: git://github.com/pre-commit/pre-commit-hooks
3-
sha: v1.1.1
3+
- repo: git://github.com/pre-commit/pre-commit-hooks
4+
rev: v2.0.0
45
hooks:
5-
- id: flake8
6+
- id: flake8
7+
language_version: python3

apiary.apib

+1
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,7 @@ This api require an api access token set in `Authorization` header. To get an ap
15151515
"sustained_count": 1,
15161516
"final_outcome": "Reprimand",
15171517
"final_finding": "Sustained",
1518+
"recommended_outcome": "10 Day Suspension",
15181519
"category": "Operation/Personnel Violations",
15191520
"complaint_percentile": 99.861,
15201521
"percentile": {

cpdb/__init__.py

Whitespace-only changes.

cpdb/activity_grid/models/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .activity_card import ActivityCard
2+
from .activity_pair_card import ActivityPairCard
3+
4+
__all__ = [ActivityCard, ActivityPairCard]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from django.db import models
2+
3+
4+
class ActivityCard(models.Model):
5+
officer = models.OneToOneField('data.Officer', on_delete=models.CASCADE, related_name='activity_card')
6+
important = models.BooleanField(default=False)
7+
last_activity = models.DateTimeField(null=True)

cpdb/activity_grid/models.py cpdb/activity_grid/models/activity_pair_card.py

-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
from django.db import models
22

33

4-
class ActivityCard(models.Model):
5-
officer = models.OneToOneField('data.Officer', on_delete=models.CASCADE, related_name='activity_card')
6-
important = models.BooleanField(default=False)
7-
last_activity = models.DateTimeField(null=True)
8-
9-
104
class ActivityPairCard(models.Model):
115
officer1 = models.ForeignKey('data.Officer', on_delete=models.CASCADE, related_name='activity_pair_card1')
126
officer2 = models.ForeignKey('data.Officer', on_delete=models.CASCADE, related_name='activity_pair_card2')

cpdb/activity_grid/views.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from activity_grid.models import ActivityCard, ActivityPairCard
88
from activity_grid.serializers import OfficerCardSerializer, SimpleCardSerializer
99
from officers.doc_types import OfficerInfoDocType
10-
from twitterbot.models import TYPE_SINGLE_OFFICER, TYPE_COACCUSED_PAIR
10+
from twitterbot.constants import RESPONSE_TYPE_SINGLE_OFFICER, RESPONSE_TYPE_COACCUSED_PAIR
1111

1212

1313
class ActivityGridViewSet(viewsets.ViewSet):
@@ -34,7 +34,7 @@ def get_activity_cards(self):
3434
result['important'] = card.important
3535
result['null_position'] = card.null_position
3636
result['last_activity'] = card.last_activity
37-
result['type'] = TYPE_SINGLE_OFFICER
37+
result['type'] = RESPONSE_TYPE_SINGLE_OFFICER
3838
results.append(result)
3939
return results
4040

@@ -72,7 +72,7 @@ def get_activity_pair_cards(self):
7272
'important': pair.important,
7373
'null_position': pair.null_position,
7474
'last_activity': pair.last_activity,
75-
'type': TYPE_COACCUSED_PAIR
75+
'type': RESPONSE_TYPE_COACCUSED_PAIR
7676
})
7777
return results
7878

cpdb/airtable_integration/services/document_request_service.py

+47-23
Original file line numberDiff line numberDiff line change
@@ -39,30 +39,51 @@ def _build_record(cls, raw_object):
3939
'email': '[email protected]',
4040
'name': 'Rajiv Sinclair'
4141
}
42-
]
42+
],
43+
'Date requested by user': raw_object.timestamp.strftime(format='%Y-%m-%d')
4344
}
4445

4546
@classmethod
4647
def _delay_upload(cls, raw_object):
4748
record = cls._build_record(raw_object)
49+
50+
if raw_object.airtable_id:
51+
airtable_id = cls._update_airtable_row(raw_object.airtable_id, record)
52+
else:
53+
airtable_id = cls._add_airtable_row(record)
54+
time.sleep(cls.API_LIMIT)
55+
return airtable_id
56+
57+
@classmethod
58+
def _add_airtable_row(cls, record):
4859
try:
4960
res = cls._get_foia_airtable().insert(record)
50-
time.sleep(cls.API_LIMIT)
5161
return res['id']
5262
except HTTPError:
5363
return ''
5464

5565
@classmethod
56-
def _get_uploaded_objects(cls):
66+
def _update_airtable_row(cls, airtable_id, record):
67+
try:
68+
res = cls._get_foia_airtable().update(airtable_id, record)
69+
return res['id']
70+
except HTTPError as err:
71+
if '404' in err.args[0].split(' '):
72+
return cls._add_airtable_row(record)
73+
else:
74+
return airtable_id
75+
76+
@classmethod
77+
def _get_uploaded_objects(cls, update_all_records=False):
5778
raise NotImplementedError
5879

5980
@classmethod
6081
def _post_handle(cls, upload_results):
6182
raise NotImplementedError
6283

6384
@classmethod
64-
def upload(cls):
65-
raw_objects = cls._get_uploaded_objects()
85+
def upload(cls, update_all_records=False):
86+
raw_objects = cls._get_uploaded_objects(update_all_records)
6687

6788
uploaded_results = [
6889
(raw_object, cls._delay_upload(raw_object))
@@ -78,25 +99,26 @@ def _build_data(cls, document_request):
7899
officer_allegations = allegation.officerallegation_set.select_related('officer')\
79100
.order_by('officer__first_name', 'officer__last_name')
80101
officers_info = [
81-
"{officer_name}(ID {officer_id})".format(
82-
officer_id=officer_allegation.officer.id,
83-
officer_name=officer_allegation.officer.full_name
84-
)
102+
f'{officer_allegation.officer.full_name}(ID {officer_allegation.officer.id})'
85103
for officer_allegation in officer_allegations if officer_allegation.officer
86104
]
87-
explanation = "Officers: {}".format(', '.join(officers_info)) if officers_info else ''
88-
requested_for = "CR {crid}".format(crid=allegation.crid)
89-
pre_2006 = allegation.incident_date and allegation.incident_date.year < 2006
105+
explanation = f"Officers: {', '.join(officers_info)}" if officers_info else ''
106+
requested_for = f'CR {allegation.crid}'
90107
agencies = [
91108
settings.AIRTABLE_CPD_AGENCY_ID
92-
if pre_2006 or document_request.investigated_by_cpd()
109+
if document_request.investigated_by_cpd
93110
else settings.AIRTABLE_COPA_AGENCY_ID
94111
]
95112
return explanation, requested_for, agencies
96113

97114
@classmethod
98-
def _get_uploaded_objects(cls):
99-
return AttachmentRequest.objects.filter(airtable_id='')
115+
def _get_uploaded_objects(cls, update_all_records=False):
116+
records = AttachmentRequest.objects.annotate_investigated_by_cpd().select_related('allegation')
117+
118+
if not update_all_records:
119+
records = records.filter(airtable_id='')
120+
121+
return records
100122

101123
@classmethod
102124
def _post_handle(cls, uploaded_results):
@@ -106,7 +128,7 @@ def _post_handle(cls, uploaded_results):
106128
attachment_request.airtable_id = record_id
107129
uploaded_attachment_requests.append(attachment_request)
108130

109-
AttachmentRequest.objects.bulk_update(
131+
AttachmentRequest.bulk_objects.bulk_update(
110132
uploaded_attachment_requests,
111133
update_fields=['airtable_id'],
112134
batch_size=1000
@@ -117,17 +139,19 @@ class TRRRequestAirTableUploader(AirTableUploader):
117139
@classmethod
118140
def _build_data(cls, document_request):
119141
officer = document_request.trr.officer
120-
explanation = "Officer: {officer_name}(ID {officer_id})".format(
121-
officer_id=officer.id,
122-
officer_name=officer.full_name
123-
) if officer else ''
142+
explanation = f'Officer: {officer.full_name}(ID {officer.id})' if officer else ''
124143

125-
requested_for = "TRR {trrid}".format(trrid=document_request.trr_id)
144+
requested_for = f'TRR {document_request.trr_id}'
126145
return explanation, requested_for, []
127146

128147
@classmethod
129-
def _get_uploaded_objects(cls):
130-
return TRRAttachmentRequest.objects.filter(airtable_id='')
148+
def _get_uploaded_objects(cls, update_all_records=False):
149+
records = TRRAttachmentRequest.objects.all()
150+
151+
if not update_all_records:
152+
records = records.filter(airtable_id='')
153+
154+
return records
131155

132156
@classmethod
133157
def _post_handle(cls, uploaded_results):

cpdb/airtable_integration/tests/management/commands/test_upload_document_requests.py

+10-7
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ class UpdateDocumentsCommandTestCase(TestCase):
2222
def test_upload_document_requests(self, airtable_mock):
2323
airtable_mock.insert.return_value = {'id': 'airtable_id'}
2424

25-
allegation123 = AllegationFactory(crid='123', incident_date=datetime(2010, 1, 1, tzinfo=pytz.utc))
25+
allegation123 = AllegationFactory(crid='123', incident_date=datetime(2005, 1, 1, tzinfo=pytz.utc))
2626
officer_1 = OfficerFactory(id=1, first_name='Marry', last_name='Jane')
2727
officer_2 = OfficerFactory(id=2, first_name='John', last_name='Henry')
2828
OfficerAllegationFactory(allegation=allegation123, officer=officer_1)
2929
OfficerAllegationFactory(allegation=allegation123, officer=officer_2)
3030
investigator = InvestigatorFactory(officer=officer_1)
3131
InvestigatorAllegationFactory(allegation=allegation123, investigator=investigator)
32-
AttachmentRequestFactory(
32+
cr_request_1 = AttachmentRequestFactory(
3333
allegation=allegation123,
3434
3535
airtable_id='')
@@ -43,7 +43,7 @@ def test_upload_document_requests(self, airtable_mock):
4343
officer_4 = OfficerFactory(id=4, first_name='John', last_name='Henry')
4444
OfficerAllegationFactory(allegation=allegation456, officer=officer_3)
4545
OfficerAllegationFactory(allegation=allegation456, officer=officer_4)
46-
AttachmentRequestFactory(
46+
cr_request_2 = AttachmentRequestFactory(
4747
allegation=allegation456,
4848
4949
airtable_id='')
@@ -53,7 +53,7 @@ def test_upload_document_requests(self, airtable_mock):
5353
airtable_id='cr4444')
5454

5555
trr = TRRFactory(id='123456', officer=officer_1)
56-
TRRAttachmentRequestFactory(
56+
trr_request = TRRAttachmentRequestFactory(
5757
trr=trr,
5858
5959
airtable_id='')
@@ -81,7 +81,8 @@ def test_upload_document_requests(self, airtable_mock):
8181
'email': '[email protected]',
8282
'name': 'Rajiv Sinclair'
8383
}
84-
]
84+
],
85+
'Date requested by user': cr_request_1.timestamp.strftime(format='%Y-%m-%d')
8586
}),
8687
call({
8788
'Explanation': 'Officers: John Henry(ID 4), Marry Jane(ID 3)',
@@ -96,7 +97,8 @@ def test_upload_document_requests(self, airtable_mock):
9697
'email': '[email protected]',
9798
'name': 'Rajiv Sinclair'
9899
}
99-
]
100+
],
101+
'Date requested by user': cr_request_2.timestamp.strftime(format='%Y-%m-%d')
100102
}),
101103
call({
102104
'Explanation': 'Officer: Marry Jane(ID 1)',
@@ -111,7 +113,8 @@ def test_upload_document_requests(self, airtable_mock):
111113
'email': '[email protected]',
112114
'name': 'Rajiv Sinclair'
113115
}
114-
]
116+
],
117+
'Date requested by user': trr_request.timestamp.strftime(format='%Y-%m-%d')
115118
})
116119
]
117120

0 commit comments

Comments
 (0)