Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feedback fixes from Prod deploy v1.10.0 #225

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:16-bullseye-slim as static_files
FROM node:18.17.0-bullseye-slim as static_files

WORKDIR /code
ENV PATH /code/node_modules/.bin:$PATH
Expand Down Expand Up @@ -148,7 +148,7 @@ RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/
&& curl https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor | tee /etc/apt/trusted.gpg.d/docker.gpg >/dev/null \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/trusted.gpg.d/docker.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
# nodejs
&& sh -c 'echo "deb https://deb.nodesource.com/node_16.x $(lsb_release -cs) main" > /etc/apt/sources.list.d/nodesource.list' \
&& sh -c 'echo "deb https://deb.nodesource.com/node_18.x $(lsb_release -cs) main" > /etc/apt/sources.list.d/nodesource.list' \
&& wget --quiet -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \
# PostgreSQL
&& sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' \
Expand Down
17 changes: 11 additions & 6 deletions frontend/src/Components/Charts/TrafficStops/TrafficStops.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import TrafficStopsStyled, {
GroupedStopsContainer,
LineWrapper,
PieStopsContainer,
PieWrapper,
StopGroupsContainer,
SwitchContainer,
Expand Down Expand Up @@ -704,6 +705,7 @@ function TrafficStops(props) {
title="Stop Purposes By Group"
maintainAspectRatio={false}
displayStopPurposeTooltips
showLegendOnBottom={false}
/>
</StopGroupsContainer>
</LineWrapper>
Expand Down Expand Up @@ -763,6 +765,7 @@ function TrafficStops(props) {
maintainAspectRatio={false}
displayLegend={false}
yAxisMax={stopsGroupedByPurposeData.max_step_size}
redraw
/>
</GroupedStopsContainer>
<GroupedStopsContainer visible={visibleStopsGroupedByPurpose[1].visible}>
Expand All @@ -773,6 +776,7 @@ function TrafficStops(props) {
displayLegend={false}
yAxisMax={stopsGroupedByPurposeData.max_step_size}
yAxisShowLabels={!visibleStopsGroupedByPurpose[0].visible}
redraw
/>
</GroupedStopsContainer>
<GroupedStopsContainer visible={visibleStopsGroupedByPurpose[2].visible}>
Expand All @@ -785,6 +789,7 @@ function TrafficStops(props) {
yAxisShowLabels={
!visibleStopsGroupedByPurpose[0].visible && !visibleStopsGroupedByPurpose[1].visible
}
redraw
/>
</GroupedStopsContainer>
</LineWrapper>
Expand All @@ -797,30 +802,30 @@ function TrafficStops(props) {
/>
)}
<PieWrapper visible={checked === true}>
<GroupedStopsContainer visible={visibleStopsGroupedByPurpose[0].visible}>
<PieStopsContainer visible={visibleStopsGroupedByPurpose[0].visible}>
<PieChart
data={stopsGroupedByPurposePieData.safety}
title="Safety Violation"
maintainAspectRatio={false}
displayLegend={false}
/>
</GroupedStopsContainer>
<GroupedStopsContainer visible={visibleStopsGroupedByPurpose[1].visible}>
</PieStopsContainer>
<PieStopsContainer visible={visibleStopsGroupedByPurpose[1].visible}>
<PieChart
data={stopsGroupedByPurposePieData.regulatory}
title="Regulatory/Equipment"
maintainAspectRatio={false}
displayLegend={false}
/>
</GroupedStopsContainer>
<GroupedStopsContainer visible={visibleStopsGroupedByPurpose[2].visible}>
</PieStopsContainer>
<PieStopsContainer visible={visibleStopsGroupedByPurpose[2].visible}>
<PieChart
data={stopsGroupedByPurposePieData.other}
title="Other"
maintainAspectRatio={false}
displayLegend={false}
/>
</GroupedStopsContainer>
</PieStopsContainer>
</PieWrapper>

<Legend
Expand Down
16 changes: 14 additions & 2 deletions frontend/src/Components/Charts/TrafficStops/TrafficStops.styled.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ export const LineWrapper = styled.div`
export const PieWrapper = styled.div`
display: ${(props) => (props.visible ? 'flex' : 'none')};
flex-direction: row;
flex-wrap: wrap;
gap: 10px;
justify-content: space-evenly;

@media (${smallerThanTabletLandscape}) {
flex-direction: column;
}
`;

export const StopGroupsContainer = styled.div`
Expand All @@ -31,7 +34,16 @@ export const StopGroupsContainer = styled.div`
`;

export const GroupedStopsContainer = styled.div`
width: 30%;
width: 80%;
height: 500px;
@media (${smallerThanTabletLandscape}) {
width: 100%;
}
display: ${(props) => (props.visible ? 'block' : 'none')};
`;

export const PieStopsContainer = styled.div`
width: 33%;
height: 500px;
@media (${smallerThanTabletLandscape}) {
width: 100%;
Expand Down
36 changes: 24 additions & 12 deletions frontend/src/Components/NewCharts/LineChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Line } from 'react-chartjs-2';
import { tooltipLanguage } from '../../util/tooltipLanguage';
import { usePopper } from 'react-popper';
import styled from 'styled-components';
import DataLoading from '../Charts/ChartPrimitives/DataLoading';

export const Tooltip = styled.div`
background: #333;
Expand All @@ -21,13 +22,14 @@ export const Tooltip = styled.div`
export default function LineChart({
data,
title,
maintainAspectRatio = true,
maintainAspectRatio = false,
displayTitle = true,
displayLegend = true,
yAxisMax = null,
yAxisShowLabels = true,
displayStopPurposeTooltips = false,
showLegendOnBottom = true,
redraw = false,
}) {
const options = {
responsive: true,
Expand All @@ -47,8 +49,10 @@ export default function LineChart({
}
},
onLeave() {
setTooltipText('');
hideTooltip();
if (displayStopPurposeTooltips) {
setTooltipText('');
hideTooltip();
}
},
},
tooltip: {
Expand Down Expand Up @@ -94,17 +98,25 @@ export default function LineChart({
popperElement.removeAttribute('data-show');
};

if (!data.datasets.length) {
return <DataLoading />;
}

return (
<>
<div ref={setReferenceElement} />
<Tooltip
ref={setPopperElement}
style={{ ...styles.popper, width: '300px' }}
{...attributes.popper}
>
{tooltipText}
</Tooltip>
<Line options={options} data={data} />
{displayStopPurposeTooltips && (
<>
<div ref={setReferenceElement} />
<Tooltip
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This popper tooltip was re-rendering on scroll, which caused the graphs to re-render and provide a bad experience. Here we just want to show the tooltip if we actually need to.

ref={setPopperElement}
style={{ ...styles.popper, width: '300px' }}
{...attributes.popper}
>
{tooltipText}
</Tooltip>
</>
)}
<Line options={options} data={data} redraw={redraw} datasetIdKey={title} />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The redraw flag adjusts the widths of the adjacent graphs, increasing/decreasing depending on the selected graphs the user wants to see.

</>
);
}
16 changes: 12 additions & 4 deletions frontend/src/Components/NewCharts/PieChart.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { Pie } from 'react-chartjs-2';
import DataLoading from '../Charts/ChartPrimitives/DataLoading';

export default function PieChart({
data,
Expand Down Expand Up @@ -31,6 +32,9 @@ export default function PieChart({
return `${context.parsed}%`;
},
},
titleColor: '#000',
bodyColor: '#000',
backgroundColor: 'rgba(255, 255, 255, 1.0)',
},
title: {
display: displayTitle,
Expand All @@ -57,20 +61,20 @@ export default function PieChart({
}
const text = `${chart.data.labels[index]}: ${chart.data.datasets[0].data[index]}%`;
const textWidth = ctx.measureText(text).width;
ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
const height = y - 15 - offsetHeight;
ctx.fillRect(x - (textWidth + 10) / 2, height, textWidth + 10, 20);

ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.beginPath();
ctx.moveTo(x, y + 15 - offsetHeight);
ctx.lineTo(x - 10, y - 5 - offsetHeight);
ctx.lineTo(x + 10, y - 5 - offsetHeight);
ctx.fill();
ctx.restore();

ctx.font = '12px Arial';
ctx.fillStyle = 'white';
ctx.font = '14px Arial';
ctx.fillStyle = 'black';
ctx.fillText(text, x - textWidth / 2, y - offsetHeight);
ctx.restore();
}
Expand Down Expand Up @@ -98,6 +102,10 @@ export default function PieChart({

const noData = data.datasets[0].data.every((v) => parseInt(v, 10) === 0);

if (!data.datasets.length) {
return <DataLoading />;
}

return (
<>
{noData && <div style={{ textAlign: 'center' }}>No Data Found</div>}
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/util/tooltipLanguage.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions nc/prime_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"nc:agency-api-searches-by-type",
"nc:agency-api-contraband-hit-rate",
"nc:agency-api-use-of-force",
"nc:stops-by-count",
"nc:stop-purpose-groups",
"nc:stops-grouped-by-purpose",
)
DEFAULT_CUTOFF_SECS = 4

Expand Down
3 changes: 3 additions & 0 deletions nc/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
path(
"api/agency/<agency_id>/stops-by-count/",
views.AgencyTrafficStopsByCountView.as_view(),
name="stops-by-count",
),
path(
"api/agency/<agency_id>/stop-purpose-groups/",
views.AgencyStopPurposeGroupView.as_view(),
name="stop-purpose-groups",
),
path(
"api/agency/<agency_id>/stops-grouped-by-purpose/",
views.AgencyStopGroupByPurposeView.as_view(),
name="stops-grouped-by-purpose",
),
]
20 changes: 16 additions & 4 deletions nc/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ def get(self, request, agency_id):

qs = qs.values(*qs_values).annotate(count=Sum("count")).order_by(date_precision)
if qs.count() == 0:
return Response(data=[], status=200)
return Response(data={"labels": [], "datasets": []}, status=200)
df = pd.DataFrame(qs)
unique_x_range = df[date_precision].unique()
pivot_df = df.pivot(index=date_precision, columns=qs_df_cols, values="count").fillna(
Expand All @@ -464,6 +464,12 @@ def get(self, request, agency_id):


class AgencyStopPurposeGroupView(APIView):
def get_values(self, df, stop_purpose, years_len):
if stop_purpose and stop_purpose in df:
return list(df[stop_purpose].values)
else:
return [0] * years_len

def get(self, request, agency_id):
qs = StopSummary.objects.all()
agency_id = int(agency_id)
Expand All @@ -480,30 +486,34 @@ def get(self, request, agency_id):
.annotate(count=Sum("count"))
.order_by("year")
)
if qs.count() == 0:
return Response(data={"labels": [], "datasets": []}, status=200)

df = pd.DataFrame(qs)
unique_years = df.year.unique()
pivot_df = df.pivot(index="year", columns="stop_purpose_group", values="count").fillna(
value=0
)
df = pd.DataFrame(pivot_df)
years_len = len(unique_years)
data = {
"labels": unique_years,
"datasets": [
{
"label": StopPurposeGroup.SAFETY_VIOLATION,
"data": list(df[StopPurposeGroup.SAFETY_VIOLATION].values),
"data": self.get_values(df, StopPurposeGroup.SAFETY_VIOLATION, years_len),
"borderColor": "#7F428A",
"backgroundColor": "#CFA9D6",
},
{
"label": StopPurposeGroup.REGULATORY_EQUIPMENT,
"data": list(df[StopPurposeGroup.REGULATORY_EQUIPMENT].values),
"data": self.get_values(df, StopPurposeGroup.REGULATORY_EQUIPMENT, years_len),
"borderColor": "#b36800",
"backgroundColor": "#ffa500",
},
{
"label": StopPurposeGroup.OTHER,
"data": list(df[StopPurposeGroup.OTHER].values),
"data": self.get_values(df, StopPurposeGroup.OTHER, years_len),
"borderColor": "#1B4D3E",
"backgroundColor": "#ACE1AF",
},
Expand Down Expand Up @@ -575,6 +585,8 @@ def get(self, request, agency_id):
.annotate(count=Sum("count"))
.order_by("year")
)
if qs.count() == 0:
return Response(data={"labels": [], "datasets": []}, status=200)
df = pd.DataFrame(qs)
unique_years = df.year.unique()
pivot_table = pd.pivot_table(
Expand Down