Skip to content

Commit

Permalink
Munger: OPA Incidents, improved detail formatting (#99)
Browse files Browse the repository at this point in the history
* Add data munging

* Documentation & comments

* Rearrange partials, force column ordering

* Improve long-form text layout

* Only show a cross-street if we have one

* Give the incident detail page more padding on the bottom

* Add ellipses to cut off text

* Only show street name if we have one

* Formatting

* Add "OPA Case" prefix to incident title

* Only show unique internal identifier if it exists

* Add args to just recipes, start stack after fresh start

* Allow incident descriptions to have HTML

* Improve OPA incident description formatting, add officer name

* Remove OPA Case prefix when matching links

* Formatting!

* Remove redundant "Incident" in report number title

* Add "number of known incidents" to general information section

* Allow spaces in incident names (but no other special characters)

* Add known incident count to officer list

* Have acceptance tests be a separate step

This should make it easier to run the regular tests, then the functional ones in dev while not having to run the regular ones twice

* Fix the functional tests based on new description cutoff

* Remove conditional for known incidents
  • Loading branch information
AetherUnbound authored Jan 9, 2022
1 parent 07f25b6 commit eef7d5f
Show file tree
Hide file tree
Showing 18 changed files with 639 additions and 337 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ jobs:
- name: Run tests
run: |
just build
just test
just test-acceptance
4 changes: 2 additions & 2 deletions OpenOversight/app/main/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,8 +514,8 @@ class IncidentForm(DateFieldForm):
report_number = StringField(
validators=[
Regexp(
r"^[a-zA-Z0-9-]*$",
message="Report numbers can contain letters, numbers, and dashes",
r"^[a-zA-Z0-9- ]*$",
message="Report cannot contain special characters (dashes permitted)",
)
],
description="Incident number for the organization tracking incidents",
Expand Down
12 changes: 7 additions & 5 deletions OpenOversight/app/static/js/incidentDescription.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
$(document).ready(function() {
let overflow_length = 500;
$(".incident-description").each(function () {
let description = this;
let incidentId = $( this ).data("incident");
if (description.innerText.length < 300) {
if (description.innerHTML.length < overflow_length) {
$("#description-overflow-row_" + incidentId).hide();
}
if(description.innerText.length > 300) {
let originalDescription = description.innerText;
description.innerText = description.innerText.substring(0, 300);
if(description.innerHTML.length > overflow_length) {
let originalDescription = description.innerHTML;
description.innerHTML = description.innerHTML.substring(0, overflow_length) + "…";
$(`#description-overflow-button_${incidentId}`).on('click', function(event) {
description.innerText = originalDescription;
event.stopImmediatePropagation();
description.innerHTML = originalDescription;
$("#description-overflow-row_" + incidentId).hide();
})
}
Expand Down
76 changes: 40 additions & 36 deletions OpenOversight/app/templates/incident_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
{% set incident = obj %}
{% block title %}{{ incident.department.name }} incident{% if incident.report_number %} {{incident.report_number}}{% endif %} - OpenOversight{% endblock %}
{% block meta %}
{% if incident.description != None %}
<meta name="description" content="{{ incident.description }}">
{% else %}
<meta name="description" content="View details for {{ incident.department.name }} incident{% if incident.report_number %} {{incident.report_number}}{% endif %}.">
{% endif %}
<!-- Google Breadcrumb https://developers.google.com/search/docs/data-types/breadcrumb -->
<script type="application/ld+json">
{% if incident.description != None %}
<meta name="description" content="{{ incident.description }}">
{% else %}
<meta name="description" content="View details for {{ incident.department.name }} incident{% if incident.report_number %} {{incident.report_number}}{% endif %}.">
{% endif %}
<!-- Google Breadcrumb https://developers.google.com/search/docs/data-types/breadcrumb -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
Expand All @@ -31,34 +31,38 @@
</script>
{% endblock %}
{% block content %}
<main class='container pt-35' role='main'>
<a href="{{ url_for('main.incident_api')}}">All Incidents</a>
{% if incident.department %}
<p>
<a href="{{ url_for('main.incident_api', department_id=incident.department_id)}}">More incidents in the {{ incident.department.name }}</a>
</p>
{% endif %}
<h1>Incident {% if incident.report_number %}{{incident.report_number}}{% endif %}</h1>
<div class='row'>
<div class="col-sm-12 col-md-6">
{% with detail=True %}
{% include 'partials/incident_fields.html' %}
{% endwith %}
</div>
<div class="col-sm-12 col-md-6">
<h5>Description</h5>
<p>{{ incident.description }}</p>
</div>
</div>
{% include 'partials/links_and_videos_row.html' %}
{% if current_user.is_administrator
<main class='container pt-35 pb-50' role='main'>
<a href="{{ url_for('main.incident_api')}}">All Incidents</a>
{% if incident.department %}
<p>
<a href="{{ url_for('main.incident_api', department_id=incident.department_id)}}">More incidents in the {{ incident.department.name }}</a>
</p>
{% endif %}
<h1>{% if incident.report_number %}{{incident.report_number}}{% endif %}</h1>
<div class='row'>
<div class="col-sm-12 col-md-6">
{% with detail=True %}
{% include 'partials/incident_fields.html' %}
{% endwith %}
</div>
<div class="col-sm-12 col-md-6">
<h4>Description</h4>
<br>
{% for paragraph in incident.description.split('\n') %}
{{ paragraph | safe }}<br/>
{% endfor %}
</p>
</div>
</div>
{% include 'partials/links_and_videos_row.html' %}
{% if current_user.is_administrator
or (current_user.is_area_coordinator and current_user.ac_department_id == incident.department_id) %}
<div class="row">
<div class="col-sm-12 col-md-6">
<a class="btn btn-primary" href="{{ '{}/edit'.format(url_for('main.incident_api', obj_id=incident.id))}}" role="button">Edit</a>
<a class="btn btn-danger" href="{{ '{}/delete'.format(url_for('main.incident_api', obj_id=incident.id))}}" role="button">Delete</a>
</div>
</div>
{% endif %}
</main>
<div class="row">
<div class="col-sm-12 col-md-6">
<a class="btn btn-primary" href="{{ '{}/edit'.format(url_for('main.incident_api', obj_id=incident.id))}}" role="button">Edit</a>
<a class="btn btn-danger" href="{{ '{}/delete'.format(url_for('main.incident_api', obj_id=incident.id))}}" role="button">Delete</a>
</div>
</div>
{% endif %}
</main>
{% endblock %}
2 changes: 2 additions & 0 deletions OpenOversight/app/templates/list_officer.html
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ <h2>
<dd>{{ officer.unit_descrip()|default('Unknown') }}</dd>
<dt>Currently on the Force</dt>
<dd>{{ officer.currently_on_force() }}</dd>
<dt>Known incidents</dt>
<dd>{{ officer.incidents | length }}</dd>
</dl>
</div>
<div class="col-md-6 col-xs-6">
Expand Down
40 changes: 16 additions & 24 deletions OpenOversight/app/templates/officer.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,35 +109,27 @@ <h1>Officer Detail: <b>{{ officer.full_name() }}</b></h1>
<div class="row">
<div class="col-sm-12 col-md-6">
{% include "partials/officer_assignment_history.html" %}
{% if officer.descriptions or is_admin_or_coordinator %}
{% include "partials/officer_descriptions.html" %}
{% endif %}
{# Notes are for internal use #}
{% if is_admin_or_coordinator %}
{% include "partials/officer_notes.html" %}
{% endif %}
{% with obj=officer %}
{% include "partials/links_and_videos_row.html" %}
{% endwith %}
</div> {# end col #}

{% if officer.salaries or is_admin_or_coordinator %}
<div class='col-sm-12 col-md-6'>
<div class='col-sm-12 col-md-6'>
{% if officer.salaries or is_admin_or_coordinator %}
{% include "partials/officer_salary.html" %}
</div>{# end col #}
{% endif %}

{% if officer.incidents or is_admin_or_coordinator %}
<div class='col-sm-12 col-md-6'>
{% endif %}
{% if officer.incidents or is_admin_or_coordinator %}
{% include "partials/officer_incidents.html" %}
</div>{# end col #}
{% endif %}

{% if officer.descriptions or is_admin_or_coordinator %}
<div class='col-sm-12 col-md-6'>
{% include "partials/officer_descriptions.html" %}
</div>{# end col #}
{% endif %}

{# Notes are for internal use #}
{% if is_admin_or_coordinator %}
<div class='col-sm-12 col-md-6'>
{% include "partials/officer_notes.html" %}
{% endif %}
</div>{# end col #}
{% endif %}

</div> {# end row #}
{% with obj=officer %}
{% include "partials/links_and_videos_row.html" %}
{% endwith %}
</div> {# end container #}
{% endblock %}
179 changes: 91 additions & 88 deletions OpenOversight/app/templates/partials/incident_fields.html
Original file line number Diff line number Diff line change
@@ -1,93 +1,96 @@
<table class='table table-hover table-responsive'>
<tbody>
<tr>
<td><strong>Date</strong></td>
<td>{{ incident.date.strftime('%b %d, %Y') }}</td>
</tr>
<tr>
<td><strong>Time</strong></td>
<td>{% if incident.time %}{{ incident.time.strftime('%l:%M %p') }}{% endif %}</td>
</tr>
{% if incident.report_number %}
<tr>
<td><strong>Report #</strong></td>
<td>{{ incident.report_number }}</td>
</tr>
{% endif %}
<tr>
<td><strong>Department</strong></td>
<td>{{ incident.department.name }}</td>
</tr>
{% if incident.officers %}
<tr>
<td><strong>Officers</strong></td>
<td>
{% for officer in incident.officers %}
<a href="{{url_for('main.officer_profile', officer_id=officer.id)}}">{{ officer.full_name()|title }}</a>{% if not loop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
{% if detail and incident.license_plates %}
<tr>
<td><strong>License Plates</strong></td>
<td>
{% for plate in incident.license_plates %}
{{ plate.state }} {{ plate.number }}{% if not loop.last %}<br/> {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
{% if not detail %}
<tr>
<td><strong>Description</strong></td>
<td class="incident-description" id="incident-description_{{incident.id}}" data-incident='{{incident.id | tojson}}'>
{{ incident.description }}
</td>
</tr>
<tr id="description-overflow-row_{{ incident.id }}" >
<td style="border-top: none"></td>
<td style="border-top: none" id="description-overflow-cell_{{ incident.id }}">
<button id="description-overflow-button_{{ incident.id }}">
Click to read more
</button>
</td>
</tr>
{% endif %}
{% with address=incident.address %}
{% if address %}
<tr>
<td><strong>Address</strong></td>
<td>
{{ address.street_name }}
{% if address.cross_street1 and address.cross_street2 %}
<em>between</em> {{ address.cross_street1 }} <em>and</em> {{ address.cross_street2 }}
{% else %}
<em>near</em> {{ address.cross_street1 }}
{% endif %}
<br/>
{{ address.city }}, {{ address.state }} {% if address.zipcode %} {{ address.zip_code }} {% endif %}
</td>
</tr>
{% endif %}
{% endwith %}
{% if detail and current_user.is_administrator %}
{% if incident.creator %}
<tr>
<td><strong>Creator</strong></td>
<td><a href="{{ url_for('main.profile', username=incident.creator.username)}}">{{ incident.creator.username }}</a></td>
</tr>
{% endif %}
{% if incident.last_updated_by %}
<tr>
<td><strong>Last Edited By</strong></td>
<td><a href="{{ url_for('main.profile', username=incident.last_updated_by.username)}}">{{ incident.last_updated_by.username }}</a></td>
</tr>
{% endif %}
{% endif %}
</tbody>
<tbody>
<tr>
<td><strong>Date</strong></td>
<td>{{ incident.date.strftime('%b %d, %Y') }}</td>
</tr>
<tr>
<td><strong>Time</strong></td>
<td>{% if incident.time %}{{ incident.time.strftime('%l:%M %p') }}{% endif %}</td>
</tr>
{% if incident.report_number %}
<tr>
<td><strong>Report #</strong></td>
<td>{{ incident.report_number }}</td>
</tr>
{% endif %}
<tr>
<td><strong>Department</strong></td>
<td>{{ incident.department.name }}</td>
</tr>
{% if incident.officers %}
<tr>
<td><strong>Officers</strong></td>
<td>
{% for officer in incident.officers %}
<a href="{{url_for('main.officer_profile', officer_id=officer.id)}}">{{ officer.full_name()|title }}</a>{% if not loop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
{% if detail and incident.license_plates %}
<tr>
<td><strong>License Plates</strong></td>
<td>
{% for plate in incident.license_plates %}
{{ plate.state }} {{ plate.number }}{% if not loop.last %}<br/> {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
{% if not detail %}
<tr>
<td><strong>Description</strong></td>
<td class="incident-description" id="incident-description_{{incident.id}}" data-incident='{{incident.id | tojson}}'>
{% for paragraph in incident.description.split('\n') %}
{{ paragraph | safe }}<br/>
{% endfor %}
</td>
</tr>
<tr id="description-overflow-row_{{ incident.id }}" >
<td style="border-top: none"></td>
<td style="border-top: none" id="description-overflow-cell_{{ incident.id }}">
<button id="description-overflow-button_{{ incident.id }}">
Click to read more
</button>
</td>
</tr>
{% endif %}
{% with address=incident.address %}
{% if address %}
<tr>
<td><strong>Address</strong></td>
<td>
{% if address.street_name %}
{{ address.street_name }}<br/>
{% endif %}
{% if address.cross_street1 and address.cross_street2 %}
<em>between</em> {{ address.cross_street1 }} <em>and</em> {{ address.cross_street2 }}
{% elif address.cross_street1 %}
<em>near</em> {{ address.cross_street1 }}
{% endif %}
{{ address.city }}, {{ address.state }} {% if address.zipcode %} {{ address.zip_code }} {% endif %}
</td>
</tr>
{% endif %}
{% endwith %}
{% if detail and current_user.is_administrator %}
{% if incident.creator %}
<tr>
<td><strong>Creator</strong></td>
<td><a href="{{ url_for('main.profile', username=incident.creator.username)}}">{{ incident.creator.username }}</a></td>
</tr>
{% endif %}
{% if incident.last_updated_by %}
<tr>
<td><strong>Last Edited By</strong></td>
<td><a href="{{ url_for('main.profile', username=incident.last_updated_by.username)}}">{{ incident.last_updated_by.username }}</a></td>
</tr>
{% endif %}
{% endif %}
</tbody>
</table>

{% block js_footer %}
<script src="{{ url_for('static', filename='js/incidentDescription.js') }}"></script>
<script src="{{ url_for('static', filename='js/incidentDescription.js') }}"></script>
{% endblock %}
Loading

0 comments on commit eef7d5f

Please sign in to comment.