From db9c0f20fdb444cb1e15fd00937b6ddfd7c0de58 Mon Sep 17 00:00:00 2001 From: Alejandro Osornio <50227494+AOx0@users.noreply.github.com> Date: Tue, 14 Nov 2023 21:28:19 -0600 Subject: [PATCH] Add 3 pie charts --- assets/script.js | 244 ++++++++++++++++++++++++++++++++++++++----- assets/tailwind.css | 4 - src/bin/api.rs | 119 ++++++++++++++++++--- templates/hello.html | 16 ++- 4 files changed, 336 insertions(+), 47 deletions(-) diff --git a/assets/script.js b/assets/script.js index 19c0b10..a0dc690 100644 --- a/assets/script.js +++ b/assets/script.js @@ -166,6 +166,94 @@ function init_draw_pinned_chart(num, data, cfg) { }); } +function load_razon_anio2(data, cfg) { + let input_fini = document.getElementById(`afini-${cfg['num']}`); + let input_init = document.getElementById(`ainit-${cfg['num']}`); + + if (input_fini != undefined && input_fini != undefined) { + let err = false; + + if (data['annio_inicio'] < 2016 || data['annio_inicio'] > 2023) { + input_init.classList.add("text-rose-300") + err = true + } else { + input_init.classList.remove("text-rose-300") + }; + + if (data['annio_final'] < 2016 || data['annio_final'] > 2023) { + input_fini.classList.add("text-rose-300") + err = true + } else { + input_fini.classList.remove("text-rose-300") + }; + + if (err) return; + } + + fetch(cfg.endpoint, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data) + } + ) + .then((response) => response.json()) + .then((json) => { + const ctx = document.getElementById(`pie-alto-${cfg.num}`); + + if( window.myBar5 === undefined) { + window.myBar5 = new Chart(ctx, { + type: 'pie', + data: { + labels: json["nombres"], + datasets: [{ + label: "", + data: json["valores"], + borderWidth: 1 + }], + }, + options: { + gridLines: { + color: "rgba(0, 0, 0, 0)", + }, + plugins: { + title: { + display: false + }, + legend: { + display: false + } + }, + scales: { + y: { + beginAtZero: true, + display: false, + grid: { + display: false, + }, + }, + x: { + display: false, + grid: { + display: false, + }, + } + } + } + }); + } else { + window.myBar5.data.datasets = [{ + label: "", + data: json["valores"], + borderWidth: 1 + }]; + window.myBar5.data.labels = json["nombres"]; + window.myBar5.update(); + } + }); +} function load_razon_anio(data, cfg) { let input_fini = document.getElementById(`afini-${cfg['num']}`); @@ -206,43 +294,51 @@ function load_razon_anio(data, cfg) { if( window.myBar4 === undefined) { window.myBar4 = new Chart(ctx, { - type: 'pie', - data: { - labels: ["Bajo", "Alto"], - datasets: [{ - label: "Razon delitos de alto y bajo impacto", - data: [json.bajo, json.alto], - borderWidth: 1 - }], - }, - options: { - gridLines: { - color: "rgba(0, 0, 0, 0)", - }, - scales: { - y: { - beginAtZero: true, - display: false, - grid: { - display: false, - }, + type: 'pie', + data: { + labels: json["nombres"], + datasets: [{ + label: "", + data: json["valores"], + borderWidth: 1 + }], + }, + options: { + gridLines: { + color: "rgba(0, 0, 0, 0)", + }, + plugins: { + title: { + display: false + }, + legend: { + display: false + } }, - x: { - display: false, - grid: { + scales: { + y: { + beginAtZero: true, display: false, + grid: { + display: false, + }, }, + x: { + display: false, + grid: { + display: false, + }, + } } } - } - }); + }); } else { window.myBar4.data.datasets = [{ - label: "Razon delitos de alto y bajo impacto", - data: [json.bajo, json.alto], + label: "", + data: json["valores"], borderWidth: 1 }]; - window.myBar4.data.labels = ["Bajo", "Alto"]; + window.myBar4.data.labels = json["nombres"]; window.myBar4.update(); } }); @@ -412,6 +508,98 @@ function load_top_anio(data, cfg) { }); } +function load_all_anio(data, cfg) { + let input_fini = document.getElementById(`afini-${cfg['num']}`); + let input_init = document.getElementById(`ainit-${cfg['num']}`); + + if (input_fini != undefined && input_fini != undefined) { + let err = false; + + if (data['annio_inicio'] < 2016 || data['annio_inicio'] > 2023) { + input_init.classList.add("text-rose-300") + err = true + } else { + input_init.classList.remove("text-rose-300") + }; + + if (data['annio_final'] < 2016 || data['annio_final'] > 2023) { + input_fini.classList.add("text-rose-300") + err = true + } else { + input_fini.classList.remove("text-rose-300") + }; + + if (err) return; + } + + console.log('Fetching') + console.log(JSON.stringify(data)) + + fetch(cfg.endpoint, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data) + } + ) + .then((response) => response.json()) + .then((json) => { + const ctx = document.getElementById(`pie-all-${cfg.num}`); + + if( window.myBar3 === undefined) { + window.myBar3 = new Chart(ctx, { + type: 'pie', + data: { + labels: json["nombres"], + datasets: [{ + label: "", + data: json["valores"], + borderWidth: 1 + }], + }, + options: { + gridLines: { + color: "rgba(0, 0, 0, 0)", + }, + plugins: { + title: { + display: false + }, + legend: { + display: false + } + }, + scales: { + y: { + beginAtZero: true, + display: false, + grid: { + display: false, + }, + }, + x: { + display: false, + grid: { + display: false, + }, + } + } + } + }); + } else { + window.myBar3.data.datasets = [{ + label: "", + data: json["valores"], + borderWidth: 1 + }]; + window.myBar3.data.labels = json["nombres"]; + window.myBar3.update(); + } + }); +} + function load_map_data(data, cfg, chart_cfg, valores) { let input_fini = document.getElementById(`afini-${cfg['num']}`); let input_init = document.getElementById(`ainit-${cfg['num']}`); diff --git a/assets/tailwind.css b/assets/tailwind.css index b4d5de7..2b3735a 100644 --- a/assets/tailwind.css +++ b/assets/tailwind.css @@ -658,10 +658,6 @@ video { width: 16.666667%; } -.w-2\/3 { - width: 66.666667%; -} - .w-3\/4 { width: 75%; } diff --git a/src/bin/api.rs b/src/bin/api.rs index e485c5c..d5c698e 100644 --- a/src/bin/api.rs +++ b/src/bin/api.rs @@ -784,6 +784,52 @@ fn uppercase_first_letter(s: &str) -> String { } } +async fn delitos_por_anio( + State(state): State, + Json(sol): Json, +) -> Json { + let SolicitudTopPorAnio { + annio_inicio, + annio_final, + mut categorias, + } = dbg!(sol); + + categorias.sort(); + + let annio_inicio = annio_inicio - OFFSET; + let annio_final = annio_final - OFFSET; + + let mut resultados: Vec<(String, i64)> = sqlx::query_as(&format!( + "SELECT categoria, COUNT(*) AS fre FROM delitos JOIN categoria USING(id_categoria) WHERE {}id_anio_hecho BETWEEN {annio_inicio} AND {annio_final} GROUP BY id_categoria;", + if categorias.is_empty() || categorias.len() >= ACTUAL_CATEGORIES { + format!("") + } else { + format!("id_categoria IN ({0}) AND ", + categorias + .iter() + .map(|id| format!("{id}")) + .collect::>() + .join(",")) + } + + )) + .fetch_all(&state.db) + .await + .unwrap(); + + resultados.sort_unstable_by_key(|(_, v)| *v); + resultados = resultados.into_iter().rev().into_iter().collect::>(); + + TopPorAnio { + valores: resultados.iter().map(|(_, n)| *n as u64).collect(), + nombres: resultados + .into_iter() + .map(|(n, _)| uppercase_first_letter(n.to_lowercase().as_str())) + .collect(), + } + .into() +} + async fn top_por_anio( State(state): State, Json(sol): Json, @@ -837,33 +883,80 @@ async fn top_por_anio( async fn cantidad_alto_y_bajo( State(state): State, - Json(sol): Json, -) -> Json { - let SolicitudAltoYBajo { + Json(sol): Json, +) -> Json { + let SolicitudTopPorAnio { annio_inicio, annio_final, + .. } = dbg!(sol); let annio_inicio = annio_inicio - OFFSET; let annio_final = annio_final - OFFSET; - let (bajo,): (i64,) = sqlx::query_as(&format!( - "SELECT COUNT(*) FROM delitos WHERE id_categoria = 1 AND id_anio_hecho BETWEEN {annio_inicio} AND {annio_final};", + let mut resultados: Vec<(String, i64)> = sqlx::query_as(&format!( + "SELECT delito, COUNT(*) AS fre FROM delitos JOIN delito USING(id_delito) WHERE id_anio_hecho BETWEEN {annio_inicio} AND {annio_final} AND id_categoria = 1 GROUP BY delito;", )) - .fetch_one(&state.db) + .fetch_all(&state.db) .await .unwrap(); - let (alto,): (i64,) = sqlx::query_as(&format!( - "SELECT COUNT(*) FROM delitos WHERE id_categoria != 1 AND id_anio_hecho BETWEEN {annio_inicio} AND {annio_final};", + resultados.sort_unstable_by_key(|(_, v)| *v); + resultados = resultados.into_iter().rev().into_iter().collect::>(); + + let mut top_15 = resultados[0..15].to_vec(); + let resto = &resultados[15..]; + + let total_resto = resto.iter().fold(0, |s, (_, v)| s + v); + + top_15.push(("Otros".to_string(), total_resto)); + + TopPorAnio { + valores: top_15.iter().map(|(_, n)| *n as u64).collect(), + nombres: top_15 + .into_iter() + .map(|(n, _)| uppercase_first_letter(n.to_lowercase().as_str())) + .collect(), + } + .into() +} + +async fn cantidad_alto_y_bajo2( + State(state): State, + Json(sol): Json, +) -> Json { + let SolicitudTopPorAnio { + annio_inicio, + annio_final, + .. + } = dbg!(sol); + + let annio_inicio = annio_inicio - OFFSET; + let annio_final = annio_final - OFFSET; + + let mut resultados: Vec<(String, i64)> = sqlx::query_as(&format!( + "SELECT delito, COUNT(*) AS fre FROM delitos JOIN delito USING(id_delito) WHERE id_anio_hecho BETWEEN {annio_inicio} AND {annio_final} AND id_categoria != 1 GROUP BY delito;", )) - .fetch_one(&state.db) + .fetch_all(&state.db) .await .unwrap(); - CantidadesAltoYBajo { - alto: alto as u64, - bajo: bajo as u64, + resultados.sort_unstable_by_key(|(_, v)| *v); + resultados = resultados.into_iter().rev().into_iter().collect::>(); + + let mut top_15 = resultados[0..15].to_vec(); + let resto = &resultados[15..]; + + let total_resto = resto.iter().fold(0, |s, (_, v)| s + v); + + top_15.push(("Otros".to_string(), total_resto)); + + TopPorAnio { + valores: top_15.iter().map(|(_, n)| *n as u64).collect(), + nombres: top_15 + .into_iter() + .map(|(n, _)| uppercase_first_letter(n.to_lowercase().as_str())) + .collect(), } .into() } @@ -933,7 +1026,9 @@ async fn main() -> anyhow::Result<()> { .route("/dias_percent", post(dias_porcentajes)) .route("/horas_percent", post(horas_porcentajes)) .route("/top_por_anio", post(top_por_anio)) + .route("/delitos_por_anio", post(delitos_por_anio)) .route("/alto_y_bajo", post(cantidad_alto_y_bajo)) + .route("/alto_y_bajo2", post(cantidad_alto_y_bajo2)) .route("/c_por_mes", post(cantidades_por_mes)) .route("/date/upnow", get(untilnow)) .with_state(state) diff --git a/templates/hello.html b/templates/hello.html index c234f11..9564680 100644 --- a/templates/hello.html +++ b/templates/hello.html @@ -251,9 +251,13 @@

Top delitos por años

@@ -273,11 +277,17 @@

Razón de delitos de bajo y alto impacto

-
+
+

Razón de delitos de bajo y alto impacto

+ +
+
+

Delitos de bajo impacto

-
- +
+

Delitos de alto impacto

+