From 5713521652d060caf663c60b6dceaa44af308b6f Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 15:25:31 -0500
Subject: [PATCH 01/55] Update database.py
---
OpenOversight/app/models/database.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/OpenOversight/app/models/database.py b/OpenOversight/app/models/database.py
index cba57f362..759224072 100644
--- a/OpenOversight/app/models/database.py
+++ b/OpenOversight/app/models/database.py
@@ -468,8 +468,7 @@ class Incident(BaseModel):
__tablename__ = "incidents"
id = db.Column(db.Integer, primary_key=True)
- date = db.Column(db.Date, unique=False, index=True)
- time = db.Column(db.Time, unique=False, index=True)
+ occurred_at = db.Column(db.DateTime(timezone=True), unique=False, nullable=False)
report_number = db.Column(db.String(50), index=True)
description = db.Column(db.Text(), nullable=True)
address_id = db.Column(db.Integer, db.ForeignKey("locations.id"))
From 71bdb39a4a3fed3ed4242948f3c897927b7c5721 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 15:38:47 -0500
Subject: [PATCH 02/55] Create
2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
---
...cea25_change_incident_date_and_time_to_.py | 51 +++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
diff --git a/OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py b/OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
new file mode 100644
index 000000000..157469a79
--- /dev/null
+++ b/OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
@@ -0,0 +1,51 @@
+"""change incident date and time to timestamptz
+
+Revision ID: 9fa948bcea25
+Revises: 18f43ac4622f
+Create Date: 2023-07-27 18:48:58.819477
+
+"""
+import os
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '9fa948bcea25'
+down_revision = '18f43ac4622f'
+branch_labels = None
+depends_on = None
+
+TIMEZONE = os.getenv("TIMEZONE", "America/Chicago")
+
+
+def upgrade():
+ op.add_column('incidents', sa.Column('occurred_at', sa.DateTime(timezone=True), nullable=True))
+ op.execute(
+ f"""
+ UPDATE incidents
+ SET occurred_at = (date::date || ' ' || COALESCE(time,
+ '00:00:00')::timetz)::timestamp AT TIME ZONE '{TIMEZONE}'
+ WHERE occurred_at IS NULL
+ """
+ )
+ op.alter_column('incidents', 'occurred_at', nullable=False)
+ op.drop_index('ix_incidents_date')
+ op.drop_index('ix_incidents_time')
+ op.drop_column('incidents', 'time')
+ op.drop_column('incidents', 'date')
+
+
+def downgrade():
+ op.add_column('incidents', sa.Column('date', sa.DATE(), nullable=True))
+ op.add_column('incidents', sa.Column('time', postgresql.TIME(), nullable=True))
+ op.execute(
+ f"""
+ UPDATE incidents
+ SET (date, time) = (occurred_at::date, (occurred_at::timestamptz AT TIME ZONE '{TIMEZONE}')::time)
+ """)
+
+ op.create_index(op.f('ix_incidents_time'), 'incidents', ['time'], unique=False)
+ op.create_index(op.f('ix_incidents_date'), 'incidents', ['date'], unique=False)
+ op.drop_column('incidents', 'occurred_at')
From d8b8b6f3e804f7092a31a44e67b3040ae61f3714 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 15:39:09 -0500
Subject: [PATCH 03/55] Update
2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
---
...cea25_change_incident_date_and_time_to_.py | 36 ++++++++++---------
1 file changed, 20 insertions(+), 16 deletions(-)
diff --git a/OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py b/OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
index 157469a79..9804b89ee 100644
--- a/OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
+++ b/OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
@@ -7,13 +7,14 @@
"""
import os
-from alembic import op
import sqlalchemy as sa
+from alembic import op
from sqlalchemy.dialects import postgresql
+
# revision identifiers, used by Alembic.
-revision = '9fa948bcea25'
-down_revision = '18f43ac4622f'
+revision = "9fa948bcea25"
+down_revision = "18f43ac4622f"
branch_labels = None
depends_on = None
@@ -21,31 +22,34 @@
def upgrade():
- op.add_column('incidents', sa.Column('occurred_at', sa.DateTime(timezone=True), nullable=True))
+ op.add_column(
+ "incidents", sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=True)
+ )
op.execute(
f"""
UPDATE incidents
- SET occurred_at = (date::date || ' ' || COALESCE(time,
+ SET occurred_at = (date::date || ' ' || COALESCE(time,
'00:00:00')::timetz)::timestamp AT TIME ZONE '{TIMEZONE}'
WHERE occurred_at IS NULL
"""
)
- op.alter_column('incidents', 'occurred_at', nullable=False)
- op.drop_index('ix_incidents_date')
- op.drop_index('ix_incidents_time')
- op.drop_column('incidents', 'time')
- op.drop_column('incidents', 'date')
+ op.alter_column("incidents", "occurred_at", nullable=False)
+ op.drop_index("ix_incidents_date")
+ op.drop_index("ix_incidents_time")
+ op.drop_column("incidents", "time")
+ op.drop_column("incidents", "date")
def downgrade():
- op.add_column('incidents', sa.Column('date', sa.DATE(), nullable=True))
- op.add_column('incidents', sa.Column('time', postgresql.TIME(), nullable=True))
+ op.add_column("incidents", sa.Column("date", sa.DATE(), nullable=True))
+ op.add_column("incidents", sa.Column("time", postgresql.TIME(), nullable=True))
op.execute(
f"""
UPDATE incidents
SET (date, time) = (occurred_at::date, (occurred_at::timestamptz AT TIME ZONE '{TIMEZONE}')::time)
- """)
+ """
+ )
- op.create_index(op.f('ix_incidents_time'), 'incidents', ['time'], unique=False)
- op.create_index(op.f('ix_incidents_date'), 'incidents', ['date'], unique=False)
- op.drop_column('incidents', 'occurred_at')
+ op.create_index(op.f("ix_incidents_time"), "incidents", ["time"], unique=False)
+ op.create_index(op.f("ix_incidents_date"), "incidents", ["date"], unique=False)
+ op.drop_column("incidents", "occurred_at")
From ac784952c93f925139358fe5f0aa098249696791 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 15:52:06 -0500
Subject: [PATCH 04/55] Update views.py
---
OpenOversight/app/main/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenOversight/app/main/views.py b/OpenOversight/app/main/views.py
index d97ae434e..7aa72da0b 100644
--- a/OpenOversight/app/main/views.py
+++ b/OpenOversight/app/main/views.py
@@ -1489,7 +1489,7 @@ def server_shutdown(): # pragma: no cover
class IncidentApi(ModelView):
model = Incident
model_name = "incident"
- order_by = "date"
+ order_by = "occurred_at"
descending = True
form = IncidentForm
create_function = create_incident
From f65b4ecfc28c77f36a624210c9e4605c006e31fc Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 15:52:11 -0500
Subject: [PATCH 05/55] Update incident_fields.html
---
.../app/templates/partials/incident_fields.html | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/OpenOversight/app/templates/partials/incident_fields.html b/OpenOversight/app/templates/partials/incident_fields.html
index 9444f9000..152b099b8 100644
--- a/OpenOversight/app/templates/partials/incident_fields.html
+++ b/OpenOversight/app/templates/partials/incident_fields.html
@@ -2,16 +2,14 @@
From 50e43f533c8bc8842590e45c27dc53a89e282e9b Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 15:59:28 -0500
Subject: [PATCH 06/55] Update officer_incidents.html
---
OpenOversight/app/templates/partials/officer_incidents.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenOversight/app/templates/partials/officer_incidents.html b/OpenOversight/app/templates/partials/officer_incidents.html
index f6a6f185f..4a79b5c6e 100644
--- a/OpenOversight/app/templates/partials/officer_incidents.html
+++ b/OpenOversight/app/templates/partials/officer_incidents.html
@@ -2,7 +2,7 @@ Incidents
{% if officer.incidents %}
- {% for incident in officer.incidents | sort(attribute='date') | reverse %}
+ {% for incident in officer.incidents | sort(attribute='occurred_at') | reverse %}
{% if not loop.first %}
|
From 30fd480ec62c3ceea7c1c5240c628294b8cb69f6 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 16:04:12 -0500
Subject: [PATCH 07/55] Update incident_detail.html
---
.../app/templates/incident_detail.html | 36 ++++++++++---------
1 file changed, 19 insertions(+), 17 deletions(-)
diff --git a/OpenOversight/app/templates/incident_detail.html b/OpenOversight/app/templates/incident_detail.html
index 4c855611b..e4b27608e 100644
--- a/OpenOversight/app/templates/incident_detail.html
+++ b/OpenOversight/app/templates/incident_detail.html
@@ -41,24 +41,26 @@
incident.department.name }}
{% endif %}
-
-
- Incident
- {% if incident.report_number %}{{ incident.report_number }}{% endif %}
-
-
-
-
- {% with detail=True %}
- {% include "partials/incident_fields.html" %}
- {% endwith %}
-
-
+
+
+
+ Incident
+ {% if incident.report_number %}{{ incident.report_number }}{% endif %}
+
+
+
+
+ {% with detail=True %}
+ {% include "partials/incident_fields.html" %}
+ {% endwith %}
+
+
+
+
+
+ Incident Description
+ {{ incident.description | markdown }}
-
-
- Incident Description
- {{ incident.description | markdown }}
{% include "partials/links_and_videos_row.html" %}
{% if current_user.is_administrator
From d5130692139351a0ceeb85eaa467f8b1a57bfcca Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 16:38:15 -0500
Subject: [PATCH 08/55] Update database_imports.py
---
OpenOversight/app/models/database_imports.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/OpenOversight/app/models/database_imports.py b/OpenOversight/app/models/database_imports.py
index 99db37e76..ac38ecba7 100644
--- a/OpenOversight/app/models/database_imports.py
+++ b/OpenOversight/app/models/database_imports.py
@@ -274,8 +274,7 @@ def get_or_create_location_from_dict(
def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> Incident:
incident = Incident(
- date=parse_date(data.get("date")),
- time=parse_time(data.get("time")),
+ occurred_at=data,
report_number=parse_str(data.get("report_number"), None),
description=parse_str(data.get("description"), None),
address_id=data.get("address_id"),
From 928d813c158d95639bbbfcfa5e9fabe0545166f4 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 16:38:19 -0500
Subject: [PATCH 09/55] Update conftest.py
---
OpenOversight/tests/conftest.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/OpenOversight/tests/conftest.py b/OpenOversight/tests/conftest.py
index ccd114692..15127a72a 100644
--- a/OpenOversight/tests/conftest.py
+++ b/OpenOversight/tests/conftest.py
@@ -540,8 +540,7 @@ def add_mockdata(session):
test_incidents = [
Incident(
- date=datetime.date(2016, 3, 16),
- time=datetime.time(4, 20),
+ occurred_at=datetime.datetime(2016, 3, 16, 4, 20),
report_number="42",
description="### A thing happened\n **Markup** description",
department_id=1,
@@ -553,8 +552,7 @@ def add_mockdata(session):
last_updated_id=1,
),
Incident(
- date=datetime.date(2017, 12, 11),
- time=datetime.time(2, 40),
+ occurred_at=datetime.datetime(2017, 12, 11, 2, 40),
report_number="38",
description="A thing happened",
department_id=2,
@@ -566,7 +564,7 @@ def add_mockdata(session):
last_updated_id=1,
),
Incident(
- date=datetime.datetime(2019, 1, 15),
+ occurred_at=datetime.datetime(2019, 1, 15),
report_number="39",
description=(
Path(__file__).parent / "description_overflow.txt"
From 242ffe4d116e2c21c751884310aa8976b160941d Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 16:44:09 -0500
Subject: [PATCH 10/55] Update forms.py
---
OpenOversight/app/utils/forms.py | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/OpenOversight/app/utils/forms.py b/OpenOversight/app/utils/forms.py
index d48ed5775..ffafa262a 100644
--- a/OpenOversight/app/utils/forms.py
+++ b/OpenOversight/app/utils/forms.py
@@ -134,8 +134,13 @@ def create_description(self, form):
def create_incident(self, form):
fields = {
- "date": form.date_field.data,
- "time": form.time_field.data,
+ "occurred_at": datetime.datetime(
+ form.date_field.data.year,
+ form.date_field.data.month,
+ form.date_field.data.day,
+ form.time_field.data.hour,
+ form.time_field.data.minute,
+ ),
"officers": [],
"license_plates": [],
"links": [],
@@ -171,8 +176,7 @@ def create_incident(self, form):
fields["links"].append(li)
return Incident(
- date=fields["date"],
- time=fields["time"],
+ occurred_at=fields["occurred_at"],
description=form.data["description"],
department=form.data["department"],
address=fields["address"],
From 0885c30d907adb0fba9ccaa50d9e43e8a0aae564 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 16:44:11 -0500
Subject: [PATCH 11/55] Update test_commands.py
---
OpenOversight/tests/test_commands.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenOversight/tests/test_commands.py b/OpenOversight/tests/test_commands.py
index 82ee7a12c..3de08cb4c 100644
--- a/OpenOversight/tests/test_commands.py
+++ b/OpenOversight/tests/test_commands.py
@@ -868,7 +868,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
report_number="Old_Report_Number",
department_id=1,
description="description",
- time=datetime.time(23, 45, 16),
+ occurred_at=datetime.datetime(2019, 3, 15, 23, 45, 16),
)
incident.officers = [officer]
session.add(incident)
From 1d6d776f68d3c4d58174bf1831dcba788f32d1fd Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:00:37 -0500
Subject: [PATCH 12/55] Update test_commands.py
---
OpenOversight/tests/test_commands.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenOversight/tests/test_commands.py b/OpenOversight/tests/test_commands.py
index 3de08cb4c..7a960fa74 100644
--- a/OpenOversight/tests/test_commands.py
+++ b/OpenOversight/tests/test_commands.py
@@ -991,7 +991,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
assert incident3.report_number == "CR-39283"
assert incident3.description == "Don't know where it happened"
assert incident3.officers == [cop1]
- assert incident3.date == datetime.date(2020, 7, 26)
+ assert incident3.occurred_at == datetime.date(2020, 7, 26)
lp = incident3.license_plates[0]
assert lp.number == "XYZ11"
assert lp.state is None
From e06cf85c11c41b6a075bcb51520be7f782b2c24e Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:00:41 -0500
Subject: [PATCH 13/55] Update test_incidents.py
---
OpenOversight/tests/routes/test_incidents.py | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/OpenOversight/tests/routes/test_incidents.py b/OpenOversight/tests/routes/test_incidents.py
index 9df9f62d3..96680683d 100644
--- a/OpenOversight/tests/routes/test_incidents.py
+++ b/OpenOversight/tests/routes/test_incidents.py
@@ -94,7 +94,7 @@ def test_admins_can_create_basic_incidents(report_number, mockdata, client, sess
assert rv.status_code == HTTPStatus.OK
assert "created" in rv.data.decode(ENCODING_UTF_8)
- inc = Incident.query.filter_by(date=test_date.date()).first()
+ inc = Incident.query.filter_by(occurred_at=test_date).first()
assert inc is not None
@@ -223,8 +223,8 @@ def test_admins_can_edit_incident_links_and_licenses(mockdata, client, session,
ooid_forms = [OOIdForm(ooid=officer.id) for officer in inc.officers]
form = IncidentForm(
- date_field=str(inc.date),
- time_field=str(inc.time),
+ date_field=str(inc.occurred_at.date()),
+ time_field=str(inc.occurred_at.time()),
report_number=inc.report_number,
description=inc.description,
department="1",
@@ -273,7 +273,7 @@ def test_admins_cannot_make_ancient_incidents(mockdata, client, session):
form = IncidentForm(
date_field=date(1899, 12, 5),
- time_field=str(inc.time),
+ time_field=str(inc.occurred_at.time()),
report_number=inc.report_number,
description=inc.description,
department="1",
@@ -408,8 +408,8 @@ def test_admins_can_edit_incident_officers(mockdata, client, session):
new_ooid_form = OOIdForm(oo_id=new_officer.id)
form = IncidentForm(
- date_field=str(inc.date),
- time_field=str(inc.time),
+ date_field=str(inc.occurred_at.date()),
+ time_field=str(inc.occurred_at.time()),
report_number=inc.report_number,
description=inc.description,
department="1",
@@ -464,8 +464,8 @@ def test_admins_cannot_edit_nonexisting_officers(mockdata, client, session):
new_ooid_form = OOIdForm(oo_id="99999999999999999")
form = IncidentForm(
- date_field=str(inc.date),
- time_field=str(inc.time),
+ date_field=str(inc.occurred_at.date()),
+ time_field=str(inc.occurred_at.time()),
report_number=inc.report_number,
description=inc.description,
department="1",
@@ -692,7 +692,7 @@ def test_users_cannot_see_who_created_incidents(mockdata, client, session):
assert "Creator" not in rv.data.decode(ENCODING_UTF_8)
-def test_form_with_officer_id_prepopulates(mockdata, client, session):
+def test_form_with_officer_id_pre_populate(mockdata, client, session):
with current_app.test_request_context():
login_admin(client)
officer_id = "1234"
From 81fa0ab18e34d19bbb18421d96cd078a16e65611 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:00:44 -0500
Subject: [PATCH 14/55] Update views.py
---
OpenOversight/app/main/views.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/OpenOversight/app/main/views.py b/OpenOversight/app/main/views.py
index 7aa72da0b..25709a334 100644
--- a/OpenOversight/app/main/views.py
+++ b/OpenOversight/app/main/views.py
@@ -1591,10 +1591,10 @@ def get_edit_form(self, obj):
form.license_plates.min_entries = no_license_plates
form.links.min_entries = no_links
form.officers.min_entries = no_officers
- if not form.date_field.data and obj.date:
- form.date_field.data = obj.date
- if not form.time_field.data and obj.time:
- form.time_field.data = obj.time
+ if not form.date_field.data and obj.occurred_at:
+ form.date_field.data = obj.occurred_at.date()
+ if not form.time_field.data and obj.occurred_at:
+ form.time_field.data = obj.occurred_at.time()
return form
def populate_obj(self, form, obj):
From 285a2548452e72e080a980a69396f3ab373f735a Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:20:03 -0500
Subject: [PATCH 15/55] Update database_imports.py
---
OpenOversight/app/models/database_imports.py | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/OpenOversight/app/models/database_imports.py b/OpenOversight/app/models/database_imports.py
index ac38ecba7..02a74c1dc 100644
--- a/OpenOversight/app/models/database_imports.py
+++ b/OpenOversight/app/models/database_imports.py
@@ -40,6 +40,12 @@ def parse_date(date_str: Optional[str]) -> Optional["datetime.date"]:
return None
+def parse_date_time(date_time_str: Optional[str]) -> Optional["datetime.datetime"]:
+ if date_time_str:
+ return dateutil.parser.parse(date_time_str)
+ return None
+
+
def parse_time(time_str: Optional[str]) -> Optional["datetime.time"]:
if time_str:
return dateutil.parser.parse(time_str).time()
@@ -274,7 +280,7 @@ def get_or_create_location_from_dict(
def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> Incident:
incident = Incident(
- occurred_at=data,
+ occurred_at=parse_date_time(" ".join([data.get("date"), data.get("time")])),
report_number=parse_str(data.get("report_number"), None),
description=parse_str(data.get("description"), None),
address_id=data.get("address_id"),
@@ -296,9 +302,12 @@ def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> I
def update_incident_from_dict(data: Dict[str, Any], incident: Incident) -> Incident:
if "date" in data:
- incident.date = parse_date(data.get("date"))
- if "time" in data:
- incident.time = parse_time(data.get("time"))
+ if "time" in data:
+ incident.occurred_at = parse_date_time(
+ " ".join([data.get("date"), data.get("time")])
+ )
+ else:
+ incident.occurred_at = parse_date_time(" ".join([data.get("date"), "00:00"]))
if "report_number" in data:
incident.report_number = parse_str(data.get("report_number"), None)
if "description" in data:
From 1a24998633b2a3b934fb9d5013deadfc5d2a658d Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:30:46 -0500
Subject: [PATCH 16/55] Update views.py
---
OpenOversight/app/main/views.py | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/OpenOversight/app/main/views.py b/OpenOversight/app/main/views.py
index 25709a334..e20df1735 100644
--- a/OpenOversight/app/main/views.py
+++ b/OpenOversight/app/main/views.py
@@ -1524,12 +1524,12 @@ def get(self, obj_id):
if occurred_before := request.args.get("occurred_before"):
before_date = datetime.datetime.strptime(occurred_before, "%Y-%m-%d").date()
form.occurred_before.data = before_date
- incidents = incidents.filter(self.model.date < before_date)
+ incidents = incidents.filter(self.model.occurred_at < before_date)
if occurred_after := request.args.get("occurred_after"):
after_date = datetime.datetime.strptime(occurred_after, "%Y-%m-%d").date()
form.occurred_after.data = after_date
- incidents = incidents.filter(self.model.date > after_date)
+ incidents = incidents.filter(self.model.occurred_at > after_date)
incidents = incidents.order_by(
getattr(self.model, self.order_by).desc()
@@ -1632,11 +1632,14 @@ def populate_obj(self, form, obj):
if license_plates and license_plates[0]["number"]:
replace_list(license_plates, obj, "license_plates", LicensePlate, db)
- obj.date = form.date_field.data
if form.time_field.raw_data and form.time_field.raw_data != [""]:
- obj.time = form.time_field.data
+ obj.occurred_at = datetime.datetime(
+ form.date_field.data, form.time_field.data
+ )
else:
- obj.time = None
+ obj.occurred_at = datetime.datetime(
+ form.date_field.data, datetime.time(0, 0)
+ )
super(IncidentApi, self).populate_obj(form, obj)
From a4ad73cf0a27a47b82300b18b2f7219ee8db1f9b Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:36:37 -0500
Subject: [PATCH 17/55] Update views.py
---
OpenOversight/app/main/views.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/OpenOversight/app/main/views.py b/OpenOversight/app/main/views.py
index e20df1735..57096ea3d 100644
--- a/OpenOversight/app/main/views.py
+++ b/OpenOversight/app/main/views.py
@@ -1633,12 +1633,12 @@ def populate_obj(self, form, obj):
replace_list(license_plates, obj, "license_plates", LicensePlate, db)
if form.time_field.raw_data and form.time_field.raw_data != [""]:
- obj.occurred_at = datetime.datetime(
+ obj.occurred_at = datetime.datetime.combine(
form.date_field.data, form.time_field.data
)
else:
- obj.occurred_at = datetime.datetime(
- form.date_field.data, datetime.time(0, 0)
+ obj.occurred_at = datetime.datetime.combine(
+ form.date_field.data.date(), datetime.time(0, 0)
)
super(IncidentApi, self).populate_obj(form, obj)
From 5f5710cfb9a0148c831ed1a6535a309f48313aa4 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:41:25 -0500
Subject: [PATCH 18/55] Update views.py
---
OpenOversight/app/main/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenOversight/app/main/views.py b/OpenOversight/app/main/views.py
index 57096ea3d..8aef4a26d 100644
--- a/OpenOversight/app/main/views.py
+++ b/OpenOversight/app/main/views.py
@@ -1638,7 +1638,7 @@ def populate_obj(self, form, obj):
)
else:
obj.occurred_at = datetime.datetime.combine(
- form.date_field.data.date(), datetime.time(0, 0)
+ form.date_field.data, datetime.time(0, 0)
)
super(IncidentApi, self).populate_obj(form, obj)
From ed1c1f963fb319b53d09ccd6760b8157d3d84d3e Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:42:58 -0500
Subject: [PATCH 19/55] Update test_incidents.py
---
OpenOversight/tests/routes/test_incidents.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/OpenOversight/tests/routes/test_incidents.py b/OpenOversight/tests/routes/test_incidents.py
index 96680683d..bc8b40c19 100644
--- a/OpenOversight/tests/routes/test_incidents.py
+++ b/OpenOversight/tests/routes/test_incidents.py
@@ -189,8 +189,7 @@ def test_admins_can_edit_incident_date_and_address(mockdata, client, session):
assert rv.status_code == HTTPStatus.OK
assert "successfully updated" in rv.data.decode(ENCODING_UTF_8)
updated = Incident.query.get(inc_id)
- assert updated.date == new_date
- assert updated.time == new_time
+ assert updated.occurred_at == datetime.combine(new_date, new_time)
assert updated.address.street_name == street_name
@@ -529,8 +528,7 @@ def test_ac_can_edit_incidents_in_their_department(mockdata, client, session):
)
assert rv.status_code == HTTPStatus.OK
assert "successfully updated" in rv.data.decode(ENCODING_UTF_8)
- assert inc.date == new_date.date()
- assert inc.time == new_date.time()
+ assert inc.occurred_at == new_date
assert inc.address.street_name == street_name
From de0936ce47f35518f2d1b80561a7be4d9fea5f67 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:51:59 -0500
Subject: [PATCH 20/55] Update test_commands.py
---
OpenOversight/tests/test_commands.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/OpenOversight/tests/test_commands.py b/OpenOversight/tests/test_commands.py
index 7a960fa74..bbf24d08b 100644
--- a/OpenOversight/tests/test_commands.py
+++ b/OpenOversight/tests/test_commands.py
@@ -868,7 +868,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
report_number="Old_Report_Number",
department_id=1,
description="description",
- occurred_at=datetime.datetime(2019, 3, 15, 23, 45, 16),
+ occurred_at=datetime.datetime(2020, 7, 26, 23, 45, 16),
)
incident.officers = [officer]
session.add(incident)
@@ -991,7 +991,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
assert incident3.report_number == "CR-39283"
assert incident3.description == "Don't know where it happened"
assert incident3.officers == [cop1]
- assert incident3.occurred_at == datetime.date(2020, 7, 26)
+ assert incident3.occurred_at == datetime.datetime(2020, 7, 26, 0, 0)
lp = incident3.license_plates[0]
assert lp.number == "XYZ11"
assert lp.state is None
From 2424c0dfee03794222b7a23b054bb46fba18f84e Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 17:52:06 -0500
Subject: [PATCH 21/55] Update forms.py
---
OpenOversight/app/utils/forms.py | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/OpenOversight/app/utils/forms.py b/OpenOversight/app/utils/forms.py
index ffafa262a..0446d6fbd 100644
--- a/OpenOversight/app/utils/forms.py
+++ b/OpenOversight/app/utils/forms.py
@@ -134,12 +134,8 @@ def create_description(self, form):
def create_incident(self, form):
fields = {
- "occurred_at": datetime.datetime(
- form.date_field.data.year,
- form.date_field.data.month,
- form.date_field.data.day,
- form.time_field.data.hour,
- form.time_field.data.minute,
+ "occurred_at": datetime.datetime.combine(
+ form.date_field.data, form.time_field.data
),
"officers": [],
"license_plates": [],
From 9c061dc89e1a180fcc6cf11fc60eceaab0b4af86 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 18:00:25 -0500
Subject: [PATCH 22/55] Update downloads.py
---
OpenOversight/app/main/downloads.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/OpenOversight/app/main/downloads.py b/OpenOversight/app/main/downloads.py
index 599ebfe28..47d63fef6 100644
--- a/OpenOversight/app/main/downloads.py
+++ b/OpenOversight/app/main/downloads.py
@@ -132,8 +132,7 @@ def incidents_record_maker(incident: Incident) -> _Record:
return {
"id": incident.id,
"report_num": incident.report_number,
- "date": incident.date,
- "time": incident.time,
+ "occurred_at": incident.occurred_at,
"description": incident.description,
"location": incident.address,
"licenses": " ".join(map(str, incident.license_plates)),
From 55d949cd652841511d79fd4ceb16dcb2baf26c8a Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 18:00:28 -0500
Subject: [PATCH 23/55] Update views.py
---
OpenOversight/app/main/views.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/OpenOversight/app/main/views.py b/OpenOversight/app/main/views.py
index 8aef4a26d..ab798f956 100644
--- a/OpenOversight/app/main/views.py
+++ b/OpenOversight/app/main/views.py
@@ -1289,8 +1289,7 @@ def download_incidents_csv(department_id):
field_names = [
"id",
"report_num",
- "date",
- "time",
+ "occurred_at",
"description",
"location",
"licenses",
From 893fb36414a37daf515e3caa1c6e29768da43199 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 18:03:59 -0500
Subject: [PATCH 24/55] Update database_imports.py
---
OpenOversight/app/models/database_imports.py | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/OpenOversight/app/models/database_imports.py b/OpenOversight/app/models/database_imports.py
index 02a74c1dc..ccba60ef1 100644
--- a/OpenOversight/app/models/database_imports.py
+++ b/OpenOversight/app/models/database_imports.py
@@ -42,7 +42,9 @@ def parse_date(date_str: Optional[str]) -> Optional["datetime.date"]:
def parse_date_time(date_time_str: Optional[str]) -> Optional["datetime.datetime"]:
if date_time_str:
- return dateutil.parser.parse(date_time_str)
+ return datetime.datetime.combine(
+ parse_date(date_time_str), parse_time(date_time_str)
+ )
return None
@@ -279,6 +281,9 @@ def get_or_create_location_from_dict(
def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> Incident:
+ print('!!!!!"')
+ print(data.get("date"))
+ print(data.get("time"))
incident = Incident(
occurred_at=parse_date_time(" ".join([data.get("date"), data.get("time")])),
report_number=parse_str(data.get("report_number"), None),
@@ -302,12 +307,17 @@ def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> I
def update_incident_from_dict(data: Dict[str, Any], incident: Incident) -> Incident:
if "date" in data:
+ print("!!!!!")
if "time" in data:
+ print(" ".join([data.get("date"), data.get("time")]))
incident.occurred_at = parse_date_time(
" ".join([data.get("date"), data.get("time")])
)
else:
- incident.occurred_at = parse_date_time(" ".join([data.get("date"), "00:00"]))
+ print(" ".join([data.get("date"), "00:00"]))
+ incident.occurred_at = parse_date_time(
+ " ".join([data.get("date"), "00:00"])
+ )
if "report_number" in data:
incident.report_number = parse_str(data.get("report_number"), None)
if "description" in data:
From 7856910adb0af3265228b7db131474c759b65c0f Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Thu, 27 Jul 2023 22:30:29 -0500
Subject: [PATCH 25/55] Update database_imports.py
---
OpenOversight/app/models/database_imports.py | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/OpenOversight/app/models/database_imports.py b/OpenOversight/app/models/database_imports.py
index ccba60ef1..72d593872 100644
--- a/OpenOversight/app/models/database_imports.py
+++ b/OpenOversight/app/models/database_imports.py
@@ -308,16 +308,9 @@ def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> I
def update_incident_from_dict(data: Dict[str, Any], incident: Incident) -> Incident:
if "date" in data:
print("!!!!!")
- if "time" in data:
- print(" ".join([data.get("date"), data.get("time")]))
- incident.occurred_at = parse_date_time(
- " ".join([data.get("date"), data.get("time")])
- )
- else:
- print(" ".join([data.get("date"), "00:00"]))
- incident.occurred_at = parse_date_time(
- " ".join([data.get("date"), "00:00"])
- )
+ incident.occurred_at = parse_date_time(
+ " ".join([data.get("date"), data.get("time", "00:00")])
+ )
if "report_number" in data:
incident.report_number = parse_str(data.get("report_number"), None)
if "description" in data:
From d7247ecf8d802bb6b17a71fc712c524d32876cc7 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 28 Jul 2023 11:46:21 -0500
Subject: [PATCH 26/55] Update database_imports.py
---
OpenOversight/app/models/database_imports.py | 28 +++++++++-----------
1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/OpenOversight/app/models/database_imports.py b/OpenOversight/app/models/database_imports.py
index 72d593872..e4fd6ca21 100644
--- a/OpenOversight/app/models/database_imports.py
+++ b/OpenOversight/app/models/database_imports.py
@@ -1,4 +1,5 @@
-from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Tuple, Union
+from datetime import date, datetime, time
+from typing import Any, Dict, Optional, Sequence, Tuple, Union
import dateutil.parser
@@ -19,10 +20,6 @@
from OpenOversight.app.validators import state_validator, url_validator
-if TYPE_CHECKING:
- import datetime
-
-
def validate_choice(
value: Optional[str], given_choices: Sequence[Tuple[str, str]]
) -> Optional[str]:
@@ -34,21 +31,19 @@ def validate_choice(
return None
-def parse_date(date_str: Optional[str]) -> Optional["datetime.date"]:
+def parse_date(date_str: Optional[str]) -> Optional[date]:
if date_str:
return dateutil.parser.parse(date_str).date()
return None
-def parse_date_time(date_time_str: Optional[str]) -> Optional["datetime.datetime"]:
+def parse_date_time(date_time_str: Optional[str]) -> Optional[datetime]:
if date_time_str:
- return datetime.datetime.combine(
- parse_date(date_time_str), parse_time(date_time_str)
- )
+ return datetime.combine(parse_date(date_time_str), parse_time(date_time_str))
return None
-def parse_time(time_str: Optional[str]) -> Optional["datetime.time"]:
+def parse_time(time_str: Optional[str]) -> Optional[time]:
if time_str:
return dateutil.parser.parse(time_str).time()
return None
@@ -281,11 +276,10 @@ def get_or_create_location_from_dict(
def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> Incident:
- print('!!!!!"')
+ print("+++++++")
print(data.get("date"))
- print(data.get("time"))
+ print(data.get("time", "00:00"))
incident = Incident(
- occurred_at=parse_date_time(" ".join([data.get("date"), data.get("time")])),
report_number=parse_str(data.get("report_number"), None),
description=parse_str(data.get("description"), None),
address_id=data.get("address_id"),
@@ -294,6 +288,11 @@ def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> I
last_updated_id=parse_int(data.get("last_updated_id")),
)
+ if "date" in data:
+ incident.occurred_at = parse_date_time(
+ " ".join([data.get("date"), data.get("time", "00:00")])
+ )
+
incident.officers = data.get("officers", [])
incident.license_plates = data.get("license_plate_objects", [])
@@ -307,7 +306,6 @@ def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> I
def update_incident_from_dict(data: Dict[str, Any], incident: Incident) -> Incident:
if "date" in data:
- print("!!!!!")
incident.occurred_at = parse_date_time(
" ".join([data.get("date"), data.get("time", "00:00")])
)
From 8c1ee4a793e65b647d0f13e1ea079e343545a1d0 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 28 Jul 2023 11:58:21 -0500
Subject: [PATCH 27/55] Update test_commands.py
---
OpenOversight/tests/test_commands.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/OpenOversight/tests/test_commands.py b/OpenOversight/tests/test_commands.py
index bbf24d08b..4f039e556 100644
--- a/OpenOversight/tests/test_commands.py
+++ b/OpenOversight/tests/test_commands.py
@@ -1087,6 +1087,7 @@ def test_advanced_csv_import__force_create(session, department, tmp_path):
incidents_data = [
{
"id": 66001,
+ "date": "2021-08-12",
"officer_ids": "99002|99001",
"department_name": department.name,
"department_state": department.state,
From 4c42df1510f952b360c0b9d0db0cbbac579e191e Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 28 Jul 2023 11:58:45 -0500
Subject: [PATCH 28/55] Update csv_imports.py
---
OpenOversight/app/csv_imports.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/OpenOversight/app/csv_imports.py b/OpenOversight/app/csv_imports.py
index 7fc86a828..93ac6a808 100644
--- a/OpenOversight/app/csv_imports.py
+++ b/OpenOversight/app/csv_imports.py
@@ -364,9 +364,8 @@ def _handle_incidents_csv(
with _csv_reader(incidents_csv) as csv_reader:
_check_provided_fields(
csv_reader,
- required_fields=["id", "department_name", "department_state"],
+ required_fields=["id", "department_name", "department_state", "date"],
optional_fields=[
- "date",
"time",
"report_number",
"description",
From 213f94f76862dafa71a39dc00223ef6197f8529e Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 28 Jul 2023 11:58:50 -0500
Subject: [PATCH 29/55] Update incidents.csv
---
OpenOversight/tests/test_csvs/incidents.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenOversight/tests/test_csvs/incidents.csv b/OpenOversight/tests/test_csvs/incidents.csv
index 45c9e803a..db81769b5 100644
--- a/OpenOversight/tests/test_csvs/incidents.csv
+++ b/OpenOversight/tests/test_csvs/incidents.csv
@@ -1,4 +1,4 @@
id,department name,department state,date,time,report number,description,street name,cross street1,cross street2,city,state,zip code,license plates,officer_ids,creator id,last updated id
,Springfield Police Department,IL,2020-07-20,06:30,CR-1234,Something happened,,East Ave,Main St,Chicago,IL,60603,ABC123_NY|98UMC_IL,#1|49483,,
-#I1,Springfield Police Department,IL,,,CR-9912,Something happened,Fake Street,Main Street,,Chicago,IL,60603,,#1,,
+#I1,Springfield Police Department,IL,2019-08-12,,CR-9912,Something happened,Fake Street,Main Street,,Chicago,IL,60603,,#1,,
123456,Springfield Police Department,IL,2020-07-26,,CR-39283,Don't know where it happened,,,,,,,XYZ11,#1,,
From 26bec47eccde29ad64e8f3418be12d8100bbd431 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 28 Jul 2023 11:58:55 -0500
Subject: [PATCH 30/55] Update database_imports.py
---
OpenOversight/app/models/database_imports.py | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/OpenOversight/app/models/database_imports.py b/OpenOversight/app/models/database_imports.py
index e4fd6ca21..f9d0f38db 100644
--- a/OpenOversight/app/models/database_imports.py
+++ b/OpenOversight/app/models/database_imports.py
@@ -276,10 +276,10 @@ def get_or_create_location_from_dict(
def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> Incident:
- print("+++++++")
- print(data.get("date"))
- print(data.get("time", "00:00"))
incident = Incident(
+ occurred_at=parse_date_time(
+ " ".join([data.get("date"), data.get("time", "00:00")])
+ ),
report_number=parse_str(data.get("report_number"), None),
description=parse_str(data.get("description"), None),
address_id=data.get("address_id"),
@@ -288,11 +288,6 @@ def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> I
last_updated_id=parse_int(data.get("last_updated_id")),
)
- if "date" in data:
- incident.occurred_at = parse_date_time(
- " ".join([data.get("date"), data.get("time", "00:00")])
- )
-
incident.officers = data.get("officers", [])
incident.license_plates = data.get("license_plate_objects", [])
From 23cebb55d983885f9f70c8d80ef24d5d3d4ad976 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 28 Jul 2023 12:12:01 -0500
Subject: [PATCH 31/55] Update route_helpers.py
---
OpenOversight/tests/routes/route_helpers.py | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/OpenOversight/tests/routes/route_helpers.py b/OpenOversight/tests/routes/route_helpers.py
index b6f6f08be..7459a0d71 100644
--- a/OpenOversight/tests/routes/route_helpers.py
+++ b/OpenOversight/tests/routes/route_helpers.py
@@ -45,17 +45,15 @@ def login_ac(client):
def process_form_data(form_dict):
- """Takes the dict from a form with embedded formd and flattens it
-
- in the way that it is flattened in the browser"""
+ """Mock the browser-flattening of a form containing embedded data."""
new_dict = {}
for key, value in form_dict.items():
if type(value) == list:
if value[0]:
if type(value[0]) is dict:
for idx, item in enumerate(value):
- for subkey, subvalue in item.items():
- new_dict[f"{key}-{idx}-{subkey}"] = subvalue
+ for sub_key, sub_value in item.items():
+ new_dict[f"{key}-{idx}-{sub_key}"] = sub_value
elif type(value[0]) is str or type(value[0]) is int:
for idx, item in enumerate(value):
new_dict[f"{key}-{idx}"] = item
@@ -66,8 +64,8 @@ def process_form_data(form_dict):
)
)
elif type(value) == dict:
- for subkey, subvalue in value.items():
- new_dict[f"{key}-{subkey}"] = subvalue
+ for sub_key, sub_value in value.items():
+ new_dict[f"{key}-{sub_key}"] = sub_value
else:
new_dict[key] = value
From 63024a5e17e78c09fdc790070c9dede0cf59b4c9 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 28 Jul 2023 13:05:21 -0500
Subject: [PATCH 32/55] Update advanced_csv_import.rst
---
docs/advanced_csv_import.rst | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/advanced_csv_import.rst b/docs/advanced_csv_import.rst
index 665b941c5..df26a0a93 100644
--- a/docs/advanced_csv_import.rst
+++ b/docs/advanced_csv_import.rst
@@ -148,8 +148,8 @@ Details:
Incidents csv
^^^^^^^^^^^^^
-- Required: ``id, department_name, department_state``
-- Optional: ``date, time, report_number, description, street_name, cross_street1, cross_street2, city, state, zip_code,
+- Required: ``id, department_name, department_state, date``
+- Optional: ``time, report_number, description, street_name, cross_street1, cross_street2, city, state, zip_code,
creator_id, last_updated_id, officer_ids, license_plates``
Details:
@@ -159,7 +159,7 @@ Details:
- ``department_state`` Name of department state exactly as it is in the server database, which will be the
`standard two-letter abbreviation `_ for the department's respective location.
- ``date`` :ref:`Date ` of the incident
-- ``time`` :ref:`Time ` of the incident
+- ``time`` :ref:`Time ` of the incident. If this field is left blank, it will be defaulted to midnight of that day.
- ``report_number`` String representing any kind of number assigned to complaints or incidents by the police department.
- ``description`` Text description of the incident.
- ``street_name`` Name of the street the incident occurred, but should not include the street number.
From e6063460410d6e755e9433a2f132111a8e5bdaaa Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 28 Jul 2023 13:13:58 -0500
Subject: [PATCH 33/55] Update test_commands.py
---
OpenOversight/tests/test_commands.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/OpenOversight/tests/test_commands.py b/OpenOversight/tests/test_commands.py
index 4f039e556..9aeb15044 100644
--- a/OpenOversight/tests/test_commands.py
+++ b/OpenOversight/tests/test_commands.py
@@ -996,7 +996,6 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
assert lp.number == "XYZ11"
assert lp.state is None
assert incident3.address is None
- assert incident3.time is None
link_new = cop4.links[0]
assert [link_new] == list(cop1.links)
From eeb9e560e4f0ab41e073de722e48c6c719098fd2 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 28 Jul 2023 13:19:57 -0500
Subject: [PATCH 34/55] Update test_incidents.py
---
OpenOversight/tests/routes/test_incidents.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/OpenOversight/tests/routes/test_incidents.py b/OpenOversight/tests/routes/test_incidents.py
index bc8b40c19..4733376aa 100644
--- a/OpenOversight/tests/routes/test_incidents.py
+++ b/OpenOversight/tests/routes/test_incidents.py
@@ -735,8 +735,8 @@ def test_admins_cannot_inject_unsafe_html(mockdata, client, session):
ooid_forms = [OOIdForm(oo_id=the_id) for the_id in old_officer_ids]
form = IncidentForm(
- date_field=str(inc.date),
- time_field=str(inc.time),
+ date_field=str(inc.occurred_at.date()),
+ time_field=str(inc.occurred_at.time()),
report_number=inc.report_number,
description="",
department="1",
From e2ef3d1a5398953aa9e0805139cb5ffbb511c2d2 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Sun, 6 Aug 2023 16:00:44 -0500
Subject: [PATCH 35/55] Update test_incidents.py
---
OpenOversight/tests/routes/test_incidents.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/OpenOversight/tests/routes/test_incidents.py b/OpenOversight/tests/routes/test_incidents.py
index 37bd385dc..147fc67a3 100644
--- a/OpenOversight/tests/routes/test_incidents.py
+++ b/OpenOversight/tests/routes/test_incidents.py
@@ -200,7 +200,7 @@ def test_admins_can_edit_incident_date_and_address(mockdata, client, session):
assert rv.status_code == HTTPStatus.OK
assert "successfully updated" in rv.data.decode(ENCODING_UTF_8)
updated = Incident.query.get(inc_id)
- assert updated.occurred_at == datetime.combine(new_date, new_time)
+ assert updated.occurred_at == datetime.combine(test_date, test_time)
assert updated.address.street_name == street_name
@@ -566,7 +566,7 @@ def test_ac_can_edit_incidents_in_their_department(mockdata, client, session):
)
assert rv.status_code == HTTPStatus.OK
assert "successfully updated" in rv.data.decode(ENCODING_UTF_8)
- assert inc.occurred_at == new_date
+ assert inc.occurred_at == test_date
assert inc.address.street_name == street_name
From 9fb83a561f5b583ad27a5aaa3a51d6b4b6a25d95 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Sun, 6 Aug 2023 23:18:50 -0500
Subject: [PATCH 36/55] Update
2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py
---
...-2316_9fa948bcea25_change_incident_date_and_time_to_.py} | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
rename OpenOversight/migrations/versions/{2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py => 2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py} (94%)
diff --git a/OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py b/OpenOversight/migrations/versions/2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py
similarity index 94%
rename from OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
rename to OpenOversight/migrations/versions/2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py
index 9804b89ee..6e9046fea 100644
--- a/OpenOversight/migrations/versions/2023-07-27-1848_9fa948bcea25_change_incident_date_and_time_to_.py
+++ b/OpenOversight/migrations/versions/2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py
@@ -1,8 +1,8 @@
"""change incident date and time to timestamptz
Revision ID: 9fa948bcea25
-Revises: 18f43ac4622f
-Create Date: 2023-07-27 18:48:58.819477
+Revises: b38c133bed3c
+Create Date: 2023-08-06 23:16:00.819477
"""
import os
@@ -14,7 +14,7 @@
# revision identifiers, used by Alembic.
revision = "9fa948bcea25"
-down_revision = "18f43ac4622f"
+down_revision = "b38c133bed3c"
branch_labels = None
depends_on = None
From 4e5588a3ac3dc32a3e6edf7558c535563e8e7179 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Sun, 6 Aug 2023 23:24:24 -0500
Subject: [PATCH 37/55] Update general.py
---
OpenOversight/app/utils/general.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/OpenOversight/app/utils/general.py b/OpenOversight/app/utils/general.py
index 1743ca930..75565bdcf 100644
--- a/OpenOversight/app/utils/general.py
+++ b/OpenOversight/app/utils/general.py
@@ -1,5 +1,6 @@
import random
import sys
+from datetime import datetime, timezone
from distutils.util import strtobool
from typing import Optional
from urllib.parse import urlparse
@@ -70,6 +71,14 @@ def get_random_image(image_query):
return None
+def get_utc_datetime(dt: datetime = None) -> datetime:
+ """Return the current datetime in UTC or the converted given datetime to UTC."""
+ if dt:
+ return datetime.now(tz=timezone.utc)
+ else:
+ return dt.replace(tzinfo=timezone.utc)
+
+
def merge_dicts(*dict_args):
"""
Given any number of dicts, shallow copy and merge into a new dict,
From 8d68b8f8167f043d2dd16781fed062395d63a9cb Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Sun, 6 Aug 2023 23:24:30 -0500
Subject: [PATCH 38/55] Update views.py
---
OpenOversight/app/main/views.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/OpenOversight/app/main/views.py b/OpenOversight/app/main/views.py
index ed87c4a85..ffc77e772 100644
--- a/OpenOversight/app/main/views.py
+++ b/OpenOversight/app/main/views.py
@@ -102,6 +102,7 @@
allowed_file,
get_or_create,
get_random_image,
+ get_utc_datetime,
replace_list,
serve_image,
validate_redirect_url,
@@ -1634,12 +1635,12 @@ def populate_obj(self, form, obj):
replace_list(license_plates, obj, "license_plates", LicensePlate, db)
if form.time_field.raw_data and form.time_field.raw_data != [""]:
- obj.occurred_at = datetime.datetime.combine(
- form.date_field.data, form.time_field.data
+ obj.occurred_at = get_utc_datetime(
+ datetime.datetime.combine(form.date_field.data, form.time_field.data)
)
else:
- obj.occurred_at = datetime.datetime.combine(
- form.date_field.data, datetime.time(0, 0)
+ obj.occurred_at = get_utc_datetime(
+ datetime.datetime.combine(form.date_field.data, datetime.time(0, 0))
)
super(IncidentApi, self).populate_obj(form, obj)
From d12759dc6263ca19666c0d63b053e1340728595d Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 00:07:36 -0500
Subject: [PATCH 39/55] Update test_commands.py
---
OpenOversight/tests/test_commands.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/OpenOversight/tests/test_commands.py b/OpenOversight/tests/test_commands.py
index 9ed578a1c..207d6258c 100644
--- a/OpenOversight/tests/test_commands.py
+++ b/OpenOversight/tests/test_commands.py
@@ -1,10 +1,10 @@
import csv
-import datetime
import operator
import os
import random
import traceback
import uuid
+from datetime import date, datetime
import pandas as pd
import pytest
@@ -857,7 +857,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
id=77021,
officer_id=officer.id,
star_no="4567",
- start_date=datetime.date(2020, 1, 1),
+ start_date=date(2020, 1, 1),
job_id=department.jobs[0].id,
created_by=user.id,
)
@@ -878,7 +878,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
report_number="Old_Report_Number",
department_id=1,
description="description",
- occurred_at=datetime.datetime(2020, 7, 26, 23, 45, 16),
+ occurred_at=datetime(2020, 7, 26, 23, 45, 16),
created_by=user.id,
)
incident.officers = [officer]
@@ -1006,7 +1006,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
assert incident3.report_number == "CR-39283"
assert incident3.description == "Don't know where it happened"
assert incident3.officers == [cop1]
- assert incident3.occurred_at == datetime.datetime(2020, 7, 26, 0, 0)
+ assert incident3.occurred_at == datetime(2020, 7, 26, 0, 0)
lp = incident3.license_plates[0]
assert lp.number == "XYZ11"
assert lp.state is None
From d138618f907d9f5a342ac7ddc84dbc482f1ed296 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 00:22:12 -0500
Subject: [PATCH 40/55] Update database_imports.py
---
OpenOversight/app/models/database_imports.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/OpenOversight/app/models/database_imports.py b/OpenOversight/app/models/database_imports.py
index 6801fbb89..abb26cacf 100644
--- a/OpenOversight/app/models/database_imports.py
+++ b/OpenOversight/app/models/database_imports.py
@@ -21,7 +21,7 @@
RACE_CHOICES,
SUFFIX_CHOICES,
)
-from OpenOversight.app.utils.general import get_or_create, str_is_true
+from OpenOversight.app.utils.general import get_or_create, get_utc_datetime, str_is_true
from OpenOversight.app.validators import state_validator, url_validator
@@ -282,8 +282,8 @@ def get_or_create_location_from_dict(
def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> Incident:
incident = Incident(
- occurred_at=parse_date_time(
- " ".join([data.get("date"), data.get("time", "00:00")])
+ occurred_at=get_utc_datetime(
+ parse_date_time(" ".join([data.get("date"), data.get("time", "00:00")]))
),
report_number=parse_str(data.get("report_number"), None),
description=parse_str(data.get("description"), None),
@@ -291,7 +291,7 @@ def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> I
department_id=parse_int(data.get("department_id")),
created_by=parse_int(data.get("created_by")),
last_updated_by=parse_int(data.get("last_updated_by")),
- last_updated_at=datetime.datetime.now(),
+ last_updated_at=datetime.now(),
)
incident.officers = data.get("officers", [])
@@ -307,8 +307,8 @@ def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> I
def update_incident_from_dict(data: Dict[str, Any], incident: Incident) -> Incident:
if "date" in data:
- incident.occurred_at = parse_date_time(
- " ".join([data.get("date"), data.get("time", "00:00")])
+ incident.occurred_at = get_utc_datetime(
+ parse_date_time(" ".join([data.get("date"), data.get("time", "00:00")]))
)
if "report_number" in data:
incident.report_number = parse_str(data.get("report_number"), None)
@@ -322,7 +322,7 @@ def update_incident_from_dict(data: Dict[str, Any], incident: Incident) -> Incid
incident.created_by = parse_int(data.get("created_by"))
if "last_updated_by" in data:
incident.last_updated_by = parse_int(data.get("last_updated_by"))
- incident.last_updated_at = datetime.datetime.now()
+ incident.last_updated_at = datetime.now()
if "officers" in data:
incident.officers = data["officers"] or []
if "license_plate_objects" in data:
From 1f8417030be04e080fead307faae0b41105eeb1d Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 00:22:18 -0500
Subject: [PATCH 41/55] Update forms.py
---
OpenOversight/app/utils/forms.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/OpenOversight/app/utils/forms.py b/OpenOversight/app/utils/forms.py
index 4ca4bf6ee..49b1a29ad 100644
--- a/OpenOversight/app/utils/forms.py
+++ b/OpenOversight/app/utils/forms.py
@@ -20,7 +20,7 @@
db,
)
from OpenOversight.app.utils.choices import GENDER_CHOICES, RACE_CHOICES
-from OpenOversight.app.utils.general import get_or_create
+from OpenOversight.app.utils.general import get_or_create, get_utc_datetime
def add_new_assignment(officer_id, form):
@@ -134,8 +134,8 @@ def create_description(self, form):
def create_incident(self, form):
fields = {
- "occurred_at": datetime.datetime.combine(
- form.date_field.data, form.time_field.data
+ "occurred_at": get_utc_datetime(
+ datetime.datetime.combine(form.date_field.data, form.time_field.data)
),
"officers": [],
"license_plates": [],
From 4502e2895c3da02a75060c81de388c84df3b5303 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 00:22:24 -0500
Subject: [PATCH 42/55] Update test_commands.py
---
OpenOversight/tests/test_commands.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/OpenOversight/tests/test_commands.py b/OpenOversight/tests/test_commands.py
index 207d6258c..0fda8c993 100644
--- a/OpenOversight/tests/test_commands.py
+++ b/OpenOversight/tests/test_commands.py
@@ -926,7 +926,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
assert cop1.last_name == "Smith"
assert cop1.gender == "M"
assert cop1.race == "WHITE"
- assert cop1.employment_date == datetime.date(2019, 7, 12)
+ assert cop1.employment_date == date(2019, 7, 12)
assert cop1.birth_year == 1984
assert cop1.middle_initial == "O"
assert cop1.suffix is None
@@ -942,8 +942,8 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
cop1.assignments, key=operator.attrgetter("start_date")
)
assert assignment_po.star_no == "1234"
- assert assignment_po.start_date == datetime.date(2019, 7, 12)
- assert assignment_po.resign_date == datetime.date(2020, 1, 1)
+ assert assignment_po.start_date == date(2019, 7, 12)
+ assert assignment_po.resign_date == date(2020, 1, 1)
assert assignment_po.job.job_title == "Police Officer"
assert assignment_po.unit_id is None
@@ -980,10 +980,10 @@ def test_advanced_csv_import__success(session, department, test_csv_dir):
cop4.assignments, key=operator.attrgetter("start_date")
)
assert updated_assignment.job.job_title == "Police Officer"
- assert updated_assignment.resign_date == datetime.date(2020, 7, 10)
+ assert updated_assignment.resign_date == date(2020, 7, 10)
assert updated_assignment.star_no == "4567"
assert new_assignment.job.job_title == "Captain"
- assert new_assignment.start_date == datetime.date(2020, 7, 10)
+ assert new_assignment.start_date == date(2020, 7, 10)
assert new_assignment.star_no == "54321"
incident = cop4.incidents[0]
From b2c8b9deabd7a5f858e17402c876dc696585b6e4 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 00:31:11 -0500
Subject: [PATCH 43/55] Update general.py
---
OpenOversight/app/utils/general.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/OpenOversight/app/utils/general.py b/OpenOversight/app/utils/general.py
index 75565bdcf..6982bbe38 100644
--- a/OpenOversight/app/utils/general.py
+++ b/OpenOversight/app/utils/general.py
@@ -73,10 +73,10 @@ def get_random_image(image_query):
def get_utc_datetime(dt: datetime = None) -> datetime:
"""Return the current datetime in UTC or the converted given datetime to UTC."""
- if dt:
- return datetime.now(tz=timezone.utc)
- else:
+ if dt is not None:
return dt.replace(tzinfo=timezone.utc)
+ else:
+ return datetime.utcnow()
def merge_dicts(*dict_args):
From 50deaaf1df118c4dabf87fc0a612e7fe6309df4e Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 00:31:13 -0500
Subject: [PATCH 44/55] Update test_utils.py
---
OpenOversight/tests/test_utils.py | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/OpenOversight/tests/test_utils.py b/OpenOversight/tests/test_utils.py
index 5f70242c2..b7fa0c669 100644
--- a/OpenOversight/tests/test_utils.py
+++ b/OpenOversight/tests/test_utils.py
@@ -1,6 +1,8 @@
+from datetime import datetime, timedelta, timezone
from io import BytesIO
import pytest
+import pytz
from flask import current_app
from flask_login import current_user
from mock import MagicMock, Mock, patch
@@ -12,9 +14,14 @@
upload_image_to_s3_and_store_in_db,
upload_obj_to_s3,
)
+from OpenOversight.app.utils.constants import KEY_TIMEZONE
from OpenOversight.app.utils.db import unit_choices
from OpenOversight.app.utils.forms import filter_by_form, grab_officers
-from OpenOversight.app.utils.general import allowed_file, validate_redirect_url
+from OpenOversight.app.utils.general import (
+ allowed_file,
+ get_utc_datetime,
+ validate_redirect_url,
+)
from OpenOversight.tests.routes.route_helpers import login_user
@@ -74,6 +81,22 @@ def test_gender_filter_include_all_genders_if_not_sure(mockdata):
assert results.count() == len(department.officers)
+def test_get_utc_datetime():
+ utc_now = datetime.utcnow()
+ test_utc_now = get_utc_datetime()
+ assert (test_utc_now - utc_now).total_seconds() < 0.5
+
+ with current_app.test_request_context():
+ server_timezone = pytz.timezone(current_app.config[KEY_TIMEZONE])
+ local = server_timezone.localize(datetime.now() + timedelta(days=10, hours=3))
+ test_local_to_utc = get_utc_datetime(local)
+ test = (local - test_local_to_utc).total_seconds()
+ correct = (
+ datetime.now(tz=timezone.utc) - datetime.now().astimezone(tz=server_timezone)
+ ).total_seconds()
+ assert test == correct
+
+
def test_rank_filter_select_all_commanders(mockdata):
department = Department.query.first()
results = grab_officers({"rank": ["Commander"], "dept": department})
From 39229d4c9f6256ade053398b629da0f32b781f60 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 15:52:35 -0500
Subject: [PATCH 45/55] Delete
2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py
---
...cea25_change_incident_date_and_time_to_.py | 55 -------------------
1 file changed, 55 deletions(-)
delete mode 100644 OpenOversight/migrations/versions/2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py
diff --git a/OpenOversight/migrations/versions/2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py b/OpenOversight/migrations/versions/2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py
deleted file mode 100644
index 6e9046fea..000000000
--- a/OpenOversight/migrations/versions/2023-08-06-2316_9fa948bcea25_change_incident_date_and_time_to_.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""change incident date and time to timestamptz
-
-Revision ID: 9fa948bcea25
-Revises: b38c133bed3c
-Create Date: 2023-08-06 23:16:00.819477
-
-"""
-import os
-
-import sqlalchemy as sa
-from alembic import op
-from sqlalchemy.dialects import postgresql
-
-
-# revision identifiers, used by Alembic.
-revision = "9fa948bcea25"
-down_revision = "b38c133bed3c"
-branch_labels = None
-depends_on = None
-
-TIMEZONE = os.getenv("TIMEZONE", "America/Chicago")
-
-
-def upgrade():
- op.add_column(
- "incidents", sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=True)
- )
- op.execute(
- f"""
- UPDATE incidents
- SET occurred_at = (date::date || ' ' || COALESCE(time,
- '00:00:00')::timetz)::timestamp AT TIME ZONE '{TIMEZONE}'
- WHERE occurred_at IS NULL
- """
- )
- op.alter_column("incidents", "occurred_at", nullable=False)
- op.drop_index("ix_incidents_date")
- op.drop_index("ix_incidents_time")
- op.drop_column("incidents", "time")
- op.drop_column("incidents", "date")
-
-
-def downgrade():
- op.add_column("incidents", sa.Column("date", sa.DATE(), nullable=True))
- op.add_column("incidents", sa.Column("time", postgresql.TIME(), nullable=True))
- op.execute(
- f"""
- UPDATE incidents
- SET (date, time) = (occurred_at::date, (occurred_at::timestamptz AT TIME ZONE '{TIMEZONE}')::time)
- """
- )
-
- op.create_index(op.f("ix_incidents_time"), "incidents", ["time"], unique=False)
- op.create_index(op.f("ix_incidents_date"), "incidents", ["date"], unique=False)
- op.drop_column("incidents", "occurred_at")
From 0d8f0bb123dddeedc6ecd5567b22d92bc7c02b5f Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 16:24:21 -0500
Subject: [PATCH 46/55] Update database.py
---
OpenOversight/app/models/database.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/OpenOversight/app/models/database.py b/OpenOversight/app/models/database.py
index 571176d4c..1d3b32989 100644
--- a/OpenOversight/app/models/database.py
+++ b/OpenOversight/app/models/database.py
@@ -633,7 +633,8 @@ class Incident(BaseModel):
__tablename__ = "incidents"
id = db.Column(db.Integer, primary_key=True)
- occurred_at = db.Column(db.DateTime(timezone=True), unique=False, nullable=False)
+ date = db.Column(db.Date, unique=False, index=True)
+ occurred_at = db.Column(db.DateTime(timezone=True), unique=False, nullable=True, index=True)
report_number = db.Column(db.String(50), index=True)
description = db.Column(db.Text(), nullable=True)
address_id = db.Column(db.Integer, db.ForeignKey("locations.id"))
From 593b0b990d4778326ae26ab113b107eef7f11193 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 16:24:24 -0500
Subject: [PATCH 47/55] Create
2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
---
...99be2696a9_add_occurred_at_to_incidents.py | 64 +++++++++++++++++++
1 file changed, 64 insertions(+)
create mode 100644 OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
diff --git a/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py b/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
new file mode 100644
index 000000000..bfdac0996
--- /dev/null
+++ b/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
@@ -0,0 +1,64 @@
+"""add occurred_at to incidents
+
+Revision ID: 2b99be2696a9
+Revises: b38c133bed3c
+Create Date: 2023-08-07 21:14:31.711553
+
+"""
+import os
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '2b99be2696a9'
+down_revision = 'b38c133bed3c'
+branch_labels = None
+depends_on = None
+
+
+TIMEZONE = os.getenv("TIMEZONE", "America/Chicago")
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('incidents', schema=None) as batch_op:
+ batch_op.add_column(sa.Column('occurred_at', sa.DateTime(timezone=True), nullable=True))
+ batch_op.create_index(batch_op.f('ix_incidents_occurred_at'), ['occurred_at'], unique=False)
+
+ op.execute(
+ f"""
+ UPDATE incidents
+ SET occurred_at = (date::date || ' ' || time::timetz)::timestamp AT TIME ZONE '{TIMEZONE}'
+ WHERE occurred_at IS NULL
+ AND time IS NOT NULL
+ AND date IS NOT NULL
+ """
+ )
+
+ with op.batch_alter_table('incidents', schema=None) as batch_op:
+ batch_op.drop_column('time')
+
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('incidents', schema=None) as batch_op:
+ batch_op.add_column(sa.Column('time', postgresql.TIME(), autoincrement=False, nullable=True))
+ batch_op.drop_index(batch_op.f('ix_incidents_occurred_at'))
+ batch_op.create_index('ix_incidents_time', ['time'], unique=False)
+
+ op.execute(
+ f"""
+ UPDATE incidents
+ SET (date, time) = (occurred_at::date, (occurred_at::timestamptz AT TIME ZONE '{TIMEZONE}')::time)
+ WHERE occurred_at IS NOT NULL
+ """
+ )
+
+ with op.batch_alter_table('incidents', schema=None) as batch_op:
+ batch_op.drop_column('occurred_at')
+
+ # ### end Alembic commands ###
From bb95a43c42ece0a33b11f1f65a8b0cc8fdb15c72 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 16:24:57 -0500
Subject: [PATCH 48/55] Update
2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
---
...99be2696a9_add_occurred_at_to_incidents.py | 35 +++++++++++--------
1 file changed, 21 insertions(+), 14 deletions(-)
diff --git a/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py b/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
index bfdac0996..8d447458a 100644
--- a/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
+++ b/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
@@ -7,13 +7,14 @@
"""
import os
-from alembic import op
import sqlalchemy as sa
+from alembic import op
from sqlalchemy.dialects import postgresql
+
# revision identifiers, used by Alembic.
-revision = '2b99be2696a9'
-down_revision = 'b38c133bed3c'
+revision = "2b99be2696a9"
+down_revision = "b38c133bed3c"
branch_labels = None
depends_on = None
@@ -23,9 +24,13 @@
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table('incidents', schema=None) as batch_op:
- batch_op.add_column(sa.Column('occurred_at', sa.DateTime(timezone=True), nullable=True))
- batch_op.create_index(batch_op.f('ix_incidents_occurred_at'), ['occurred_at'], unique=False)
+ with op.batch_alter_table("incidents", schema=None) as batch_op:
+ batch_op.add_column(
+ sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=True)
+ )
+ batch_op.create_index(
+ batch_op.f("ix_incidents_occurred_at"), ["occurred_at"], unique=False
+ )
op.execute(
f"""
@@ -37,18 +42,20 @@ def upgrade():
"""
)
- with op.batch_alter_table('incidents', schema=None) as batch_op:
- batch_op.drop_column('time')
+ with op.batch_alter_table("incidents", schema=None) as batch_op:
+ batch_op.drop_column("time")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table('incidents', schema=None) as batch_op:
- batch_op.add_column(sa.Column('time', postgresql.TIME(), autoincrement=False, nullable=True))
- batch_op.drop_index(batch_op.f('ix_incidents_occurred_at'))
- batch_op.create_index('ix_incidents_time', ['time'], unique=False)
+ with op.batch_alter_table("incidents", schema=None) as batch_op:
+ batch_op.add_column(
+ sa.Column("time", postgresql.TIME(), autoincrement=False, nullable=True)
+ )
+ batch_op.drop_index(batch_op.f("ix_incidents_occurred_at"))
+ batch_op.create_index("ix_incidents_time", ["time"], unique=False)
op.execute(
f"""
@@ -58,7 +65,7 @@ def downgrade():
"""
)
- with op.batch_alter_table('incidents', schema=None) as batch_op:
- batch_op.drop_column('occurred_at')
+ with op.batch_alter_table("incidents", schema=None) as batch_op:
+ batch_op.drop_column("occurred_at")
# ### end Alembic commands ###
From 47c8f3c5dfb2694d874b49969d3aec388c9394ab Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 16:24:59 -0500
Subject: [PATCH 49/55] Update database.py
---
OpenOversight/app/models/database.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/OpenOversight/app/models/database.py b/OpenOversight/app/models/database.py
index 1d3b32989..1a4516dad 100644
--- a/OpenOversight/app/models/database.py
+++ b/OpenOversight/app/models/database.py
@@ -634,7 +634,9 @@ class Incident(BaseModel):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.Date, unique=False, index=True)
- occurred_at = db.Column(db.DateTime(timezone=True), unique=False, nullable=True, index=True)
+ occurred_at = db.Column(
+ db.DateTime(timezone=True), unique=False, nullable=True, index=True
+ )
report_number = db.Column(db.String(50), index=True)
description = db.Column(db.Text(), nullable=True)
address_id = db.Column(db.Integer, db.ForeignKey("locations.id"))
From 662b36b558b5c950568e01c0c8b5b5628d50ff4d Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Mon, 7 Aug 2023 16:25:02 -0500
Subject: [PATCH 50/55] Update test_utils.py
---
OpenOversight/tests/test_utils.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/OpenOversight/tests/test_utils.py b/OpenOversight/tests/test_utils.py
index b7fa0c669..93bf2a8f1 100644
--- a/OpenOversight/tests/test_utils.py
+++ b/OpenOversight/tests/test_utils.py
@@ -92,7 +92,8 @@ def test_get_utc_datetime():
test_local_to_utc = get_utc_datetime(local)
test = (local - test_local_to_utc).total_seconds()
correct = (
- datetime.now(tz=timezone.utc) - datetime.now().astimezone(tz=server_timezone)
+ datetime.now(tz=timezone.utc)
+ - datetime.now().astimezone(tz=server_timezone)
).total_seconds()
assert test == correct
From db81b9989174e00f50bf93555456280a59ee7bb5 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Tue, 8 Aug 2023 16:23:48 -0500
Subject: [PATCH 51/55] Update incident_detail.html
---
OpenOversight/app/templates/incident_detail.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenOversight/app/templates/incident_detail.html b/OpenOversight/app/templates/incident_detail.html
index e4b27608e..56c0ab492 100644
--- a/OpenOversight/app/templates/incident_detail.html
+++ b/OpenOversight/app/templates/incident_detail.html
@@ -41,7 +41,7 @@
incident.department.name }}
{% endif %}
-
+
Incident
From c68554fbe1b51f3a99b323919e8fdce8ca5c6808 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Wed, 16 Aug 2023 12:37:11 -0500
Subject: [PATCH 52/55] Update
2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
---
...114_2b99be2696a9_add_occurred_at_to_incidents.py | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py b/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
index 8d447458a..f4241c23a 100644
--- a/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
+++ b/OpenOversight/migrations/versions/2023-08-07-2114_2b99be2696a9_add_occurred_at_to_incidents.py
@@ -9,7 +9,6 @@
import sqlalchemy as sa
from alembic import op
-from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
@@ -35,27 +34,23 @@ def upgrade():
op.execute(
f"""
UPDATE incidents
- SET occurred_at = (date::date || ' ' || time::timetz)::timestamp AT TIME ZONE '{TIMEZONE}'
+ SET
+ occurred_at = (date::date || ' ' || time::timetz)::timestamp AT TIME ZONE '{TIMEZONE}',
+ time = NULL,
+ date = NULL
WHERE occurred_at IS NULL
AND time IS NOT NULL
AND date IS NOT NULL
"""
)
- with op.batch_alter_table("incidents", schema=None) as batch_op:
- batch_op.drop_column("time")
-
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("incidents", schema=None) as batch_op:
- batch_op.add_column(
- sa.Column("time", postgresql.TIME(), autoincrement=False, nullable=True)
- )
batch_op.drop_index(batch_op.f("ix_incidents_occurred_at"))
- batch_op.create_index("ix_incidents_time", ["time"], unique=False)
op.execute(
f"""
From c59cca8e180a7b308ebe3078cf5216f919f1270a Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Wed, 16 Aug 2023 12:58:25 -0500
Subject: [PATCH 53/55] Update general.py
---
OpenOversight/app/utils/general.py | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/OpenOversight/app/utils/general.py b/OpenOversight/app/utils/general.py
index c516c9173..d5cc760e9 100644
--- a/OpenOversight/app/utils/general.py
+++ b/OpenOversight/app/utils/general.py
@@ -14,7 +14,7 @@ def ac_can_edit_officer(officer, ac):
return False
-def allowed_file(filename):
+def allowed_file(filename: str):
return (
"." in filename
and filename.rsplit(".", 1)[1].lower()
@@ -65,12 +65,9 @@ def get_random_image(image_query):
return None
-def get_utc_datetime(dt: datetime = None) -> datetime:
+def get_utc_datetime(dt: datetime) -> datetime:
"""Return the current datetime in UTC or the converted given datetime to UTC."""
- if dt is not None:
- return dt.replace(tzinfo=timezone.utc)
- else:
- return datetime.utcnow()
+ return dt.replace(tzinfo=timezone.utc)
def merge_dicts(*dict_args):
@@ -84,7 +81,7 @@ def merge_dicts(*dict_args):
return result
-def normalize_gender(input_gender):
+def normalize_gender(input_gender: str):
if input_gender is None:
return None
normalized_genders = {
@@ -149,8 +146,8 @@ def serve_image(filepath):
return url_for("static", filename=filepath.replace("static/", "").lstrip("/"))
-def str_is_true(str_):
- return strtobool(str_.lower())
+def str_is_true(string: str):
+ return strtobool(string.lower())
def validate_redirect_url(url: Optional[str]) -> Optional[str]:
From 592e37b4939303b163f0c47d927c8b39694df1d1 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Wed, 16 Aug 2023 12:58:28 -0500
Subject: [PATCH 54/55] Update database_imports.py
---
OpenOversight/app/models/database_imports.py | 30 ++++++++++++++------
1 file changed, 22 insertions(+), 8 deletions(-)
diff --git a/OpenOversight/app/models/database_imports.py b/OpenOversight/app/models/database_imports.py
index abb26cacf..e18f8a2ef 100644
--- a/OpenOversight/app/models/database_imports.py
+++ b/OpenOversight/app/models/database_imports.py
@@ -36,22 +36,22 @@ def validate_choice(
return None
-def parse_date(date_str: Optional[str]) -> Optional[date]:
+def parse_date(date_str: Optional[str]) -> date:
if date_str:
return dateutil.parser.parse(date_str).date()
- return None
+ return datetime.now().date()
-def parse_date_time(date_time_str: Optional[str]) -> Optional[datetime]:
+def parse_date_time(date_time_str: str) -> datetime:
if date_time_str:
return datetime.combine(parse_date(date_time_str), parse_time(date_time_str))
- return None
+ return datetime.now()
-def parse_time(time_str: Optional[str]) -> Optional[time]:
+def parse_time(time_str: str) -> time:
if time_str:
return dateutil.parser.parse(time_str).time()
- return None
+ return datetime.now().time()
def parse_int(value: Optional[Union[str, int]]) -> Optional[int]:
@@ -283,7 +283,14 @@ def get_or_create_location_from_dict(
def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> Incident:
incident = Incident(
occurred_at=get_utc_datetime(
- parse_date_time(" ".join([data.get("date"), data.get("time", "00:00")]))
+ parse_date_time(
+ " ".join(
+ [
+ data.get("date", datetime.now().date().strftime("%x")),
+ data.get("time", "00:00"),
+ ]
+ )
+ )
),
report_number=parse_str(data.get("report_number"), None),
description=parse_str(data.get("description"), None),
@@ -308,7 +315,14 @@ def create_incident_from_dict(data: Dict[str, Any], force_id: bool = False) -> I
def update_incident_from_dict(data: Dict[str, Any], incident: Incident) -> Incident:
if "date" in data:
incident.occurred_at = get_utc_datetime(
- parse_date_time(" ".join([data.get("date"), data.get("time", "00:00")]))
+ parse_date_time(
+ " ".join(
+ [
+ data.get("date", datetime.now().date().strftime("%x")),
+ data.get("time", "00:00"),
+ ]
+ )
+ )
)
if "report_number" in data:
incident.report_number = parse_str(data.get("report_number"), None)
From 157bb3a906359eb833e898d4a1b2f7deafccb427 Mon Sep 17 00:00:00 2001
From: michplunkett <5885605+michplunkett@users.noreply.github.com>
Date: Wed, 16 Aug 2023 15:52:52 -0500
Subject: [PATCH 55/55] Update database.py
---
OpenOversight/app/models/database.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/OpenOversight/app/models/database.py b/OpenOversight/app/models/database.py
index 6ca741055..885d9eeb4 100644
--- a/OpenOversight/app/models/database.py
+++ b/OpenOversight/app/models/database.py
@@ -636,6 +636,7 @@ class Incident(BaseModel):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.Date, unique=False, index=True)
+ time = db.Column(db.Time, unique=False, index=True)
occurred_at = db.Column(
db.DateTime(timezone=True), unique=False, nullable=True, index=True
)
|