From 3e854b836ad037ec9490cdbbe3966f69c3a1ce62 Mon Sep 17 00:00:00 2001 From: Aristotel Fani Date: Tue, 6 Feb 2024 10:46:09 -0500 Subject: [PATCH 1/4] Fix KeyError bugs (#262) * fix dataframe lookups crashing the site * update graphs to use temp loading variable when fetching --- .../Charts/Contraband/Contraband.js | 56 ++++++++++++++++--- .../Components/Charts/Overview/Overview.js | 1 + .../Charts/SearchRate/SearchRate.js | 12 ++-- .../Components/Charts/Searches/Searches.js | 12 +++- .../Charts/TrafficStops/TrafficStops.js | 23 ++++++-- .../Charts/UseOfForce/UseOfForce.js | 6 +- .../NewCharts/HorizontalBarChart.js | 8 +-- .../src/Components/NewCharts/LineChart.js | 2 +- frontend/src/Components/NewCharts/PieChart.js | 2 +- .../Components/NewCharts/VerticalBarChart.js | 8 +-- nc/views.py | 32 ++++++++--- 11 files changed, 122 insertions(+), 40 deletions(-) diff --git a/frontend/src/Components/Charts/Contraband/Contraband.js b/frontend/src/Components/Charts/Contraband/Contraband.js index 2d0b082d..8dfc64e9 100644 --- a/frontend/src/Components/Charts/Contraband/Contraband.js +++ b/frontend/src/Components/Charts/Contraband/Contraband.js @@ -47,6 +47,7 @@ function Contraband(props) { isModalOpen: false, tableData: [], csvData: [], + loading: true, }); const [contrabandYear, setContrabandYear] = useState(YEARS_DEFAULT); const [contrabandTypesData, setContrabandTypesData] = useState({ @@ -55,12 +56,14 @@ function Contraband(props) { isModalOpen: false, tableData: [], csvData: [], + loading: true, }); const [contrabandTypesYear, setContrabandTypesYear] = useState(YEARS_DEFAULT); const [contrabandStopPurposeData, setContrabandStopPurposeData] = useState({ labels: [], datasets: [], + loading: true, }); const [contrabandStopPurposeModalData, setContrabandStopPurposeModalData] = useState({ modalData: {}, @@ -68,6 +71,7 @@ function Contraband(props) { tableData: [], csvData: [], selectedPurpose: STOP_PURPOSE_TYPES[0], + loading: true, }); const [groupedContrabandStopPurposeModalData, setGroupedContrabandStopPurposeModalData] = @@ -76,6 +80,7 @@ function Contraband(props) { isOpen: false, tableData: [], csvData: [], + loading: true, }); const [contrabandStopPurposeYear, setContrabandStopPurposeYear] = useState(YEARS_DEFAULT); @@ -84,14 +89,17 @@ function Contraband(props) { { labels: [], datasets: [], + loading: true, }, { labels: [], datasets: [], + loading: true, }, { labels: [], datasets: [], + loading: true, }, ]; const [contrabandGroupedStopPurposeData, setContrabandGroupedStopPurposeData] = useState( @@ -155,10 +163,16 @@ function Contraband(props) { // Build New Contraband Data useEffect(() => { - let url = `/api/agency/${agencyId}/contraband/`; + const params = []; if (contrabandYear && contrabandYear !== 'All') { - url = `${url}?year=${contrabandYear}`; + params.push({ param: 'year', val: contrabandYear }); } + if (officerId) { + params.push({ param: 'officer', val: officerId }); + } + + const urlParams = params.map((p) => `${p.param}=${p.val}`).join('&'); + const url = `/api/agency/${agencyId}/contraband/?${urlParams}`; axios .get(url) .then((res) => { @@ -204,10 +218,16 @@ function Contraband(props) { }, [contrabandYear]); useEffect(() => { - let url = `/api/agency/${agencyId}/contraband-types/`; + const params = []; if (contrabandTypesYear && contrabandTypesYear !== 'All') { - url = `${url}?year=${contrabandTypesYear}`; + params.push({ param: 'year', val: contrabandTypesYear }); + } + if (officerId) { + params.push({ param: 'officer', val: officerId }); } + + const urlParams = params.map((p) => `${p.param}=${p.val}`).join('&'); + const url = `/api/agency/${agencyId}/contraband-types/?${urlParams}`; axios .get(url) .then((res) => { @@ -252,10 +272,16 @@ function Contraband(props) { }, [contrabandTypesYear]); useEffect(() => { - let url = `/api/agency/${agencyId}/contraband-stop-purpose/`; + const params = []; if (contrabandStopPurposeYear && contrabandStopPurposeYear !== 'All') { - url = `${url}?year=${contrabandStopPurposeYear}`; + params.push({ param: 'year', val: contrabandStopPurposeYear }); + } + if (officerId) { + params.push({ param: 'officer', val: officerId }); } + + const urlParams = params.map((p) => `${p.param}=${p.val}`).join('&'); + const url = `/api/agency/${agencyId}/contraband-stop-purpose/?${urlParams}`; axios .get(url) .then((res) => { @@ -291,10 +317,16 @@ function Contraband(props) { }, []); const fetchHitRateByStopPurpose = (yr) => { - let url = `/api/agency/${agencyId}/contraband-grouped-stop-purpose/`; + const params = []; if (yr && yr !== 'All') { - url = `${url}?year=${yr}`; + params.push({ param: 'year', val: yr }); + } + if (officerId) { + params.push({ param: 'officer', val: officerId }); } + + const urlParams = params.map((p) => `${p.param}=${p.val}`).join('&'); + const url = `/api/agency/${agencyId}/contraband-grouped-stop-purpose/?${urlParams}`; axios .get(url) .then((res) => { @@ -325,9 +357,15 @@ function Contraband(props) { }; useEffect(() => { + const params = []; + if (officerId) { + params.push({ param: 'officer', val: officerId }); + } + + const urlParams = params.map((p) => `${p.param}=${p.val}`).join('&'); const url = `/api/agency/${agencyId}/contraband-grouped-stop-purpose/modal/?grouped_stop_purpose=${selectedGroupedContrabandStopPurpose}&contraband_type=${toTitleCase( selectedGroupedContrabandType - )}`; + )}/${urlParams}`; axios.get(url).then((res) => { const tableData = JSON.parse(res.data.table_data)['data']; updateGroupedContrabandModalData(tableData); diff --git a/frontend/src/Components/Charts/Overview/Overview.js b/frontend/src/Components/Charts/Overview/Overview.js index 00ac6303..5ac676b9 100644 --- a/frontend/src/Components/Charts/Overview/Overview.js +++ b/frontend/src/Components/Charts/Overview/Overview.js @@ -53,6 +53,7 @@ function Overview(props) { ...pieChartConfig, }, ], + loading: true, }; const [censusPieData, setCensusPieData] = useState(initChartData); diff --git a/frontend/src/Components/Charts/SearchRate/SearchRate.js b/frontend/src/Components/Charts/SearchRate/SearchRate.js index 982e8cfa..30d85b4e 100644 --- a/frontend/src/Components/Charts/SearchRate/SearchRate.js +++ b/frontend/src/Components/Charts/SearchRate/SearchRate.js @@ -27,22 +27,26 @@ function SearchRate(props) { const [chartState] = useDataset(agencyId, LIKELIHOOD_OF_SEARCH); const [year, setYear] = useState(YEARS_DEFAULT); - const [searchRateData, setSearchRateData] = useState({ labels: [], datasets: [] }); + const [searchRateData, setSearchRateData] = useState({ labels: [], datasets: [], loading: true }); const renderMetaTags = useMetaTags(); const [renderTableModal, { openModal }] = useTableModal(); useEffect(() => { - let url = `/api/agency/${agencyId}/search-rate/`; + const params = []; if (year && year !== 'All') { - url = `${url}?year=${year}`; + params.push({ param: 'year', val: year }); } if (officerId) { - url = `${url}&officer=${officerId}`; + params.push({ param: 'officer', val: officerId }); } + + const urlParams = params.map((p) => `${p.param}=${p.val}`).join('&'); + const url = `/api/agency/${agencyId}/search-rate/?${urlParams}`; axios .get(url) .then((res) => { + console.log(res); setSearchRateData(res.data); }) .catch((err) => console.log(err)); diff --git a/frontend/src/Components/Charts/Searches/Searches.js b/frontend/src/Components/Charts/Searches/Searches.js index 5d5f4b6a..e17c8d90 100644 --- a/frontend/src/Components/Charts/Searches/Searches.js +++ b/frontend/src/Components/Charts/Searches/Searches.js @@ -40,9 +40,17 @@ function Searches(props) { const [searchType, setSearchType] = useState(SEARCH_TYPE_DEFAULT); - const [searchPercentageData, setSearchPercentageData] = useState({ labels: [], datasets: [] }); + const [searchPercentageData, setSearchPercentageData] = useState({ + labels: [], + datasets: [], + loading: true, + }); - const [searchCountData, setSearchCountData] = useState({ labels: [], datasets: [] }); + const [searchCountData, setSearchCountData] = useState({ + labels: [], + datasets: [], + loading: true, + }); const [searchCountType, setSearchCountType] = useState(0); const renderMetaTags = useMetaTags(); const [renderTableModal, { openModal }] = useTableModal(); diff --git a/frontend/src/Components/Charts/TrafficStops/TrafficStops.js b/frontend/src/Components/Charts/TrafficStops/TrafficStops.js index 65590ff2..31b21dc7 100644 --- a/frontend/src/Components/Charts/TrafficStops/TrafficStops.js +++ b/frontend/src/Components/Charts/TrafficStops/TrafficStops.js @@ -102,12 +102,16 @@ function TrafficStops(props) { const renderMetaTags = useMetaTags(); const [renderTableModal, { openModal }] = useTableModal(); - const [stopPurposeGroupsData, setStopPurposeGroups] = useState({ labels: [], datasets: [] }); + const [stopPurposeGroupsData, setStopPurposeGroups] = useState({ + labels: [], + datasets: [], + loading: true, + }); const [stopsGroupedByPurposeData, setStopsGroupedByPurpose] = useState({ labels: [], - safety: { labels: [], datasets: [] }, - regulatory: { labels: [], datasets: [] }, - other: { labels: [], datasets: [] }, + safety: { labels: [], datasets: [], loading: true }, + regulatory: { labels: [], datasets: [], loading: true }, + other: { labels: [], datasets: [], loading: true }, max_step_size: null, }); const groupedPieChartConfig = { @@ -126,6 +130,7 @@ function TrafficStops(props) { ...groupedPieChartConfig, }, ], + loading: true, }, regulatory: { labels: groupedPieChartLabels, @@ -135,6 +140,7 @@ function TrafficStops(props) { ...groupedPieChartConfig, }, ], + loading: true, }, other: { labels: groupedPieChartLabels, @@ -144,6 +150,7 @@ function TrafficStops(props) { ...groupedPieChartConfig, }, ], + loading: true, }, }); @@ -193,6 +200,7 @@ function TrafficStops(props) { const [trafficStopsByCount, setTrafficStopsByCount] = useState({ labels: [], datasets: [], + loading: true, }); const createDateForRange = (yr) => @@ -252,6 +260,7 @@ function TrafficStops(props) { }, []); const buildPercentages = (data, ds) => { + if (!data.length) return [0, 0, 0, 0, 0, 0]; const dsTotal = data[ds].datasets .map((s) => s.data.reduce((a, b) => a + b, 0)) .reduce((a, b) => a + b, 0); @@ -279,7 +288,11 @@ function TrafficStops(props) { .catch((err) => console.log(err)); }, []); - const [stopsByPercentageData, setStopsByPercentageData] = useState({ labels: [], datasets: [] }); + const [stopsByPercentageData, setStopsByPercentageData] = useState({ + labels: [], + datasets: [], + loading: true, + }); useEffect(() => { let url = `/api/agency/${agencyId}/stops-by-percentage/`; diff --git a/frontend/src/Components/Charts/UseOfForce/UseOfForce.js b/frontend/src/Components/Charts/UseOfForce/UseOfForce.js index 07b4d18f..cf243a8f 100644 --- a/frontend/src/Components/Charts/UseOfForce/UseOfForce.js +++ b/frontend/src/Components/Charts/UseOfForce/UseOfForce.js @@ -36,7 +36,11 @@ function UseOfForce(props) { const [year, setYear] = useState(YEARS_DEFAULT); - const [useOfForceBarData, setUseOfForceBarData] = useState({ labels: [], datasets: [] }); + const [useOfForceBarData, setUseOfForceBarData] = useState({ + labels: [], + datasets: [], + loading: true, + }); const [useOfForcePieData, setUseOfForcePieData] = useState({ labels: pieChartLabels, datasets: [ diff --git a/frontend/src/Components/NewCharts/HorizontalBarChart.js b/frontend/src/Components/NewCharts/HorizontalBarChart.js index 4d259467..baa19e95 100644 --- a/frontend/src/Components/NewCharts/HorizontalBarChart.js +++ b/frontend/src/Components/NewCharts/HorizontalBarChart.js @@ -132,10 +132,6 @@ export default function HorizontalBarChart({ popperElement.removeAttribute('data-show'); }; - if (!data.labels.length) { - return ; - } - const whiteBackground = { id: 'customBarCanvasBackgroundColor', beforeDraw: (chart, args, config) => { @@ -167,6 +163,10 @@ export default function HorizontalBarChart({ const barChartModalPlugins = [whiteBackground]; const barChartModalOptions = createModalOptions(options); + if (data.loading) { + return ; + } + return ( <> {displayStopPurposeTooltips && ( diff --git a/frontend/src/Components/NewCharts/LineChart.js b/frontend/src/Components/NewCharts/LineChart.js index 41c42044..fa3192dc 100644 --- a/frontend/src/Components/NewCharts/LineChart.js +++ b/frontend/src/Components/NewCharts/LineChart.js @@ -164,7 +164,7 @@ export default function LineChart({ const lineChartModalPlugins = [whiteBackground]; const lineChartModalOptions = createModalOptions(options); - if (!data.datasets.length) { + if (data.loading) { return ; } diff --git a/frontend/src/Components/NewCharts/PieChart.js b/frontend/src/Components/NewCharts/PieChart.js index e9d3a0fb..f1cd4db7 100644 --- a/frontend/src/Components/NewCharts/PieChart.js +++ b/frontend/src/Components/NewCharts/PieChart.js @@ -136,7 +136,7 @@ export default function PieChart({ const pieChartModalPlugins = [whiteBackground, alwaysShowTooltip]; const pieChartModalOptions = createModalOptions(options); - if (!data.datasets.length) { + if (data.loading) { return ; } diff --git a/frontend/src/Components/NewCharts/VerticalBarChart.js b/frontend/src/Components/NewCharts/VerticalBarChart.js index 48c52911..d2f077f6 100644 --- a/frontend/src/Components/NewCharts/VerticalBarChart.js +++ b/frontend/src/Components/NewCharts/VerticalBarChart.js @@ -77,10 +77,6 @@ export default function VerticalBarChart({ const [isChartOpen, setIsChartOpen] = useState(false); const zoomedLineChartRef = useRef(null); - if (!data.labels.length) { - return ; - } - const whiteBackground = { id: 'customVerticalBarCanvasBackgroundColor', beforeDraw: (chart, args, config) => { @@ -106,6 +102,10 @@ export default function VerticalBarChart({ const barChartModalPlugins = [whiteBackground]; const barChartModalOptions = createModalOptions(options); + if (data.loading) { + return ; + } + return ( <> diff --git a/nc/views.py b/nc/views.py index 6f0f4b0d..153dad09 100644 --- a/nc/views.py +++ b/nc/views.py @@ -544,6 +544,9 @@ def get(self, request, agency_id): if officer: qs = qs.filter(officer_id=officer) + if qs.count() == 0: + return Response(data={"labels": [], "datasets": []}, status=200) + if date_precision == "year": qs = qs.annotate(year=ExtractYear("date")) else: @@ -556,8 +559,6 @@ def get(self, request, agency_id): qs_values = [date_precision] + qs_df_cols qs = qs.values(*qs_values).annotate(count=Sum("count")).order_by(date_precision) - if qs.count() == 0: - 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( @@ -691,7 +692,16 @@ def get(self, request, agency_id): .order_by("year") ) if qs.count() == 0: - return Response(data={"labels": [], "datasets": []}, status=200) + return Response( + data={ + "labels": [], + "safety": {"labels": [], "datasets": []}, + "regulatory": {"labels": [], "datasets": []}, + "other": {"labels": [], "datasets": []}, + "max_step_size": 0, + }, + status=200, + ) df = pd.DataFrame(qs) unique_years = df.year.unique() pivot_table = pd.pivot_table( @@ -1308,9 +1318,13 @@ def get(self, request, agency_id): total_search = 0 total_stop = 0 for c in columns: - total_search += search_df[c][year] - total_stop += stops_df[c][year] - search_df[c][year] = search_df[c][year] / stops_df[c][year] + if c in search_df and c in stops_df: + total_search += search_df[c][year] or 0 + total_stop += stops_df[c][year] or 0 + try: + search_df[c][year] = float(search_df[c][year]) / float(stops_df[c][year]) + except (ValueError, ZeroDivisionError): + search_df[c][year] = 0 search_df["Average"][year] = total_search / total_stop data = self.build_response(search_df, unique_x_range) @@ -1473,14 +1487,14 @@ def get(self, request, agency_id): search_qs = search_qs.filter(year=year) stop_qs = stop_qs.filter(year=year) + if search_qs.count() == 0: + return Response(data={"labels": [], "datasets": []}, status=200) + search_qs = search_qs.values("stop_purpose", "driver_race_comb").annotate( count=Sum("count") ) stop_qs = stop_qs.values("stop_purpose", "driver_race_comb").annotate(count=Sum("count")) - if search_qs.count() == 0: - return Response(data={"labels": [], "datasets": []}, status=200) - search_df = pd.DataFrame(search_qs) stops_df = pd.DataFrame(stop_qs) From 596f0e89c2c38a0054494caf426d2d7683c4a60a Mon Sep 17 00:00:00 2001 From: Aristotel Fani Date: Tue, 6 Feb 2024 17:43:23 -0500 Subject: [PATCH 2/4] Fix various bugs preventing missing contraband table data from rendering rest of page (#263) * Add missing data label to new charts * Update contraband graph y axis labels * update subject observing methods to include state language --- .../Charts/ChartSections/EmptyChartMessage.js | 2 +- .../Charts/Contraband/Contraband.js | 36 ++++++++++++------- .../Components/Charts/Overview/Overview.js | 19 +++++----- .../Charts/SearchRate/SearchRate.js | 13 ++++--- .../Components/Charts/Searches/Searches.js | 18 +++++----- .../Charts/TrafficStops/TrafficStops.js | 25 +++++++------ .../Charts/UseOfForce/UseOfForce.js | 12 +++---- .../NewCharts/HorizontalBarChart.js | 4 +++ .../src/Components/NewCharts/LineChart.js | 2 ++ .../Components/NewCharts/VerticalBarChart.js | 6 +++- 10 files changed, 77 insertions(+), 60 deletions(-) diff --git a/frontend/src/Components/Charts/ChartSections/EmptyChartMessage.js b/frontend/src/Components/Charts/ChartSections/EmptyChartMessage.js index b683dbc9..6a028962 100644 --- a/frontend/src/Components/Charts/ChartSections/EmptyChartMessage.js +++ b/frontend/src/Components/Charts/ChartSections/EmptyChartMessage.js @@ -1,7 +1,7 @@ import React from 'react'; import * as S from './ChartsCommon.styled'; -function EmptyMessage() { +export function EmptyMessage() { return ( NO DATA AVAILABLE diff --git a/frontend/src/Components/Charts/Contraband/Contraband.js b/frontend/src/Components/Charts/Contraband/Contraband.js index 8dfc64e9..b19050bd 100644 --- a/frontend/src/Components/Charts/Contraband/Contraband.js +++ b/frontend/src/Components/Charts/Contraband/Contraband.js @@ -177,7 +177,9 @@ function Contraband(props) { .get(url) .then((res) => { const tableData = []; - const resTableData = JSON.parse(res.data.table_data); + const resTableData = res.data.table_data.length + ? JSON.parse(res.data.table_data) + : { data: [] }; resTableData.data.forEach((e) => { const dataCounts = { ...e }; delete dataCounts.year; @@ -232,7 +234,9 @@ function Contraband(props) { .get(url) .then((res) => { const tableData = []; - const resTableData = JSON.parse(res.data.table_data); + const resTableData = res.data.table_data.length + ? JSON.parse(res.data.table_data) + : { data: [] }; resTableData.data.forEach((e) => { const dataCounts = { ...e }; delete dataCounts.year; @@ -344,7 +348,7 @@ function Contraband(props) { Weapons: '#A653F4', }; const stopPurposeDataSets = data.map((sp) => ({ - labels: ['W', 'B', 'H', 'A', 'NA', 'O'], + labels: ['White', 'Black', 'Hispanic', 'Asian', 'Native American', 'Other'], datasets: sp.data.map((ds) => ({ label: ds.contraband, data: ds.data, @@ -358,16 +362,22 @@ function Contraband(props) { useEffect(() => { const params = []; + params.push({ + param: 'grouped_stop_purpose', + val: selectedGroupedContrabandStopPurpose, + }); + params.push({ + param: 'contraband_type', + val: toTitleCase(selectedGroupedContrabandType), + }); if (officerId) { params.push({ param: 'officer', val: officerId }); } const urlParams = params.map((p) => `${p.param}=${p.val}`).join('&'); - const url = `/api/agency/${agencyId}/contraband-grouped-stop-purpose/modal/?grouped_stop_purpose=${selectedGroupedContrabandStopPurpose}&contraband_type=${toTitleCase( - selectedGroupedContrabandType - )}/${urlParams}`; + const url = `/api/agency/${agencyId}/contraband-grouped-stop-purpose/modal/?${urlParams}`; axios.get(url).then((res) => { - const tableData = JSON.parse(res.data.table_data)['data']; + const tableData = res.data.table_data.length ? JSON.parse(res.data.table_data).data : []; updateGroupedContrabandModalData(tableData); }); }, [selectedGroupedContrabandStopPurpose, selectedGroupedContrabandType]); @@ -491,19 +501,19 @@ function Contraband(props) { const subjectObserving = () => { if (officerId) { - return 'officer'; + return 'by this officer'; } - if (agencyId) { - return 'department'; + if (agencyId === '-1') { + return 'for the entire state'; } - return ''; + return 'by this department'; }; - const getBarChartModalSubHeading = (title) => `${title} by this ${subjectObserving()}.`; + const getBarChartModalSubHeading = (title) => `${title} ${subjectObserving()}.`; const getBarChartModalHeading = (title, yearSelected) => { let subject = chartState.data[AGENCY_DETAILS].name; - if (subjectObserving() === 'officer') { + if (officerId) { subject = `Officer ${officerId}`; } let fromYear = ` since ${chartState.yearRange[chartState.yearRange.length - 1]}`; diff --git a/frontend/src/Components/Charts/Overview/Overview.js b/frontend/src/Components/Charts/Overview/Overview.js index 5ac676b9..37209b6a 100644 --- a/frontend/src/Components/Charts/Overview/Overview.js +++ b/frontend/src/Components/Charts/Overview/Overview.js @@ -65,18 +65,17 @@ function Overview(props) { const subjectObserving = () => { if (officerId) { - return 'officer'; + return 'by this officer'; } - if (agencyId) { - return 'department'; + if (agencyId === '-1') { + return 'for the entire state'; } - return ''; + return 'by this department'; }; const getYearPhrase = () => (year && year !== 'All' ? ` in ${year}` : ''); - const getChartModalSubHeading = (title) => - `${title} by this ${subjectObserving()}${getYearPhrase()}.`; + const getChartModalSubHeading = (title) => `${title} ${subjectObserving()}${getYearPhrase()}.`; const getOverviewSubheader = () => `Shows the race/ethnic composition of drivers ${subjectObserving()}${getYearPhrase()} reported using force against.`; @@ -131,7 +130,7 @@ function Overview(props) { const pieChartTitle = (chartTitle) => { let subject = chartState.data[AGENCY_DETAILS].name; - if (subjectObserving() === 'officer') { + if (officerId) { subject = `Officer ${officerId}`; } let title = `${chartTitle} for ${subject}`; @@ -254,7 +253,7 @@ function Overview(props) { /> - Shows the race/ethnic composition of drivers stopped by this {subjectObserving()}. + Shows the race/ethnic composition of drivers stopped {subjectObserving()}. buildUrl(slugs.TRAFFIC_STOPS_SLUG)}> View traffic stops over time @@ -281,7 +280,7 @@ function Overview(props) { /> - Shows the race/ethnic composition of drivers searched by this {subjectObserving()}. + Shows the race/ethnic composition of drivers searched {subjectObserving()}. buildUrl(slugs.SEARCHES_SLUG)}>View searches over time @@ -306,7 +305,7 @@ function Overview(props) { using force against. buildUrl(slugs.USE_OF_FORCE_SLUG)}> - View use-of-force over time + View use of force over time diff --git a/frontend/src/Components/Charts/SearchRate/SearchRate.js b/frontend/src/Components/Charts/SearchRate/SearchRate.js index 30d85b4e..28772c84 100644 --- a/frontend/src/Components/Charts/SearchRate/SearchRate.js +++ b/frontend/src/Components/Charts/SearchRate/SearchRate.js @@ -46,7 +46,6 @@ function SearchRate(props) { axios .get(url) .then((res) => { - console.log(res); setSearchRateData(res.data); }) .catch((err) => console.log(err)); @@ -66,23 +65,23 @@ function SearchRate(props) { const subjectObserving = () => { if (officerId) { - return 'officer'; + return 'by this officer'; } - if (agencyId) { - return 'department'; + if (agencyId === '-1') { + return 'for the entire state'; } - return ''; + return 'by this department'; }; const getBarChartModalSubHeading = () => `Shows the likelihood that drivers of a particular race / ethnicity are searched compared to white drivers, based on stop cause. Stops done for “safety” purposes may be less likely to show racial bias than stops done for “investigatory” - purposes by this ${subjectObserving()}.`; + purposes ${subjectObserving()}.`; const getBarChartModalHeading = (title) => { let subject = chartState.data[AGENCY_DETAILS].name; - if (subjectObserving() === 'officer') { + if (officerId) { subject = `Officer ${officerId}`; } let fromYear = ` since ${chartState.yearRange[chartState.yearRange.length - 1]}`; diff --git a/frontend/src/Components/Charts/Searches/Searches.js b/frontend/src/Components/Charts/Searches/Searches.js index e17c8d90..4219629c 100644 --- a/frontend/src/Components/Charts/Searches/Searches.js +++ b/frontend/src/Components/Charts/Searches/Searches.js @@ -112,12 +112,12 @@ function Searches(props) { const subjectObserving = () => { if (officerId) { - return 'officer'; + return 'by this officer'; } - if (agencyId) { - return 'department'; + if (agencyId === '-1') { + return 'for the entire state'; } - return ''; + return 'by this department'; }; const getLineChartModalSubHeading = (title, showStopPurpose = false) => { @@ -128,12 +128,12 @@ function Searches(props) { ? ` for ${SEARCH_TYPES[searchCountType - 1]}` : ''; } - return `${title} by this ${subjectObserving()}${stopPurposeSelected}.`; + return `${title} ${subjectObserving()}${stopPurposeSelected}.`; }; const getLineChartModalHeading = (title, showStopPurpose = false) => { let subject = chartState.data[AGENCY_DETAILS].name; - if (subjectObserving() === 'officer') { + if (officerId) { subject = `Officer ${officerId}`; } let stopPurposeSelected = ''; @@ -195,8 +195,8 @@ function Searches(props) {

- Shows the number of searches performed by the {subjectObserving()}, broken down by search - type and race / ethnicity. + Shows the number of searches performed {subjectObserving()}, broken down by search type + and race / ethnicity.

@@ -209,7 +209,7 @@ function Searches(props) { modalConfig={{ tableHeader: 'Searches By Count', tableSubheader: getLineChartModalSubHeading( - `Shows the number of searches performed by the ${subjectObserving()}, broken down by search type and race / ethnicity` + `Shows the number of searches performed ${subjectObserving()}, broken down by search type and race / ethnicity` ), agencyName: chartState.data[AGENCY_DETAILS].name, chartTitle: getLineChartModalHeading('Searches By Count', true), diff --git a/frontend/src/Components/Charts/TrafficStops/TrafficStops.js b/frontend/src/Components/Charts/TrafficStops/TrafficStops.js index 31b21dc7..0380ea9b 100644 --- a/frontend/src/Components/Charts/TrafficStops/TrafficStops.js +++ b/frontend/src/Components/Charts/TrafficStops/TrafficStops.js @@ -449,12 +449,12 @@ function TrafficStops(props) { const subjectObserving = () => { if (officerId) { - return 'officer'; + return 'by this officer'; } - if (agencyId) { - return 'department'; + if (agencyId === '-1') { + return 'for the entire state'; } - return ''; + return 'by this department'; }; const updateStopsByCount = (val) => { @@ -541,7 +541,7 @@ function TrafficStops(props) { const pieChartTitle = () => { let subject = stopsChartState.data[AGENCY_DETAILS].name; - if (subjectObserving() === 'officer') { + if (officerId) { subject = `Officer ${officerId}`; } return `Traffic Stops By Percentage for ${subject} ${ @@ -551,12 +551,12 @@ function TrafficStops(props) { const getPieChartModalSubHeading = (title) => { const yearSelected = year && year !== 'All' ? ` in ${year}` : ''; - return `${title} by this ${subjectObserving()}${yearSelected}.`; + return `${title} ${subjectObserving()}${yearSelected}.`; }; const getPieChartModalHeading = (stopPurpose) => { let subject = stopsChartState.data[AGENCY_DETAILS].name; - if (subjectObserving() === 'officer') { + if (officerId) { subject = `Officer ${officerId}`; } return `Traffic Stops By ${stopPurpose} and Race Count for ${subject} ${ @@ -574,12 +574,12 @@ function TrafficStops(props) { ? ` for ${STOP_TYPES[trafficStopsByCountPurpose - 1]}` : ''; } - return `${title} by this ${subjectObserving()}${stopPurposeSelected}.`; + return `${title} ${subjectObserving()}${stopPurposeSelected}.`; }; const getLineChartModalHeading = (title, showStopPurpose = false) => { let subject = stopsChartState.data[AGENCY_DETAILS].name; - if (subjectObserving() === 'officer') { + if (officerId) { subject = `Officer ${officerId}`; } let stopPurposeSelected = ''; @@ -596,7 +596,7 @@ function TrafficStops(props) { const stopsByPercentageModalTitle = () => { let subject = stopsChartState.data[AGENCY_DETAILS].name; - if (subjectObserving() === 'officer') { + if (officerId) { subject = `Officer ${officerId}`; } return `Traffic Stops by Percentage for ${subject} since ${stopsByPercentageData.labels[0]}`; @@ -614,8 +614,7 @@ function TrafficStops(props) { />

- Shows the race/ethnic composition of drivers stopped by this {subjectObserving()} over - time. + Shows the race/ethnic composition of drivers stopped {subjectObserving()} over time.

{getChartDetailedBreakdown()}

@@ -629,7 +628,7 @@ function TrafficStops(props) { tooltipLabelCallback={formatTooltipValue} modalConfig={{ tableHeader: 'Traffic Stops By Percentage', - tableSubheader: `Shows the race/ethnic composition of drivers stopped by this ${subjectObserving()} over time.`, + tableSubheader: `Shows the race/ethnic composition of drivers stopped ${subjectObserving()} over time.`, agencyName: stopsChartState.data[AGENCY_DETAILS].name, chartTitle: stopsByPercentageModalTitle(), }} diff --git a/frontend/src/Components/Charts/UseOfForce/UseOfForce.js b/frontend/src/Components/Charts/UseOfForce/UseOfForce.js index cf243a8f..413cd08c 100644 --- a/frontend/src/Components/Charts/UseOfForce/UseOfForce.js +++ b/frontend/src/Components/Charts/UseOfForce/UseOfForce.js @@ -56,17 +56,17 @@ function UseOfForce(props) { const subjectObserving = () => { if (officerId) { - return 'whom this officer'; + return 'by this officer'; } - if (agencyId) { - return 'whom law enforcement officers'; + if (agencyId === '-1') { + return 'for the entire state'; } - return ''; + return 'by this department'; }; useEffect(() => { let url = `/api/agency/${agencyId}/use-of-force/`; - if (officerId !== null) { + if (officerId) { url = `${url}?officer=${officerId}`; } axios @@ -123,7 +123,7 @@ function UseOfForce(props) { const chartModalTitle = (displayYear = true) => { let subject = chartState.data[AGENCY_DETAILS].name; - if (subjectObserving() === 'officer') { + if (officerId) { subject = `Officer ${officerId}`; } let yearOf = `since ${useOfForceBarData.labels[0]}`; diff --git a/frontend/src/Components/NewCharts/HorizontalBarChart.js b/frontend/src/Components/NewCharts/HorizontalBarChart.js index baa19e95..2873e976 100644 --- a/frontend/src/Components/NewCharts/HorizontalBarChart.js +++ b/frontend/src/Components/NewCharts/HorizontalBarChart.js @@ -5,6 +5,7 @@ import { usePopper } from 'react-popper'; import { tooltipLanguage } from '../../util/tooltipLanguage'; import styled from 'styled-components'; import ChartModal from './ChartModal'; +import { EmptyMessage } from '../Charts/ChartSections/EmptyChartMessage'; export const Tooltip = styled.div` background: #333; @@ -167,6 +168,8 @@ export default function HorizontalBarChart({ return ; } + const noData = data.datasets.every((d) => d.data.every((v) => v === 0)); + return ( <> {displayStopPurposeTooltips && ( @@ -181,6 +184,7 @@ export default function HorizontalBarChart({ )} + {noData && } )} + {!data.labels.length && } - +
+ {!data.labels.length && } + +
setIsChartOpen(false)} From 15363ebc228a50eba3c985d2b9a08dccb202cccd Mon Sep 17 00:00:00 2001 From: Aristotel Fani Date: Tue, 6 Feb 2024 18:07:25 -0500 Subject: [PATCH 3/4] Remove victory package (#264) --- frontend/package-lock.json | 634 +----------------- frontend/package.json | 3 +- .../Components/Charts/ChartPrimitives/Bar.js | 40 -- .../Charts/ChartPrimitives/ChartBase.js | 81 --- .../ChartPrimitives/ChartBase.styled.js | 12 - .../Charts/ChartPrimitives/ChartLoading.js | 18 - .../ChartPrimitives/ChartLoading.styled.js | 11 - .../Charts/ChartPrimitives/CopwatchChart.js | 112 ---- .../ChartPrimitives/CopwatchChart.styled.js | 45 -- .../Charts/ChartPrimitives/GroupedBar.js | 84 --- .../Components/Charts/ChartPrimitives/Line.js | 63 -- .../Components/Charts/ChartPrimitives/Pie.js | 47 -- .../ResponsiveChartContainer.styled.js | 21 - .../Charts/ChartPrimitives/StackedBar.js | 61 -- .../Charts/ChartPrimitives/chartConstants.js | 24 - frontend/src/Components/Charts/chartUtils.js | 3 - 16 files changed, 4 insertions(+), 1255 deletions(-) delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/Bar.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/ChartBase.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/ChartBase.styled.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/ChartLoading.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/ChartLoading.styled.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/CopwatchChart.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/CopwatchChart.styled.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/GroupedBar.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/Line.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/Pie.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/ResponsiveChartContainer.styled.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/StackedBar.js delete mode 100644 frontend/src/Components/Charts/ChartPrimitives/chartConstants.js diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c7749645..4ffcca6d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -38,8 +38,7 @@ "reaktus": "^0.1.20", "styled-components": "^5.3.5", "styled-system": "^5.1.5", - "tinycolor2": "^1.4.2", - "victory": "^36.5.3" + "tinycolor2": "^1.4.2" }, "devDependencies": { "@testing-library/react": "^13.3.0", @@ -3620,60 +3619,6 @@ "@types/node": "*" } }, - "node_modules/@types/d3-array": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz", - "integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==" - }, - "node_modules/@types/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", - "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", - "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.0.tgz", - "integrity": "sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", - "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" - }, "node_modules/@types/eslint": { "version": "8.4.5", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", @@ -6477,121 +6422,6 @@ "node": ">=12.0.0" } }, - "node_modules/d3-array": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", - "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", - "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", - "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-voronoi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", - "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -6759,19 +6589,6 @@ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==" }, - "node_modules/delaunator": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz", - "integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==" - }, - "node_modules/delaunay-find": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/delaunay-find/-/delaunay-find-0.0.6.tgz", - "integrity": "sha512-1+almjfrnR7ZamBk0q3Nhg6lqSe6Le4vL0WJDSMx4IDbQwTpUTXPjxC00lqLBT8MYsJpPCbI16sIkw9cPsbi7Q==", - "dependencies": { - "delaunator": "^4.0.0" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -9417,14 +9234,6 @@ "node": ">= 0.4" } }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" - } - }, "node_modules/ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -10944,7 +10753,8 @@ "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true }, "node_modules/json5": { "version": "2.2.3", @@ -16269,444 +16079,6 @@ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, - "node_modules/victory": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory/-/victory-36.5.3.tgz", - "integrity": "sha512-YzDstb6Cj3IeXGKs4mjeIP2xbpv9bCYfI0cQzj/oc7RutLHJGrOyKjUxtSjbZSeQlzM3iaxN39HoxgXBpXzUKA==", - "dependencies": { - "victory-area": "^36.5.3", - "victory-axis": "^36.5.3", - "victory-bar": "^36.5.3", - "victory-box-plot": "^36.5.3", - "victory-brush-container": "^36.5.3", - "victory-brush-line": "^36.5.3", - "victory-candlestick": "^36.5.3", - "victory-canvas": "^36.5.3", - "victory-chart": "^36.5.3", - "victory-core": "^36.5.3", - "victory-create-container": "^36.5.3", - "victory-cursor-container": "^36.5.3", - "victory-errorbar": "^36.5.3", - "victory-group": "^36.5.3", - "victory-histogram": "^36.5.3", - "victory-legend": "^36.5.3", - "victory-line": "^36.5.3", - "victory-pie": "^36.5.3", - "victory-polar-axis": "^36.5.3", - "victory-scatter": "^36.5.3", - "victory-selection-container": "^36.5.3", - "victory-shared-events": "^36.5.3", - "victory-stack": "^36.5.3", - "victory-tooltip": "^36.5.3", - "victory-voronoi": "^36.5.3", - "victory-voronoi-container": "^36.5.3", - "victory-zoom-container": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-area": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-area/-/victory-area-36.5.3.tgz", - "integrity": "sha512-USJmbNQhWGwsrQ64S8mfVuBvMH0t00KJskIMa48JbcyVi5jpyI7lG7+F3Zs2B/6D28wtON9IzMb45FuKBSLd/w==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3", - "victory-vendor": "^36.5.1" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-axis": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-axis/-/victory-axis-36.5.3.tgz", - "integrity": "sha512-ciDNdBOD2Lmmkrpgap7ta1KVK7ZZXvLU+iQ6O8E3sEKjVyxOMkZImoSxwH29GRqmEmGIpxULlH9MmaEBlSrHKg==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-bar": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-36.5.3.tgz", - "integrity": "sha512-eGXubJEs69IAH8DSvZi24xwBFOrdwGB8vojsH+Xolb8N2d2OkHqGwwUeFQ1JNr1TKnJAtHiZY8MiHxIwe1p20A==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3", - "victory-vendor": "^36.5.1" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-box-plot": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-box-plot/-/victory-box-plot-36.5.3.tgz", - "integrity": "sha512-dwJRWVyeR0Qd8hPp5PPptBWDo4PZdi/v2Op3oNKdUkrlSfw/TU3u1BN3NviAX2kJJSOahoD+LMN+EyhwSpscGA==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3", - "victory-vendor": "^36.5.1" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-brush-container": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-36.5.3.tgz", - "integrity": "sha512-4OTSkAngc5ejXZ4zPUCa/mUyy/KfVyUbisNdVNWA6clBUpMLGj+ICPSFgEWiqQ4fCCzctMRSpV8DmupD8EiBjQ==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-brush-line": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-brush-line/-/victory-brush-line-36.5.3.tgz", - "integrity": "sha512-ulBUWFz11m4Omp74SFVCKTYxw7EeCxutCeKX1jkeLy0ptuto0zXkYw++n++0/rzh5hMacM3+JIDERef3eH0mbA==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-candlestick": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-candlestick/-/victory-candlestick-36.5.3.tgz", - "integrity": "sha512-+AeQcGJaA/630KJXsa21MYaewUzWBArgsEHUFqsSE2LlW+pqqYN8vWrMZ5Vh26Df4by8Iwb+pL2xq3ng3AobGg==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-canvas": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-canvas/-/victory-canvas-36.5.3.tgz", - "integrity": "sha512-oS78ApPkySbd5l4xsV7AGaT+wAVH1+dV6+Pw6UHJmjyLCVmUFV9lo1EerciGSYzEOTTm3De/9oadMBf6EG91Kg==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-chart": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-36.5.3.tgz", - "integrity": "sha512-CNTQT37VtpuEGiSC9hsNTMr001t1bBfqD+ZK/WQFltIfacje+bVGacBHhoUT23Me3mp/PyGNUGWTzBtGv7DjXw==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "react-fast-compare": "^3.2.0", - "victory-axis": "^36.5.3", - "victory-core": "^36.5.3", - "victory-polar-axis": "^36.5.3", - "victory-shared-events": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-core": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.5.3.tgz", - "integrity": "sha512-UBT445zetqGDVZf7sD/wGVf80f4aQd0vXa7uB4jW3MQl3LcwXN9LaDGmztDc6qwFL4FiUiwy2TS9e9L1PD6UBw==", - "dependencies": { - "lodash": "^4.17.21", - "prop-types": "^15.8.1", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.5.1" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-create-container": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-36.5.3.tgz", - "integrity": "sha512-vhsR2gMp5B5YHMzrae1fwGq9pBu9zJPb8EnssFbo2HspTjrdlI4JiUABmwjhmOtQ0Wzqy6J3EKbuzu6m2YcNEQ==", - "dependencies": { - "lodash": "^4.17.19", - "victory-brush-container": "^36.5.3", - "victory-core": "^36.5.3", - "victory-cursor-container": "^36.5.3", - "victory-selection-container": "^36.5.3", - "victory-voronoi-container": "^36.5.3", - "victory-zoom-container": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-cursor-container": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-36.5.3.tgz", - "integrity": "sha512-mSVNUw1YAz3iK6TEZUkTAvY1cl4xQTfhv6iAOxAbpdfSJsmbXEJTDeMp0j/mjjcKOq3t0H+Hf/rNyIpVEMnHqQ==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-errorbar": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-errorbar/-/victory-errorbar-36.5.3.tgz", - "integrity": "sha512-wm/xm+x20ZEFpu1hamvFH9dkoQRyfM8coPrhcLS1llCeaf6+ybLLokQUi9+Xj06mVpL1tNYVHjlKd2J43VG3zA==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-group": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-36.5.3.tgz", - "integrity": "sha512-5w0r7gRvvX1Gz+UAbQmP7E7cdyMYbY1Ui2j/sp3BPkmEeYxkYSsDHFE0ztSrexL+fcX/Mm6L/5qmdYVn+vNfQA==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.5.3", - "victory-shared-events": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-histogram": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-histogram/-/victory-histogram-36.5.3.tgz", - "integrity": "sha512-c9B4HeLlXzBd4Vtaxebu/0fHjj4AMzbW2OUlHO3PMhB6NKBBlxLLarK0lmTDwlZbavsdW0fif4ALoIqubFveJw==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "react-fast-compare": "^3.2.0", - "victory-bar": "^36.5.3", - "victory-core": "^36.5.3", - "victory-vendor": "^36.5.1" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-legend": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-36.5.3.tgz", - "integrity": "sha512-MkmQq7UiX7udi2BXKx2B5PPPsFafye8yjVUNDPhSD+oh0plWoJID4vbcnru34Hnu5pDTi1xCMyThVzc5HMfwDw==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-line": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-36.5.3.tgz", - "integrity": "sha512-8VE3gz4KCKdngOurRewkCNkjIf7kwKInULZ7DoLb+KDnmnzrk/TXqga33TS4hhQyr5vlBzxF5P4nJwY/KGUNfw==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3", - "victory-vendor": "^36.5.1" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-pie": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-36.5.3.tgz", - "integrity": "sha512-0p7yIDQVzz5eUs4BMrwN2DdLMETAuK6ZvegVcIgPPGRKXi+NqRz996eqybaCVSDf1Un8PpL8OW5bH9CT8jfDAQ==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3", - "victory-vendor": "^36.5.1" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-polar-axis": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-polar-axis/-/victory-polar-axis-36.5.3.tgz", - "integrity": "sha512-88UDm4sSsqs42cX7XTpagEVAdoNmLYXB8JJXYBU98vM4hNET/YDNqHRgAZzz4CeGjxyeJw2+riXqAH5nA7vHKA==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-scatter": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-36.5.3.tgz", - "integrity": "sha512-/NG7O1BErpNH2umgkWZXT9bIj9l/T5McqbJzmeA9kk2x6OELnA9cR54zb+5yQrx/pcZPkuk+mlVQdNKoOOGnhA==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-selection-container": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-36.5.3.tgz", - "integrity": "sha512-JkZ7IPf3682G7HVhEdLusIo4q3jSp6LvPxtS4PvjFq4pYGPrnLJxiaDxaDin4qSVGoIKKfwDG1XJHmgJUQzluA==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-shared-events": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-36.5.3.tgz", - "integrity": "sha512-VBr6jd37e+MrCRaDC0qYy69PhLeouhWB8vvc1TsPT5Qdg5Rgcpo9Pr4S2FAatOGEX+BequQH399EC6gQFnNouQ==", - "dependencies": { - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-stack": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-36.5.3.tgz", - "integrity": "sha512-SU8lFlCiTmNmaj7p1sCPnDqvT5FkQ63XfZ+tcSseyQsl5FWl1XGd1NowM1zPvW5+cVicpdccSFtVs5fJBeOkoA==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.5.3", - "victory-shared-events": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-tooltip": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-36.5.3.tgz", - "integrity": "sha512-Hr0VQsVE8EaJVZ2oqQFpLL2EF1oUxwTjlGtfGLgb3Uwinok5/U5XBVfHNm943QuHLOhYQXgIHOAY2Ip2j9xLRg==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-vendor": { - "version": "36.5.1", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.5.1.tgz", - "integrity": "sha512-VMGEnIOs6GWMxdkWeEyc80s9MfI3LN9kIKdUT0bJOmxqh3pRfK7NmpYxq0ARazu0ZfeTW9dYAVuehGYBwgQB/w==", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "node_modules/victory-voronoi": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-voronoi/-/victory-voronoi-36.5.3.tgz", - "integrity": "sha512-qWVL4KT7BnCXG3WfgZFt3OFd9oom70M4ShJD4zdvVu1RobgOskBoc18MELf7tWFBvk+xyZID47CYCKaOuMhfLw==", - "dependencies": { - "d3-voronoi": "^1.1.4", - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-voronoi-container": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-36.5.3.tgz", - "integrity": "sha512-Gv/T1wwkHPA9zDbiPeibhncfgbBsMhj4KX07YbxCil/ZLpB1STMfhVmlKodrxVcINkBNhRWxm7eXWsn47hTdGg==", - "dependencies": { - "delaunay-find": "0.0.6", - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "react-fast-compare": "^3.2.0", - "victory-core": "^36.5.3", - "victory-tooltip": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-zoom-container": { - "version": "36.5.3", - "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-36.5.3.tgz", - "integrity": "sha512-+KCkXy/5Qf58dPINLHMTYMCCvuBGF6tn6a+K1K+7ykgwp/xFUJDajZ+0LGIIbHJtiZitK1v/0O7pZL030u4EEA==", - "dependencies": { - "lodash": "^4.17.19", - "prop-types": "^15.8.1", - "victory-core": "^36.5.3" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5b64b800..3a921b94 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,8 +34,7 @@ "reaktus": "^0.1.20", "styled-components": "^5.3.5", "styled-system": "^5.1.5", - "tinycolor2": "^1.4.2", - "victory": "^36.5.3" + "tinycolor2": "^1.4.2" }, "devDependencies": { "@testing-library/react": "^13.3.0", diff --git a/frontend/src/Components/Charts/ChartPrimitives/Bar.js b/frontend/src/Components/Charts/ChartPrimitives/Bar.js deleted file mode 100644 index 37d9749c..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/Bar.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { VictoryAxis, VictoryBar, VictoryChart, VictoryContainer } from 'victory'; -import { AXIS_STYLE } from './chartConstants'; -import ChartLoading from './ChartLoading'; -import BarSkeleton from '../../Elements/Skeletons/BarSkeleton'; -import EmptyChartMessage from '../ChartSections/EmptyChartMessage'; - -function Bar({ data, chartProps, xAxisProps, yAxisProps, barProps }) { - if (!data) return ; - return ( - <> - - } - > - - - - - - ); -} - -Bar.propTypes = { - chartProps: PropTypes.instanceOf(Object), - yAxisProps: PropTypes.instanceOf(Object), - xAxisProps: PropTypes.instanceOf(Object), - barProps: PropTypes.instanceOf(Object), -}; - -Bar.defaultProps = { - chartProps: {}, - yAxisProps: {}, - xAxisProps: {}, - barProps: {}, -}; - -export default Bar; diff --git a/frontend/src/Components/Charts/ChartPrimitives/ChartBase.js b/frontend/src/Components/Charts/ChartPrimitives/ChartBase.js deleted file mode 100644 index 65fb39ee..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/ChartBase.js +++ /dev/null @@ -1,81 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import ChartBaseStyled from './ChartBase.styled'; - -// Children -import ResponsiveChartContainer from './ResponsiveChartContainer.styled'; -import Legend from '../ChartSections/Legend/Legend'; -import DataLoading from './DataLoading'; - -function ChartBase({ - children, - datasetKey, - hideLegend, - chartState, - chartTitle, - mapData, - groupKeys, - getLabelFromKey, - renderAdditionalFilter, - ...props -}) { - const [keysToShow, setKeysToShow] = useState(groupKeys); - const [data, setData] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [hasError, setHasError] = useState(false); - - useEffect(() => { - const mapped = mapData(keysToShow); - setData(mapped); - }, [keysToShow, mapData]); - - const handleLegendKeyClick = (keyId) => { - let newKeys = [...keysToShow]; - if (keysToShow.includes(keyId)) newKeys = newKeys.filter((k) => k !== keyId); - else newKeys.push(keyId); - setKeysToShow(newKeys); - }; - - // set isLoading - useEffect(() => { - if (Array.isArray(datasetKey)) { - const loadingStates = datasetKey.map((dk) => chartState?.loading[dk]); - setIsLoading(loadingStates.some(Boolean)); - } else setIsLoading(chartState?.loading[datasetKey]); - }, [chartState?.loading[datasetKey]]); - - // set hasError - useEffect(() => { - if (Array.isArray(datasetKey)) { - const errorsPresent = datasetKey.map((dk) => chartState?.errors[dk]); - setHasError(errorsPresent.some((e) => e)); - } else setHasError(chartState?.errors[datasetKey]); - }, [chartState?.errors[datasetKey]]); - - return ( - - {hasError &&

some chart error message

} - {isLoading && } -

{chartTitle}

- {!hideLegend && ( - - )} - {renderAdditionalFilter && renderAdditionalFilter()} - - {data.length > 0 && React.cloneElement(children, { data })} - -
- ); -} - -export default ChartBase; diff --git a/frontend/src/Components/Charts/ChartPrimitives/ChartBase.styled.js b/frontend/src/Components/Charts/ChartPrimitives/ChartBase.styled.js deleted file mode 100644 index 026755eb..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/ChartBase.styled.js +++ /dev/null @@ -1,12 +0,0 @@ -import styled from 'styled-components'; -import { motion } from 'framer-motion'; -import { smallerThanTabletLandscape } from '../../../styles/breakpoints'; - -export default styled(motion.article)` - width: 100%; - padding: 2rem 2rem; - - @media (${smallerThanTabletLandscape}) { - padding: 2rem 0; - } -`; diff --git a/frontend/src/Components/Charts/ChartPrimitives/ChartLoading.js b/frontend/src/Components/Charts/ChartPrimitives/ChartLoading.js deleted file mode 100644 index d269d283..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/ChartLoading.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import ChartLoadingStyle from './ChartLoading.styled'; - -// Hooks -import useOfficerId from '../../../Hooks/useOfficerId'; - -function ChartLoading({ skeleton: Skeleton }) { - const officerId = useOfficerId(); - - return ( - -

Loading {officerId ? 'Officer' : 'Agency'} data...

- -
- ); -} - -export default ChartLoading; diff --git a/frontend/src/Components/Charts/ChartPrimitives/ChartLoading.styled.js b/frontend/src/Components/Charts/ChartPrimitives/ChartLoading.styled.js deleted file mode 100644 index 40ba2fb5..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/ChartLoading.styled.js +++ /dev/null @@ -1,11 +0,0 @@ -import styled from 'styled-components'; - -export default styled.div` - padding: 2rem 0; - h3 { - text-align: center; - font-size: 28px; - font-weight: 200; - color: ${(props) => props.theme.colors.grey}; - } -`; diff --git a/frontend/src/Components/Charts/ChartPrimitives/CopwatchChart.js b/frontend/src/Components/Charts/ChartPrimitives/CopwatchChart.js deleted file mode 100644 index 617112b6..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/CopwatchChart.js +++ /dev/null @@ -1,112 +0,0 @@ -import React from 'react'; -import * as S from './CopwatchChart.styled'; -import { VictoryChart, VictoryVoronoiContainer, VictoryTooltip } from 'victory'; - -function CopwatchChart({ children, ...props }) { - return ( - }> - {children} - - ); -} - -export default CopwatchChart; - -export function CopwatchTooltip({ - yAxisLabel, - transformCenter, - pie, - tooltipFontSize = null, - ...props -}) { - return ( - - } - /> - ); -} - -CopwatchTooltip.defaultEvents = VictoryTooltip.defaultEvents; - -const VORONOI_LABEL_X_OFFSET = 10; -const defaultYAxisLabel = (value) => value; -const defaultTransformCenter = ({ x, y, datum }) => ({ - x: x + VORONOI_LABEL_X_OFFSET, - y: y - datum.height, -}); - -export function CopwatchChartFlyout({ - yAxisLabel = defaultYAxisLabel, - transformCenter = defaultTransformCenter, - pie, - tooltipFontSize, - ...data -}) { - const centered = transformCenter({ x: data.center.x, y: data.center.y, datum: data }); - - const isVoronoi = !!data.activePoints; - - const getColor = (d) => { - if (pie) { - return d.datum.color; - } - if (isVoronoi) { - return d.color || d.style.data.stroke; - } - return d.datum.color; - }; - - const getLabel = (d) => { - if (pie) { - return d.datum.x; - } - if (isVoronoi) { - return d.displayName; - } - return d.datum.ethnicGroup; - }; - - const getValue = (d) => { - if (pie) { - return yAxisLabel(d.datum.y); - } - if (isVoronoi) { - return yAxisLabel(d.y); - } - return yAxisLabel(d.datum.y); - }; - - return ( - - - {!pie && {data.datum.x}} - - {data.activePoints?.map((p) => ( - - {getLabel(p)}: - {getValue(p)} - - )) || ( - - {getLabel(data)}: - {getValue(data)} - - )} - - - - ); -} diff --git a/frontend/src/Components/Charts/ChartPrimitives/CopwatchChart.styled.js b/frontend/src/Components/Charts/ChartPrimitives/CopwatchChart.styled.js deleted file mode 100644 index 1f627c5c..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/CopwatchChart.styled.js +++ /dev/null @@ -1,45 +0,0 @@ -import styled from 'styled-components'; -import { darken } from '../../../styles/styleUtils/lighten-darken'; - -export const FlyoutContainer = styled.div` - background: ${(props) => props.theme.colors.white}; - color: ${(props) => props.theme.colors.black}; - width: fit-content; - ${(props) => (props.fontSize ? `font-size: ${props.fontSize}px;` : '')} - padding: 4px; - border-radius: ${(props) => props.theme.radii.standard}px; - border-style: solid; - border-width: thin; -`; - -export const FlyoutLabel = styled.h4` - font-family: ${(props) => props.theme.fonts.heading}; - margin-bottom: 2px; -`; - -export const DataList = styled.ul` - padding: 0; - margin: 0; - list-style: none; - font-family: ${(props) => props.theme.fonts.body}; - font-size: 12px; - font-weight: bold; -`; - -export const DataListItem = styled.li` - color: ${(props) => darken(props.color, 10)}; - display: flex; - ${(props) => (props.fontSize ? `font-size: ${props.fontSize}px;` : '')} - - &:not(:last-child) { - margin-bottom: 5px; - } -`; - -export const DatumLabel = styled.span` - flex: 1; -`; - -export const DatumValue = styled.span` - margin-left: 20px; -`; diff --git a/frontend/src/Components/Charts/ChartPrimitives/GroupedBar.js b/frontend/src/Components/Charts/ChartPrimitives/GroupedBar.js deleted file mode 100644 index 0d49d549..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/GroupedBar.js +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { - VictoryChart, - VictoryGroup, - VictoryBar, - VictoryAxis, - VictoryContainer, - VictoryTooltip, -} from 'victory'; -import { AXIS_STYLE } from './chartConstants'; -import ChartLoading from './ChartLoading'; -import EmptyChartMessage from '../ChartSections/EmptyChartMessage'; -import BarSkeleton from '../../Elements/Skeletons/BarSkeleton'; - -function GroupedBar({ - data, - loading, - horizontal, - iTickValues, - iTickFormat, - chartProps, - dAxisProps, - iAxisProps, - barProps, - toolTipFontSize, -}) { - if (loading) return ; - - return ( - <> - - } - > - - - - {data.map((bar) => ( - - `${datum.ethnicGroup}, ${datum.x}, ${dAxisProps.tickFormat(datum.y)}` - } - labelComponent={ - - } - {...barProps} - /> - ))} - - - - ); -} - -GroupedBar.propTypes = { - horizontal: PropTypes.bool, -}; - -GroupedBar.defaultProps = { - horizontal: false, -}; - -export default GroupedBar; diff --git a/frontend/src/Components/Charts/ChartPrimitives/Line.js b/frontend/src/Components/Charts/ChartPrimitives/Line.js deleted file mode 100644 index ec2c3c31..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/Line.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; - -// Constants -import { AXIS_STYLE } from './chartConstants'; - -// Deps -import { VictoryLine, VictoryAxis, VictoryTooltip } from 'victory'; -import ChartLoading from './ChartLoading'; -import BarSkeleton from '../../Elements/Skeletons/BarSkeleton'; -import EmptyChartMessage from '../ChartSections/EmptyChartMessage'; -import CopwatchChart from './CopwatchChart'; - -function Line({ - data = [], - loading, - iTickValues, - iTickFormat, - dTickValues, - dTickFormat, - dAxisProps = {}, - iAxisProps = {}, - yAxisLabel, - xAxisLabel = 'Year', -}) { - if (loading) return ; - - return ( - <> - - - - - {data.map((lineData) => ( - - `${datum.x}, ${datum.displayName}, ${dAxisProps.tickFormat(datum.y)}` - } - labelComponent={} - /> - ))} - - - ); -} - -export default Line; diff --git a/frontend/src/Components/Charts/ChartPrimitives/Pie.js b/frontend/src/Components/Charts/ChartPrimitives/Pie.js deleted file mode 100644 index 99f25038..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/Pie.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -// Elements -import PieSkeleton from '../../Elements/Skeletons/PieSkeleton'; -import { VictoryPie } from 'victory'; -import { P, WEIGHTS } from '../../../styles/StyledComponents/Typography'; -import { CopwatchTooltip } from './CopwatchChart'; -import ChartLoading from './ChartLoading'; - -const PIE_STYLES = { - data: { - fill: ({ datum }) => datum.color, - }, - parent: { touchAction: 'auto' }, -}; - -function Pie({ data, loading }) { - const _dataIsZeros = (d) => d.length === 0 || d.every((dt) => dt.y === 0); - - if (loading) return ; - - if (_dataIsZeros(data)) { - return ( - -

No data

-
- ); - } - - return ( - `${val}%`} />} - labels={() => ' '} - labelRadius={({ innerRadius }) => innerRadius + 80} - /> - ); -} - -const NoData = styled.div` - text-align: center; - margin: 5em auto; -`; - -export default Pie; diff --git a/frontend/src/Components/Charts/ChartPrimitives/ResponsiveChartContainer.styled.js b/frontend/src/Components/Charts/ChartPrimitives/ResponsiveChartContainer.styled.js deleted file mode 100644 index 91169348..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/ResponsiveChartContainer.styled.js +++ /dev/null @@ -1,21 +0,0 @@ -import styled from 'styled-components'; -import { - smallerThanDesktop, - smallerThanTabletLandscape, - phoneOnly, -} from '../../../styles/breakpoints'; - -export default styled.div` - height: 700px; - /* padding: 2rem 0; */ - - @media (${smallerThanDesktop}) { - height: 600px; - } - @media (${smallerThanTabletLandscape}) { - height: 500px; - } - @media (${phoneOnly}) { - height: 100vw; - } -`; diff --git a/frontend/src/Components/Charts/ChartPrimitives/StackedBar.js b/frontend/src/Components/Charts/ChartPrimitives/StackedBar.js deleted file mode 100644 index a0b4dce4..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/StackedBar.js +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { VictoryStack, VictoryBar, VictoryAxis, VictoryTooltip, VictoryChart } from 'victory'; -import { AXIS_STYLE } from './chartConstants'; - -// Children -import ChartLoading from './ChartLoading'; -import BarSkeleton from '../../Elements/Skeletons/BarSkeleton'; -import EmptyChartMessage from '../ChartSections/EmptyChartMessage'; - -function StackedBar({ data, loading, tickValues, yAxisLabel }) { - if (loading) return ; - - return ( - <> - - - (t % 20 === 0 ? `${t}%` : null)} - labels={({ datum }) => `HEY: ${datum.y} ${datum.x}`} - labelComponent={} - /> - (t % 2 === 0 ? t : null)} - /> - - {data.map((bar) => ( - (datum ? `${datum.x}, ${datum.displayName}, ${datum.y}%` : '')} - labelComponent={} - /> - ))} - - - - ); -} - -StackedBar.propTypes = { - data: PropTypes.instanceOf(Array).isRequired, - tickValues: PropTypes.arrayOf(PropTypes.number), -}; - -StackedBar.defaultProps = { - tickValues: [], -}; - -export default StackedBar; diff --git a/frontend/src/Components/Charts/ChartPrimitives/chartConstants.js b/frontend/src/Components/Charts/ChartPrimitives/chartConstants.js deleted file mode 100644 index ee718050..00000000 --- a/frontend/src/Components/Charts/ChartPrimitives/chartConstants.js +++ /dev/null @@ -1,24 +0,0 @@ -export const AXIS_STYLE = { - grid: { stroke: '#818e99', strokeWidth: 0.5 }, - tickLabels: { fontSize: 8 }, -}; - -export const CHART_ANIMATION = { duration: 500, easing: 'linear' }; - -export const TOOLTIP_PROPS = { - flyoutWidth: 95, - flyoutHeight: 35, - cornerRadius: 6, - pointerLength: 40, - flyoutStyle: { - stroke: '#868C97', - strokeWidth: 2, - fill: '#FFFFFF', - }, - style: { - fill: '#868C97', - fontSize: 8, - fontWeight: 500, - textAnchor: 'middle', - }, -}; diff --git a/frontend/src/Components/Charts/chartUtils.js b/frontend/src/Components/Charts/chartUtils.js index 5923a6f0..a434c565 100644 --- a/frontend/src/Components/Charts/chartUtils.js +++ b/frontend/src/Components/Charts/chartUtils.js @@ -69,9 +69,6 @@ export function reduceYearsToTotal(data, ethnicGroup) { })); } -export function filterSinglePurpose(data, purpose) { - return data.filter((d) => d.purpose === purpose); -} /** * Given an Array of objects with shape { year, asian, black, etc. }, reduce to percentages of total by race. * provide Theme object to provide fill colors. From cc76e97affb8c5df9dc2130d87441d0b82c8235f Mon Sep 17 00:00:00 2001 From: Aristotel Fani Date: Thu, 8 Feb 2024 18:20:40 -0500 Subject: [PATCH 4/4] Remove int path convert for all urls, statewide agency id is a negative id and would be redirected, crashing the contraband page (#265) --- nc/urls.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nc/urls.py b/nc/urls.py index bfaae6ee..6a9a23ad 100755 --- a/nc/urls.py +++ b/nc/urls.py @@ -51,27 +51,27 @@ name="search-rate", ), path( - "api/agency//contraband/", + "api/agency//contraband/", views.AgencyContrabandView.as_view(), name="contraband-percentages", ), path( - "api/agency//contraband-types/", + "api/agency//contraband-types/", views.AgencyContrabandTypesView.as_view(), name="contraband-type-percentages", ), path( - "api/agency//contraband-stop-purpose/", + "api/agency//contraband-stop-purpose/", views.AgencyContrabandStopPurposeView.as_view(), name="contraband-percentages-stop-purpose-groups", ), path( - "api/agency//contraband-grouped-stop-purpose/", + "api/agency//contraband-grouped-stop-purpose/", views.AgencyContrabandGroupedStopPurposeView.as_view(), name="contraband-percentages-grouped-stop-purpose", ), path( - "api/agency//contraband-grouped-stop-purpose/modal/", + "api/agency//contraband-grouped-stop-purpose/modal/", views.AgencyContrabandStopGroupByPurposeModalView.as_view(), name="contraband-percentages-grouped-stop-purpose-modal", ),