diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 69906ed..ff56ae3 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -59,6 +59,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "async-channel" version = "1.9.0" @@ -128,13 +134,16 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" name = "backend" version = "0.1.0" dependencies = [ + "bardecoder", "chrono", "dotenvy", "env_logger", + "image", "isahc", "lazy_static", "log", "once_cell", + "printpdf", "reqwest", "rocket", "scraper", @@ -158,6 +167,19 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bardecoder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757eeab9757d5ddd88aa1420449f3ce92631339b7f5b3785808da7a148de35d9" +dependencies = [ + "anyhow", + "image", + "log", + "newtype_derive", + "thiserror", +] + [[package]] name = "base64" version = "0.21.2" @@ -176,6 +198,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -200,12 +228,29 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +dependencies = [ + "memchr", + "regex-automata 0.3.6", + "serde", +] + [[package]] name = "bumpalo" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" + [[package]] name = "byteorder" version = "1.4.3" @@ -254,6 +299,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "concurrent-queue" version = "2.3.0" @@ -348,6 +399,39 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-queue" version = "0.3.8" @@ -367,6 +451,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -606,6 +696,22 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "exr" +version = "1.71.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +dependencies = [ + "bit_field", + "flume 0.11.0", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -621,6 +727,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +[[package]] +name = "fdeflate" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" +dependencies = [ + "simd-adler32", +] + [[package]] name = "figment" version = "0.10.10" @@ -635,6 +750,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flume" version = "0.10.14" @@ -647,6 +772,15 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -841,6 +975,16 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.28.0" @@ -872,6 +1016,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1090,6 +1243,25 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "qoi", + "tiff", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1186,6 +1358,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.64" @@ -1204,6 +1385,12 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.147" @@ -1249,6 +1436,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.5" @@ -1286,6 +1479,23 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "lopdf" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c8e1b6184b1b32ea5f72f572ebdc40e5da1d2921fa469947ff7c480ad1f85a" +dependencies = [ + "encoding_rs", + "flate2", + "itoa", + "linked-hash-map", + "log", + "md5", + "pom", + "time 0.3.27", + "weezl", +] + [[package]] name = "mac" version = "0.1.1" @@ -1330,12 +1540,27 @@ dependencies = [ "digest", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -1355,6 +1580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", + "simd-adler32", ] [[package]] @@ -1412,6 +1638,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "newtype_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac8cd24d9f185bb7223958d8c1ff7a961b74b1953fd05dba7cc568a63b3861ec" +dependencies = [ + "rustc_version", +] + [[package]] name = "nom" version = "7.1.3" @@ -1470,6 +1705,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.16" @@ -1555,6 +1801,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owned_ttf_parser" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60ac8dda2e5cc09bf6480e3b3feff9783db251710c922ae9369a429c51efdeb0" +dependencies = [ + "ttf-parser", +] + [[package]] name = "parking" version = "2.1.1" @@ -1767,6 +2022,19 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "png" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "2.8.0" @@ -1783,6 +2051,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "pom" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d73a5fe10d458e77534589512104e5aa8ac480aa9ac30b74563274235cce4" +dependencies = [ + "bstr", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1795,6 +2072,19 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "printpdf" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f626e180738289baa7ea2d70e603698520735060a664141203cc17bd8e4379c0" +dependencies = [ + "image", + "js-sys", + "lopdf", + "owned_ttf_parser", + "time 0.3.27", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -1833,6 +2123,15 @@ dependencies = [ "psl-types", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quote" version = "1.0.33" @@ -1872,6 +2171,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -2134,6 +2453,15 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.8" @@ -2279,6 +2607,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" + [[package]] name = "serde" version = "1.0.188" @@ -2390,6 +2724,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "siphasher" version = "0.3.11" @@ -2665,7 +3005,7 @@ checksum = "be4c21bf34c7cae5b283efb3ac1bcc7670df7561124dc2f8bdc0b59be40f79a2" dependencies = [ "atoi", "chrono", - "flume", + "flume 0.10.14", "futures-channel", "futures-core", "futures-executor", @@ -2831,6 +3171,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiff" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.1.45" @@ -3068,6 +3419,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "ttf-parser" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6" + [[package]] name = "typenum" version = "1.16.0" @@ -3306,6 +3663,12 @@ dependencies = [ "rustls-webpki", ] +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "whoami" version = "1.4.1" @@ -3454,3 +3817,12 @@ name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 3148567..dc22f65 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -19,5 +19,8 @@ lazy_static = "1.4.0" once_cell = "1.18.0" log = "0.4.20" env_logger = "0.10.0" +bardecoder = "0.5.0" +image = "0.24.7" +printpdf = { version = "0.6.0", features = ["image", "embedded_images"] } diff --git a/backend/src/controller/mod.rs b/backend/src/controller/mod.rs index eaf75b1..e8d6754 100644 --- a/backend/src/controller/mod.rs +++ b/backend/src/controller/mod.rs @@ -3,3 +3,4 @@ pub mod course; pub mod custom_label; pub mod person; pub mod register; +pub mod scans; diff --git a/backend/src/controller/person/mod.rs b/backend/src/controller/person/mod.rs index d841e7f..1e5ccb7 100644 --- a/backend/src/controller/person/mod.rs +++ b/backend/src/controller/person/mod.rs @@ -1,4 +1,4 @@ -use log::{info, error}; +use log::{error, info}; use reqwest::Client; use rocket::http::Status; use rocket::serde::json::Json; diff --git a/backend/src/controller/scans/mod.rs b/backend/src/controller/scans/mod.rs new file mode 100644 index 0000000..9f439cf --- /dev/null +++ b/backend/src/controller/scans/mod.rs @@ -0,0 +1,125 @@ +use crate::json_result::JsonResult; +use printpdf::{Mm, PdfDocument}; +use rocket::{http::Status, serde::json::Json}; +use std::{ + fs::{self, File}, + io::BufWriter, + path::PathBuf, +}; + +const SCAN_PATH: &str = "/srv/srv/shares/eegsac/ESCANEOS/"; + +#[get("/scans/detect")] +pub async fn detect_scans() -> (Status, Json>>) { + // TODO: Move to ENV variable? + let paths = match fs::read_dir(SCAN_PATH) { + Ok(paths) => paths, + Err(err) => { + eprintln!("Error reading dir: {:?}", err); + return ( + Status::InternalServerError, + JsonResult::err("Error leyendo directorio".into()), + ); + } + }; + + let mut result = Vec::::new(); + + for p in paths { + let p = match p { + Ok(p) => p, + Err(err) => { + eprintln!("Error reading path: {:?}", err); + return ( + Status::InternalServerError, + JsonResult::err("Error leyendo directorio".into()), + ); + } + }; + + let file_path = p.path(); + + if file_path.is_dir() { + continue; + } + + let filename = match file_path.file_name() { + Some(filename) => filename, + None => { + eprintln!("Error getting file name: {:?}", file_path); + return ( + Status::InternalServerError, + JsonResult::err("Error leyendo archivo en directorio".into()), + ); + } + }; + + let filename = match filename.to_str() { + Some(p) => p, + None => { + eprintln!("Error getting file as string: {:?}", p); + return ( + Status::InternalServerError, + JsonResult::err("Error leyendo directorio".into()), + ); + } + }; + + if filename.ends_with(".jpg") && filename.starts_with("eeg") { + result.push(filename.to_string()); + } + } + + for filename in result.iter() { + let path = PathBuf::from(format!("{}{}", SCAN_PATH, filename)); + + examine_scan(&path); + } + + (Status::Ok, JsonResult::ok(result)) +} + +fn examine_scan(path: &PathBuf) { + let img = image::open(path).unwrap(); + + // Get width & height + let width = img.width(); + let height = img.height(); + + let third_point_height = (height / 3) * 2; + let mid_point_width = width / 2; + let remaining_height = height - third_point_height; + + // crop image + let cropped_img = img.crop_imm(0, third_point_height, mid_point_width, remaining_height); + + // get qr from cropped image + let results = bardecoder::default_decoder().decode(&cropped_img); + // TODO: get first qr, parse, query db, get info, determine filename + for result in results { + println!("{}", result.unwrap()); + } + + // Rotate image 90 degrees clockwise + let img = img.rotate270(); + + // Generate PDF + let (doc, page1, layer1) = + PdfDocument::new("PDF_Document_title", Mm(297.0), Mm(210.0), "Layer 1"); + let current_layer = doc.get_page(page1).get_layer(layer1); + + let pdf_image = printpdf::image::Image::from_dynamic_image(&img); + let img_transform = printpdf::ImageTransform::default(); + let img_transform = printpdf::ImageTransform { + dpi: Some(200.0), + ..img_transform + }; + + pdf_image.add_to_layer(current_layer, img_transform); + + // + doc.save(&mut BufWriter::new( + File::create(format!("{}output.pdf", SCAN_PATH)).unwrap(), + )) + .unwrap(); +} diff --git a/backend/src/main.rs b/backend/src/main.rs index ccd2c2b..f3eaf84 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -68,6 +68,10 @@ async fn rocket() -> _ { controller::classroom::get_expiration_date, controller::classroom::set_expiration_date_options, controller::classroom::set_expiration_date, + // + // Scans routes + // + controller::scans::detect_scans, ], ) } diff --git a/frontend/index.html b/frontend/index.html index 3047135..fb79b76 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -9,7 +9,7 @@ - +