Skip to content

Commit

Permalink
add "lastModified" to Incident entity, add column in DataTable (#1589)
Browse files Browse the repository at this point in the history
Before this change, we were figuring out last modification time on the
client side. That was being calculated incorrectly, since we weren't
passing system report entries into the Incidents view
(exclude_system_entries = True). This moves over that computation to
the server.

Also, this adds a "Modified" column to the Incidents view. I'm
theorizing that people might find this useful; possibly more useful
than "Created". It could especially be useful as a sort order, so that
active Incidents are at the top and older ones fall to the bottom.

NOTE that if we want to remove this DataTables column in the future,
we should still keep the server-side piece of this commit, and we should
still use the new "last_modified" field in the API to know when an
Incident was last modified.

(finally, there's one little tweak in here for document titles, making
them more concise)
  • Loading branch information
srabraham authored Feb 9, 2025
1 parent d4eb6e1 commit df77088
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 35 deletions.
2 changes: 2 additions & 0 deletions src/ims/element/incident/incidents_template/template.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
<tr>
<th>#</th>
<th>Created</th>
<th>Modified</th>
<th>State</th>
<th>Rangers</th>
<th>Location</th>
Expand All @@ -178,6 +179,7 @@
<tr>
<th>#</th>
<th>Created</th>
<th>Modified</th>
<th>State</th>
<th>Rangers</th>
<th>Location</th>
Expand Down
12 changes: 3 additions & 9 deletions src/ims/element/static/ims.js
Original file line number Diff line number Diff line change
Expand Up @@ -505,10 +505,7 @@ function incidentAsString(incident) {
if (incident.number == null) {
return "New Incident";
}
return (
"IMS #" + incident.number + ": " +
summarizeIncident(incident)
);
return `#${incident.number}: ${summarizeIncident(incident)} (${incident.event})`;
}


Expand All @@ -517,11 +514,8 @@ function fieldReportAsString(report) {
if (report.number == null) {
return "New Field Report";
}
return (
"FR #" + report.number +
" (" + fieldReportAuthor(report) + "): " +
summarizeFieldReport(report)
);
return `FR #${report.number} (${fieldReportAuthor(report)}): ` +
`${summarizeFieldReport(report)} (${report.event})`;
}


Expand Down
35 changes: 17 additions & 18 deletions src/ims/element/static/incidents.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,36 +242,43 @@ function initDataTables() {
"render": renderDate,
},
{ // 2
"name": "incident_last_modified",
"className": "incident_last_modified text-center",
"data": "last_modified",
"defaultContent": null,
"render": renderDate,
},
{ // 3
"name": "incident_state",
"className": "incident_state text-center",
"data": "state",
"defaultContent": null,
"render": renderState,
},
{ // 3
{ // 4
"name": "incident_ranger_handles",
"className": "incident_ranger_handles",
"data": "ranger_handles",
"defaultContent": "",
"render": renderSafeSorted,
"width": "6em",
},
{ // 4
{ // 5
"name": "incident_location",
"className": "incident_location",
"data": "location",
"defaultContent": "",
"render": renderLocation,
},
{ // 5
{ // 6
"name": "incident_types",
"className": "incident_types",
"data": "incident_types",
"defaultContent": "",
"render": renderSafeSorted,
"width": "5em",
},
{ // 6
{ // 7
"name": "incident_summary",
"className": "incident_summary",
"data": "summary",
Expand All @@ -295,6 +302,11 @@ function initDataTables() {
"title",
fullDateTime.format(Date.parse(incident.created)),
);
row.getElementsByClassName("incident_last_modified")[0]
.setAttribute(
"title",
fullDateTime.format(Date.parse(incident.last_modified)),
);
},
});
}
Expand Down Expand Up @@ -435,19 +447,6 @@ function initSearchField() {
//

function initSearch() {
function modifiedAfter(incident, timestamp) {
if (timestamp < Date.parse(incident.created)) {
return true;
}

for (const entry of incident.report_entries??[]) {
if (timestamp < Date.parse(entry.created)) {
return true;
}
}

return false;
}

$.fn.dataTable.ext.search.push(
function(settings, rowData, rowIndex) {
Expand All @@ -472,7 +471,7 @@ function initSearch() {

if (
_showModifiedAfter != null &&
! modifiedAfter(incident, _showModifiedAfter)
Date.parse(incident.last_modified) < _showModifiedAfter
) {
return false
}
Expand Down
5 changes: 4 additions & 1 deletion src/ims/model/_incident.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from collections.abc import Iterable, Sequence
from datetime import datetime as DateTime

from attrs import field, frozen
from attrs import converters, field, frozen

from ims.ext.attr import sorted_tuple

Expand Down Expand Up @@ -53,6 +53,9 @@ class Incident(ReplaceMixIn):
eventID: str
number: int
created: DateTime = field(converter=normalizeDateTime)
lastModified: DateTime | None = field(
converter=converters.optional(normalizeDateTime)
)
state: IncidentState
priority: IncidentPriority
summary: str | None
Expand Down
2 changes: 2 additions & 0 deletions src/ims/model/json/_incident.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class IncidentJSONKey(Enum):
eventID = "event"
number = "number"
created = "created"
lastModified = "last_modified"
state = "state"
priority = "priority"
summary = "summary"
Expand All @@ -65,6 +66,7 @@ class IncidentJSONType(Enum):
eventID = str
number = int
created = DateTime
lastModified = DateTime | None
state = IncidentState
priority = IncidentPriority
summary = str | None
Expand Down
1 change: 1 addition & 0 deletions src/ims/model/json/test/json_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def jsonFromIncident(incident: Incident) -> dict[str, Any]:
"event": jsonSerialize(incident.eventID),
"number": jsonSerialize(incident.number),
"created": jsonSerialize(incident.created),
"last_modified": jsonSerialize(incident.lastModified),
"state": jsonSerialize(incident.state),
"priority": jsonSerialize(incident.priority),
"summary": jsonSerialize(incident.summary),
Expand Down
14 changes: 8 additions & 6 deletions src/ims/model/strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,21 +392,23 @@ def incidents(

types = [t.name for t in draw(lists(incidentTypes()))]

created = draw(dateTimes(beforeNow=beforeNow, fromNow=fromNow))
entries = draw(
lists(reportEntries(automatic=automatic, beforeNow=beforeNow, fromNow=fromNow))
)
lastModified = max(re.created for re in entries) if entries else created
return Incident(
eventID=event.id,
number=number,
created=draw(dateTimes(beforeNow=beforeNow, fromNow=fromNow)),
created=created,
lastModified=lastModified,
state=draw(incidentStates()),
priority=draw(incidentPriorities()),
summary=draw(incidentSummaries()),
location=draw(locations()),
rangerHandles=draw(lists(rangerHandles())),
incidentTypes=types,
reportEntries=draw(
lists(
reportEntries(automatic=automatic, beforeNow=beforeNow, fromNow=fromNow)
)
),
reportEntries=entries,
fieldReportNumbers=frozenset(),
)

Expand Down
13 changes: 12 additions & 1 deletion src/ims/store/_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,11 +659,17 @@ def _fetchIncidents(
int(val) for val in loads(str(row["FIELD_REPORT_NUMBERS"]))
]
incidentNumber = cast(int, row["NUMBER"])

lastModified = self.fromDateTimeValue(row["CREATED"])
if reportEntries[incidentNumber]:
lastModified = max(re.created for re in reportEntries[incidentNumber])

results.append(
Incident(
eventID=eventID,
number=incidentNumber,
created=self.fromDateTimeValue(row["CREATED"]),
lastModified=lastModified,
state=self.fromIncidentStateValue(row["STATE"]),
priority=self.fromPriorityValue(row["PRIORITY"]),
summary=cast(str | None, row["SUMMARY"]),
Expand Down Expand Up @@ -715,7 +721,7 @@ def notFound() -> NoReturn:

txn.execute(self.query.incident_reportEntries.text, parameters)

reportEntries = (
reportEntries = tuple(
ReportEntry(
id=cast(int, row["ID"]),
created=self.fromDateTimeValue(row["CREATED"]),
Expand All @@ -728,6 +734,10 @@ def notFound() -> NoReturn:
if row["TEXT"]
)

lastModified = self.fromDateTimeValue(row["CREATED"])
if reportEntries:
lastModified = max(re.created for re in reportEntries)

# FIXME: This is because schema thinks concentric is an int
if row["LOCATION_CONCENTRIC"] is None:
concentric = None
Expand All @@ -742,6 +752,7 @@ def notFound() -> NoReturn:
eventID=eventID,
number=incidentNumber,
created=self.fromDateTimeValue(row["CREATED"]),
lastModified=lastModified,
state=self.fromIncidentStateValue(row["STATE"]),
priority=self.fromPriorityValue(row["PRIORITY"]),
summary=cast(str | None, row["SUMMARY"]),
Expand Down
7 changes: 7 additions & 0 deletions src/ims/store/test/incident.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
eventID=anEvent.id,
number=0,
created=DateTime.now(UTC) + TimeDelta(seconds=1),
lastModified=DateTime.now(UTC) + TimeDelta(seconds=2),
state=IncidentState.new,
priority=IncidentPriority.normal,
summary="A thing happened",
Expand All @@ -70,6 +71,7 @@
eventID=anEvent.id,
number=1,
created=DateTime.now(UTC) + TimeDelta(seconds=2),
lastModified=DateTime.now(UTC) + TimeDelta(seconds=3),
state=IncidentState.new,
priority=IncidentPriority.normal,
summary="A thing happened",
Expand All @@ -84,6 +86,7 @@
eventID=anEvent2.id,
number=325,
created=DateTime.now(UTC) + TimeDelta(seconds=3),
lastModified=DateTime.now(UTC) + TimeDelta(seconds=4),
state=IncidentState.new,
priority=IncidentPriority.normal,
summary="Another thing happened 🙂",
Expand Down Expand Up @@ -884,6 +887,10 @@ def assertIncidentsEqual(
if store.dateTimesEqual(valueA, valueB):
continue
messages.append(f"{name} delta: {valueA - valueB}")
elif name == "lastModified":
# this field is calculated on read, and shouldn't be equal
# to what was written
continue
elif name == "reportEntries":
if store.reportEntriesEqual(valueA, valueB, ignoreAutomatic):
continue
Expand Down

0 comments on commit df77088

Please sign in to comment.