Scan UI changes. Rename files to be processed with unix timestamp

master
Araozu 2023-11-14 16:39:15 -05:00
parent 645ad1f387
commit 9f5460f27f
2 changed files with 134 additions and 18 deletions

View File

@ -3,9 +3,11 @@ use printpdf::{Mm, PdfDocument};
use rocket::{http::Status, serde::json::Json};
use serde::Serialize;
use std::{
f32::consts::E,
fs::{self, File},
io::BufWriter,
path::PathBuf,
time::{SystemTime, UNIX_EPOCH},
};
const SCAN_PATH: &str = "/srv/srv/shares/eegsac/ESCANEOS/";
@ -14,14 +16,13 @@ const SCAN_PATH: &str = "/srv/srv/shares/eegsac/ESCANEOS/";
#[derive(Serialize)]
enum ScanInfo {
/// The url has both DNI & id
Full(String, i32),
Full(String, i32, String),
/// The url only has a DNI
Partial(String),
Partial(String, String),
/// Te url is invalid
Error(String),
}
#[get("/scans/detect")]
pub async fn detect_scans() -> (Status, Json<JsonResult<Vec<(PathBuf, ScanInfo)>>>) {
let files = match get_valid_files() {
@ -50,6 +51,20 @@ fn get_valid_files() -> Result<Vec<PathBuf>, String> {
let mut result = Vec::<PathBuf>::new();
/*
TODO: The system will detect info from an image.
If successful, that image will be renamed to:
eeg_<unix_timestamp>.jpg
and the timestamp will be sent to the frontend
along with the detected info.
Then the frontend will send back the list of timestamps & info
and the backend will do the conversion.
This way, we can ensure that only the images sent to & from the FE
can be converted.
*/
for p in paths {
let p = match p {
Ok(p) => p,
@ -83,13 +98,12 @@ fn get_valid_files() -> Result<Vec<PathBuf>, String> {
if filename.ends_with(".jpg") && filename.starts_with("eeg") {
result.push(file_path);
}
};
}
Ok(result)
}
/// Detects the QR code data from every file
fn get_details_from_paths(paths: Vec<PathBuf>) -> Vec<(PathBuf, ScanInfo)> {
paths
@ -99,7 +113,19 @@ fn get_details_from_paths(paths: Vec<PathBuf>) -> Vec<(PathBuf, ScanInfo)> {
}
fn get_image_info(path: PathBuf) -> ScanInfo {
let img = image::open(path).unwrap();
let img = image::open(&path).unwrap();
// get unix timestamp now
// Get current time in seconds
let current_time = SystemTime::now().duration_since(UNIX_EPOCH);
let current_time = match current_time {
Ok(t) => t,
Err(err) => {
eprintln!("Error obteniendo hora actual: {:?}", err);
return ScanInfo::Error("Error obteniendo hora actual: TIME WENT BACKWARDS.".into());
}
};
let current_ms = current_time.as_millis();
// Get width & height
let width = img.width();
@ -142,14 +168,36 @@ fn get_image_info(path: PathBuf) -> ScanInfo {
let iid = url.chars().skip(equals_pos + 1).collect::<String>();
match iid.parse() {
Ok(v) => ScanInfo::Full(dni, v),
Err(_) => ScanInfo::Error(
"QR invalido: El iid no es un número.".into(),
),
Ok(v) => {
// Rename file
let mut new_path = path.clone();
new_path.set_file_name(format!("eeg_{}.jpg", current_ms));
match fs::rename(path, new_path) {
Ok(_) => ScanInfo::Full(dni, v, current_ms.to_string()),
Err(err) => {
eprintln!("Error renombrando archivo: {:?}", err);
ScanInfo::Error("Error renombrando archivo.".into())
}
}
}
Err(_) => ScanInfo::Error("QR invalido: El iid no es un número.".into()),
}
}
None => {
return ScanInfo::Partial(url.chars().skip(31).collect());
// Rename file
let mut new_path = path.clone();
new_path.set_file_name(format!("eeg_{}.jpg", current_ms));
match fs::rename(path, new_path) {
Ok(_) => ScanInfo::Partial(url.chars().skip(31).collect(), current_ms.to_string()),
Err(err) => {
eprintln!("Error renombrando archivo: {:?}", err);
ScanInfo::Error("Error renombrando archivo.".into())
}
}
}
}
}

View File

@ -1,12 +1,52 @@
import { For, createMemo, createSignal } from "solid-js";
import { FilledButton } from "../components/FilledButton";
import { FilledCard } from "../components/FilledCard";
import { LoadingIcon } from "../icons/LoadingIcon";
import { MagnifyingGlassIcon } from "../icons/MagnifyingGlassIcon";
import { LoadingStatus, useLoading } from "../utils/functions";
/**
* Represents the data about the scans that were detected.
* Serialization & Deserialization is done by the backend.
*/
interface ScanData {
Ok: Array<[string, ScanResult]>,
}
type ScanResult =
| {Full: [string, number, string]}
| {Partial: [string, string]}
| {Error: string};
function dataFromScanResult(result: ScanResult) {
if ("Full" in result) {
return `DNI: ${result.Full[0]}, iid: ${result.Full[1]}`;
} else if ("Partial" in result) {
return `DNI: ${result.Partial}`;
} else if ("Error" in result) {
return `Error: ${result.Error}`;
}
}
export function Scans() {
const {status, setStatus, error, setError} = useLoading();
const [scanData, setScanData] = createSignal<ScanData | null>(null);
const loading = createMemo(() => status() === LoadingStatus.Loading);
const detectScans = () => {
setStatus(LoadingStatus.Loading);
fetch(`${import.meta.env.VITE_BACKEND_URL}/api/scans/detect`)
.then((res) => res.json())
.then(console.log);
.then((data) => {
setStatus(LoadingStatus.Ok);
setScanData(data);
})
.catch((err) => {
setStatus(LoadingStatus.Error);
console.error(err);
});
};
return (
@ -63,8 +103,7 @@ export function Scans() {
</ul>
</div>
</FilledCard>
</div>
<div>
<FilledCard>
<h1 class="py-2 px-4 font-medium text-xl mb-2">
Detectar escaneos
@ -86,16 +125,26 @@ export function Scans() {
<div class="relative">
<FilledButton
class="mt-2"
onClick={detectScans}
>
<span
class="absolute top-[0.9rem] left-2 inline-block"
class="absolute top-[1.35rem] left-2"
style={{display: loading() ? "none" : "inline-block"}}
>
<MagnifyingGlassIcon
fill="var(--c-on-primary)"
/>
</span>
<span
class="absolute top-[1.35rem] left-2"
style={{display: loading() ? "inline-block" : "none"}}
>
<LoadingIcon
class="animate-spin"
fill="var(--c-primary-container)"
/>
</span>
<span class="ml-5">
Detectar escaneos
</span>
@ -103,14 +152,33 @@ export function Scans() {
</div>
</div>
</FilledCard>
</div>
<div>
<FilledCard class="border border-c-outline overflow-hidden">
<h1 class="p-3 font-medium text-xl">
Escaneos detectados
</h1>
<div class="bg-c-surface p-4">
:D
<div class="bg-c-surface py-2">
<For each={scanData()?.Ok ?? []}>
{([path, result]) => (
<p class="py-1 px-4 hover:bg-c-surface-variant transition-colors">
<span class="font-mono">
{path.substring(path.lastIndexOf("/") + 1)}
</span>
<br />
{dataFromScanResult(result)}
</p>
)}
</For>
</div>
<p class="p-4">
Al convertir los escaneos a PDF, se eliminan los archivos JPG.
<br />
La lista de escaneos a convertir puede haber cambiado desde
la última vez que se realizó la detección.
</p>
</FilledCard>
</div>
</div>