[Scans] Fixes #26: Generate & show a list of errors for individual scans
This commit is contained in:
parent
12093b88c3
commit
bc69762fb0
@ -54,11 +54,11 @@ pub fn convert_scans_options() -> Status {
|
||||
}
|
||||
|
||||
#[post("/scans/convert", format = "json", data = "<data>")]
|
||||
pub async fn convert_scans(data: Json<Vec<ScanInfo>>) -> (Status, Json<JsonResult<()>>) {
|
||||
pub async fn convert_scans(data: Json<Vec<ScanInfo>>) -> (Status, Json<JsonResult<Vec<String>>>) {
|
||||
let data = data.into_inner();
|
||||
|
||||
match convert_scans_from_data(&data).await {
|
||||
Ok(_) => (Status::Ok, JsonResult::ok(())),
|
||||
Ok(error_list) => (Status::Ok, JsonResult::ok(error_list)),
|
||||
Err(reason) => (Status::InternalServerError, JsonResult::err(reason)),
|
||||
}
|
||||
}
|
||||
@ -289,7 +289,7 @@ fn get_image_info(path: PathBuf) -> ScanInfo {
|
||||
/// Converts a list of files into PDFs.
|
||||
///
|
||||
/// Uses the timestamps inside `data` to read the correct JPG files and convert them.
|
||||
async fn convert_scans_from_data(data: &Vec<ScanInfo>) -> Result<(), String> {
|
||||
async fn convert_scans_from_data(data: &Vec<ScanInfo>) -> Result<Vec<String>, String> {
|
||||
// Get a tuple with all the DNIs & iids.
|
||||
let (ids, dnis) = data.iter().fold(
|
||||
(vec!["-1".to_string()], vec!["''".to_string()]),
|
||||
@ -324,6 +324,9 @@ async fn convert_scans_from_data(data: &Vec<ScanInfo>) -> Result<(), String> {
|
||||
}
|
||||
};
|
||||
|
||||
// List of non-fatal errors
|
||||
let mut errors = Vec::<String>::new();
|
||||
|
||||
// Match the results from DB with the data from the frontend,
|
||||
// & convert the files to PDFs
|
||||
for info in data {
|
||||
@ -334,6 +337,10 @@ async fn convert_scans_from_data(data: &Vec<ScanInfo>) -> Result<(), String> {
|
||||
Some(p) => (p.clone(), timestamp),
|
||||
None => {
|
||||
log::error!("Register not found in data from DB: id {}, dni {}", id, dni);
|
||||
errors.push(format!(
|
||||
"Certificado no encontrado en la base de datos: id {}, dni {}, timestamp {}",
|
||||
id, dni, timestamp
|
||||
));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -346,6 +353,10 @@ async fn convert_scans_from_data(data: &Vec<ScanInfo>) -> Result<(), String> {
|
||||
Some(p) => (p.clone(), timestamp),
|
||||
None => {
|
||||
log::error!("Register not found in data from DB: dni {}", dni);
|
||||
errors.push(format!(
|
||||
"Certificado no encontrado en la base de datos: dni {}, timestamp {}",
|
||||
dni, timestamp
|
||||
));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -364,7 +375,7 @@ async fn convert_scans_from_data(data: &Vec<ScanInfo>) -> Result<(), String> {
|
||||
(scan_data, timestamp)
|
||||
}
|
||||
ScanInfo::Error(reason) => {
|
||||
log::info!("Found an error while matching data with DB: {}", reason);
|
||||
log::info!("Tried to process an errored cert: {}", reason);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@ -374,6 +385,10 @@ async fn convert_scans_from_data(data: &Vec<ScanInfo>) -> Result<(), String> {
|
||||
Ok(t) => t,
|
||||
Err(err) => {
|
||||
log::error!("Error obteniendo hora actual: {:?}", err);
|
||||
errors.push(format!(
|
||||
"Error conviritendo archivo {}: Error obteniendo hora actual.",
|
||||
timestamp
|
||||
));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@ -401,17 +416,25 @@ async fn convert_scans_from_data(data: &Vec<ScanInfo>) -> Result<(), String> {
|
||||
Ok(_) => log::info!("Deleted image for: {}", filename),
|
||||
Err(err) => {
|
||||
log::error!("Error deleting image for {}: {:?}", filename, err);
|
||||
errors.push(format!(
|
||||
"Error eliminando archivo jpg {}: {}",
|
||||
timestamp, err
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
log::error!("Error converting to pdf: {}", reason);
|
||||
errors.push(format!(
|
||||
"Error convirtiendo a PDF {}: {}",
|
||||
timestamp, reason
|
||||
));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(errors)
|
||||
}
|
||||
|
||||
/// Converts a file to PDF using Imagemagick
|
||||
|
@ -1,10 +1,10 @@
|
||||
use cors::Cors;
|
||||
use once_cell::sync::OnceCell;
|
||||
use sqlx::mysql::MySqlPoolOptions;
|
||||
use sqlx::Connection;
|
||||
use sqlx::MySql;
|
||||
use sqlx::MySqlConnection;
|
||||
use sqlx::Pool;
|
||||
use sqlx::mysql::MySqlPoolOptions;
|
||||
use std::env;
|
||||
use std::time::Instant;
|
||||
|
||||
@ -55,8 +55,8 @@ pub async fn db() -> Result<&'static Pool<MySql>, String> {
|
||||
log::info!("DB active connections: {}", db.size());
|
||||
log::info!("DB num_idle connections: {}", db.num_idle());
|
||||
|
||||
return Ok(db)
|
||||
},
|
||||
return Ok(db);
|
||||
}
|
||||
None => {
|
||||
log::info!("DB not initialized, initializing from db()");
|
||||
let _ = init_db().await;
|
||||
@ -83,10 +83,8 @@ pub async fn init_db() -> Result<(), String> {
|
||||
/*
|
||||
On some afternoons for some god forsaken reason the idle connections
|
||||
to the db stay alive, but stop responding.
|
||||
|
||||
When this happens, we must restart the server, or wait for all
|
||||
the active connections to timeout.
|
||||
|
||||
Here are some measures to circumvent that:
|
||||
*/
|
||||
// Set the maximum wait time for connections to 10 seconds
|
||||
@ -96,7 +94,10 @@ pub async fn init_db() -> Result<(), String> {
|
||||
.idle_timeout(std::time::Duration::from_secs(10 * 60))
|
||||
.connect(db_url.as_str())
|
||||
.await;
|
||||
log::info!("DB Pool connection took: {:?} ms", start.elapsed().as_millis());
|
||||
log::info!(
|
||||
"DB Pool connection took: {:?} ms",
|
||||
start.elapsed().as_millis()
|
||||
);
|
||||
|
||||
match pool {
|
||||
Ok(pool) => match DB.set(pool) {
|
||||
|
@ -35,9 +35,7 @@ impl Course {
|
||||
}
|
||||
};
|
||||
|
||||
let results = sqlx::query!("SELECT * FROM course")
|
||||
.fetch_all(db)
|
||||
.await;
|
||||
let results = sqlx::query!("SELECT * FROM course").fetch_all(db).await;
|
||||
|
||||
let results = match results {
|
||||
Ok(res) => res,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Show, createMemo } from "solid-js";
|
||||
import { For, Show, createMemo, createSignal } from "solid-js";
|
||||
import { ScanResult } from ".";
|
||||
import { FilledButton } from "../components/FilledButton";
|
||||
import { FilledCard } from "../components/FilledCard";
|
||||
@ -11,6 +11,7 @@ import { JsonResult } from "../types/JsonResult";
|
||||
|
||||
export function ScansList(props: {scanData: Array<ScanResult> | null, onSuccess: () => void}) {
|
||||
const {status, setStatus, error, setError} = useLoading();
|
||||
const [convertionResult, setConvertionResult] = createSignal<Array<string> | null>(null);
|
||||
|
||||
const loading = createMemo(() => status() === LoadingStatus.Loading);
|
||||
|
||||
@ -40,6 +41,7 @@ export function ScansList(props: {scanData: Array<ScanResult> | null, onSuccess:
|
||||
|
||||
const convertScans = () => {
|
||||
setStatus(LoadingStatus.Loading);
|
||||
setConvertionResult(null);
|
||||
|
||||
if (props.scanData === null) {
|
||||
setError("No se detectaron escaneos");
|
||||
@ -48,8 +50,9 @@ export function ScansList(props: {scanData: Array<ScanResult> | null, onSuccess:
|
||||
}
|
||||
|
||||
axios.post(`${import.meta.env.VITE_BACKEND_URL}/api/scans/convert`, props.scanData)
|
||||
.then(() => {
|
||||
.then((response) => {
|
||||
setStatus(LoadingStatus.Ok);
|
||||
setConvertionResult(response?.data.Ok ?? []);
|
||||
props.onSuccess();
|
||||
})
|
||||
.catch((err) => {
|
||||
@ -166,6 +169,29 @@ export function ScansList(props: {scanData: Array<ScanResult> | null, onSuccess:
|
||||
</Show>
|
||||
</FilledCard>
|
||||
</Show>
|
||||
|
||||
<Show when={convertionResult() !== null && convertionResult()!.length === 0}>
|
||||
<FilledCard>
|
||||
<h1 class="py-2 px-4 font-medium text-xl mb-2">
|
||||
Archivos convertidos con éxito
|
||||
</h1>
|
||||
</FilledCard>
|
||||
</Show>
|
||||
|
||||
<Show when={convertionResult() !== null && convertionResult()!.length > 0}>
|
||||
<FilledCard>
|
||||
<h1 class="py-2 px-4 font-medium text-xl mb-2">
|
||||
Archivos convertidos con errores
|
||||
</h1>
|
||||
|
||||
<div class="bg-c-surface p-4">
|
||||
<p class="my-2">Se encontraron errores convirtiendo {convertionResult()!.length} archivos:</p>
|
||||
<For each={convertionResult()!}>
|
||||
{(error) => <p class="my-1 text-c-error">- {error}</p>}
|
||||
</For>
|
||||
</div>
|
||||
</FilledCard>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -25,14 +25,13 @@ export type ScanResult =
|
||||
export function Scans() {
|
||||
const { status, setStatus, error, setError } = useLoading();
|
||||
const [scanData, setScanData] = createSignal<Array<ScanResult> | null>(null);
|
||||
const [converted, setConverted] = createSignal(false);
|
||||
|
||||
|
||||
const loading = createMemo(() => status() === LoadingStatus.Loading);
|
||||
|
||||
const detectScans = () => {
|
||||
setStatus(LoadingStatus.Loading);
|
||||
setScanData(null);
|
||||
setConverted(false);
|
||||
|
||||
fetch(`${import.meta.env.VITE_BACKEND_URL}/api/scans/detect`)
|
||||
.then((res) => res.json())
|
||||
@ -179,18 +178,9 @@ export function Scans() {
|
||||
</FilledCard>
|
||||
|
||||
<ScansList scanData={scanData()} onSuccess={() => {
|
||||
setConverted(true);
|
||||
setScanData(null);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Show when={converted()}>
|
||||
<FilledCard>
|
||||
<h1 class="py-2 px-4 font-medium text-xl mb-2">
|
||||
Archivos convertidos con éxito
|
||||
</h1>
|
||||
</FilledCard>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user