aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoris2025-01-26 17:58:57 +0100
committerJoris2025-01-26 17:58:57 +0100
commit24eeb54a6b7159964e8887ade7fa5173b50feb3a (patch)
tree91af6253df784445db9b084b02b38b37a83224e8
parentc5759f348e70cf54b4bfa4cd17e1fe1828ead30a (diff)
Replace tera by minijinjamain
tera was doing the job all right, but minijinja has fewer dependencies.
-rw-r--r--Cargo.lock345
-rw-r--r--Cargo.toml2
-rw-r--r--src/assets.rs2
-rw-r--r--src/controller/balance.rs21
-rw-r--r--src/controller/categories.rs40
-rw-r--r--src/controller/error.rs14
-rw-r--r--src/controller/incomes.rs56
-rw-r--r--src/controller/login.rs21
-rw-r--r--src/controller/payments.rs62
-rw-r--r--src/controller/statistics.rs15
-rw-r--r--src/controller/utils.rs39
-rw-r--r--src/controller/wallet.rs3
-rw-r--r--src/db/jobs.rs4
-rw-r--r--src/db/payments.rs6
-rw-r--r--src/jobs/mod.rs7
-rw-r--r--src/jobs/weekly_report.rs27
-rw-r--r--src/mail.rs4
-rw-r--r--src/main.rs3
-rw-r--r--src/routes.rs3
-rw-r--r--src/templates.rs164
-rw-r--r--templates/balance.html14
-rw-r--r--templates/base.html6
-rw-r--r--templates/category/create.html4
-rw-r--r--templates/category/update.html4
-rw-r--r--templates/income/create.html12
-rw-r--r--templates/income/table.html6
-rw-r--r--templates/income/update.html10
-rw-r--r--templates/macros/paging.html16
-rw-r--r--templates/payment/create.html12
-rw-r--r--templates/payment/table.html8
-rw-r--r--templates/payment/table/search.html8
-rw-r--r--templates/payment/update.html10
-rw-r--r--templates/report/list.j26
-rw-r--r--templates/report/report.j226
-rw-r--r--templates/statistics.html2
35 files changed, 377 insertions, 605 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7237130..1455ebf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -187,16 +187,6 @@ dependencies = [
]
[[package]]
-name = "bstr"
-version = "1.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
-dependencies = [
- "memchr",
- "serde",
-]
-
-[[package]]
name = "budget"
version = "0.1.0"
dependencies = [
@@ -209,6 +199,7 @@ dependencies = [
"hyper",
"hyper-util",
"log",
+ "minijinja",
"rand",
"rand_core",
"serde",
@@ -217,7 +208,6 @@ dependencies = [
"sha2",
"sqlx",
"sqlx-core",
- "tera",
"tokio",
"tokio-util",
"url",
@@ -271,28 +261,6 @@ dependencies = [
]
[[package]]
-name = "chrono-tz"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb"
-dependencies = [
- "chrono",
- "chrono-tz-build",
- "phf",
-]
-
-[[package]]
-name = "chrono-tz-build"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1"
-dependencies = [
- "parse-zoneinfo",
- "phf",
- "phf_codegen",
-]
-
-[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -354,25 +322,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
-name = "crossbeam-deque"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
-dependencies = [
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
name = "crossbeam-queue"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -409,12 +358,6 @@ dependencies = [
]
[[package]]
-name = "deunicode"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
-
-[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -651,30 +594,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
-name = "globset"
-version = "0.4.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
-dependencies = [
- "aho-corasick",
- "bstr",
- "log",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "globwalk"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
-dependencies = [
- "bitflags",
- "ignore",
- "walkdir",
-]
-
-[[package]]
name = "h2"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -799,15 +718,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
-name = "humansize"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
-dependencies = [
- "libm",
-]
-
-[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1016,22 +926,6 @@ dependencies = [
]
[[package]]
-name = "ignore"
-version = "0.4.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
-dependencies = [
- "crossbeam-deque",
- "globset",
- "log",
- "memchr",
- "regex-automata",
- "same-file",
- "walkdir",
- "winapi-util",
-]
-
-[[package]]
name = "indexmap"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1149,6 +1043,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
+name = "memo-map"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b"
+
+[[package]]
+name = "minijinja"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "212b4cab3aad057bc6e611814472905546c533295723b9e26a31c7feb19a8e65"
+dependencies = [
+ "memo-map",
+ "self_cell",
+ "serde",
+]
+
+[[package]]
name = "miniz_oxide"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1260,15 +1171,6 @@ dependencies = [
]
[[package]]
-name = "parse-zoneinfo"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
-dependencies = [
- "regex",
-]
-
-[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1284,89 +1186,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
-name = "pest"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
-dependencies = [
- "memchr",
- "thiserror",
- "ucd-trie",
-]
-
-[[package]]
-name = "pest_derive"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
-dependencies = [
- "pest",
- "pest_generator",
-]
-
-[[package]]
-name = "pest_generator"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
-dependencies = [
- "pest",
- "pest_meta",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "pest_meta"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
-dependencies = [
- "once_cell",
- "pest",
- "sha2",
-]
-
-[[package]]
-name = "phf"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
-dependencies = [
- "phf_shared",
-]
-
-[[package]]
-name = "phf_codegen"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
-dependencies = [
- "phf_generator",
- "phf_shared",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
-dependencies = [
- "phf_shared",
- "rand",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
-dependencies = [
- "siphasher",
-]
-
-[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1607,21 +1426,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
+name = "self_cell"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
+
+[[package]]
name = "serde"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1713,12 +1529,6 @@ dependencies = [
]
[[package]]
-name = "siphasher"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
-
-[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1728,16 +1538,6 @@ dependencies = [
]
[[package]]
-name = "slug"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724"
-dependencies = [
- "deunicode",
- "wasm-bindgen",
-]
-
-[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2029,28 +1829,6 @@ dependencies = [
]
[[package]]
-name = "tera"
-version = "1.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee"
-dependencies = [
- "chrono",
- "chrono-tz",
- "globwalk",
- "humansize",
- "lazy_static",
- "percent-encoding",
- "pest",
- "pest_derive",
- "rand",
- "regex",
- "serde",
- "serde_json",
- "slug",
- "unic-segment",
-]
-
-[[package]]
name = "thiserror"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2199,62 +1977,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
-name = "ucd-trie"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
-
-[[package]]
-name = "unic-char-property"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
-dependencies = [
- "unic-char-range",
-]
-
-[[package]]
-name = "unic-char-range"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
-
-[[package]]
-name = "unic-common"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
-
-[[package]]
-name = "unic-segment"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
-dependencies = [
- "unic-ucd-segment",
-]
-
-[[package]]
-name = "unic-ucd-segment"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
-dependencies = [
- "unic-char-property",
- "unic-char-range",
- "unic-ucd-version",
-]
-
-[[package]]
-name = "unic-ucd-version"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
-dependencies = [
- "unic-common",
-]
-
-[[package]]
name = "unicode-bidi"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2329,16 +2051,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
-name = "walkdir"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
-[[package]]
name = "want"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2437,15 +2149,6 @@ dependencies = [
]
[[package]]
-name = "winapi-util"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
-dependencies = [
- "windows-sys 0.59.0",
-]
-
-[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 1f89362..d206b30 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,7 @@ http-body-util = "0.1"
hyper = { version = "1.5", features = ["full"] }
hyper-util = { version = "0.1", features = ["full"] }
log = "0.4"
+minijinja = { version = "2.6", features = ["loader"] }
rand = { version = "0.8", features = ["getrandom"] }
rand_core = "0.6"
serde = { version = "1.0", features = ["derive"] }
@@ -22,7 +23,6 @@ serde_urlencoded = "0.7"
sha2 = "0.10"
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite", "chrono"] }
sqlx-core = "0.8"
-tera = { version = "1.20", features = ["builtins"] }
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7", features = ["codec"] }
url = "2.5"
diff --git a/src/assets.rs b/src/assets.rs
index 36fab55..fdfbfdd 100644
--- a/src/assets.rs
+++ b/src/assets.rs
@@ -1,8 +1,8 @@
use sha2::{Digest, Sha256};
use std::collections::HashMap;
+use std::fmt::Write;
use std::fs;
use std::iter::FromIterator;
-use std::fmt::Write;
pub fn get() -> HashMap<String, String> {
let paths = fs::read_dir("assets").unwrap().map(|e| {
diff --git a/src/controller/balance.rs b/src/controller/balance.rs
index c5d9d4a..efe5af6 100644
--- a/src/controller/balance.rs
+++ b/src/controller/balance.rs
@@ -2,7 +2,6 @@ use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
use std::collections::HashMap;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -31,18 +30,16 @@ pub async fn get(wallet: &Wallet) -> Response<Full<Bytes>> {
let exceeding_payers =
payer::exceeding(&users, &user_incomes, &user_payments);
- let mut context = Context::new();
- context.insert("header", &templates::Header::Balance);
- context.insert("connected_user", &wallet.user);
- context.insert(
- "incomes_from",
- &incomes_from.map(|d| d.format("%d/%m/%Y").to_string()),
+ let context = minijinja::context!(
+ header => templates::Header::Balance,
+ connected_user => wallet.user,
+ incomes_from => incomes_from.map(|d| d.format("%d/%m/%Y").to_string()),
+ total_income => total_income,
+ user_incomes => template_user_incomes,
+ total_payments => total_payments,
+ user_payments => template_user_payments,
+ exceeding_payers => exceeding_payers
);
- context.insert("total_income", &total_income);
- context.insert("user_incomes", &template_user_incomes);
- context.insert("total_payments", &total_payments);
- context.insert("user_payments", &template_user_payments);
- context.insert("exceeding_payers", &exceeding_payers);
utils::template(&wallet.assets, &wallet.templates, "balance.html", context)
}
diff --git a/src/controller/categories.rs b/src/controller/categories.rs
index ff2d8e7..fbbd309 100644
--- a/src/controller/categories.rs
+++ b/src/controller/categories.rs
@@ -2,7 +2,6 @@ use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
use std::collections::HashMap;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -17,11 +16,12 @@ pub async fn table(
) -> Response<Full<Bytes>> {
let categories = db::categories::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Categories);
- context.insert("connected_user", &wallet.user);
- context.insert("categories", &categories);
- context.insert("highlight", &query.highlight);
+ let context = minijinja::context!(
+ header => templates::Header::Categories,
+ connected_user => wallet.user,
+ categories => categories,
+ highlight => query.highlight
+ );
utils::template(
&wallet.assets,
@@ -40,11 +40,12 @@ async fn create_form_feedback(
form: HashMap<String, String>,
error: Option<String>,
) -> Response<Full<Bytes>> {
- let mut context = Context::new();
- context.insert("header", &templates::Header::Categories);
- context.insert("connected_user", &wallet.user.clone());
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => &templates::Header::Categories,
+ connected_user => &wallet.user.clone(),
+ form => &form,
+ error => &error
+ );
utils::template(
&wallet.assets,
@@ -89,14 +90,15 @@ async fn update_form_feedback(
let is_category_used =
db::payments::is_category_used(&wallet.pool, id).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Categories);
- context.insert("connected_user", &wallet.user);
- context.insert("id", &id);
- context.insert("category", &category);
- context.insert("is_category_used", &is_category_used);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => templates::Header::Categories,
+ connected_user => wallet.user,
+ id => id,
+ category => category,
+ is_category_used => is_category_used,
+ form => form,
+ error => error
+ );
utils::template(
&wallet.assets,
diff --git a/src/controller/error.rs b/src/controller/error.rs
index 0f6dcc1..fb1375c 100644
--- a/src/controller/error.rs
+++ b/src/controller/error.rs
@@ -3,7 +3,6 @@ use hyper::body::Bytes;
use hyper::header::CACHE_CONTROL;
use hyper::Response;
use std::collections::HashMap;
-use tera::{Context, Tera};
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -24,14 +23,15 @@ pub fn error(
pub fn template(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
title: &str,
message: &str,
) -> String {
- let mut context = Context::new();
- context.insert("title", title);
- context.insert("message", message);
- context.insert("assets", assets);
+ let context = minijinja::context!(
+ title => title,
+ message => message,
+ assets => assets
+ );
- templates.render("error.html", &context).unwrap()
+ templates.render_str("error.html", &context).unwrap()
}
diff --git a/src/controller/incomes.rs b/src/controller/incomes.rs
index f22098b..ac3a332 100644
--- a/src/controller/incomes.rs
+++ b/src/controller/incomes.rs
@@ -4,7 +4,6 @@ use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
use std::collections::HashMap;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -24,13 +23,14 @@ pub async fn table(
let incomes = db::incomes::list(&wallet.pool, page, PER_PAGE).await;
let max_page = (count as f32 / PER_PAGE as f32).ceil() as i64;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Incomes);
- context.insert("connected_user", &wallet.user);
- context.insert("incomes", &incomes);
- context.insert("page", &page);
- context.insert("max_page", &max_page);
- context.insert("highlight", &query.highlight);
+ let context = minijinja::context!(
+ header => templates::Header::Incomes,
+ connected_user => wallet.user,
+ incomes => incomes,
+ page => page,
+ max_page => max_page,
+ highlight => query.highlight
+ );
utils::template(
&wallet.assets,
@@ -70,15 +70,16 @@ async fn create_form_feedback(
) -> Response<Full<Bytes>> {
let users = db::users::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Incomes);
- context.insert("connected_user", &wallet.user);
- context.insert("users", &users);
- context.insert("query", &query);
- context.insert("current_month", &Utc::now().date_naive().month());
- context.insert("months", &MONTHS);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => templates::Header::Incomes,
+ connected_user => wallet.user,
+ users => users,
+ query => query,
+ current_month => Utc::now().date_naive().month(),
+ months => MONTHS,
+ form => form,
+ error => error,
+ );
utils::template(
&wallet.assets,
@@ -144,16 +145,17 @@ async fn update_form_feedback(
let users = db::users::list(&wallet.pool).await;
let income = db::incomes::get(&wallet.pool, id).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Incomes);
- context.insert("connected_user", &wallet.user);
- context.insert("users", &users);
- context.insert("id", &id);
- context.insert("income", &income);
- context.insert("query", &query);
- context.insert("months", &MONTHS);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => &templates::Header::Incomes,
+ connected_user => &wallet.user,
+ users => &users,
+ id => &id,
+ income => &income,
+ query => &query,
+ months => &MONTHS,
+ form => &form,
+ error => &error
+ );
utils::template(
&wallet.assets,
diff --git a/src/controller/login.rs b/src/controller/login.rs
index a1bf466..31370cc 100644
--- a/src/controller/login.rs
+++ b/src/controller/login.rs
@@ -5,7 +5,6 @@ use hyper::header::SET_COOKIE;
use hyper::Response;
use sqlx::sqlite::SqlitePool;
use std::collections::HashMap;
-use tera::{Context, Tera};
use crate::controller::utils::with_headers;
use crate::controller::wallet::Wallet;
@@ -18,14 +17,15 @@ use crate::validation;
pub async fn page(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
error: Option<&str>,
) -> Response<Full<Bytes>> {
let connected_user: Option<User> = None;
- let mut context = Context::new();
- context.insert("connected_user", &connected_user);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ connected_user => &connected_user,
+ error => &error
+ );
utils::template(assets, templates, "login.html", context)
}
@@ -33,7 +33,7 @@ pub async fn page(
pub async fn login(
config: &Config,
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
form: HashMap<String, String>,
pool: SqlitePool,
) -> Response<Full<Bytes>> {
@@ -75,7 +75,10 @@ pub async fn login(
}
Ok(false) => not_authorized(assets, templates).await,
Err(err) => {
- log::error!("Error verifying bcrypt password: {:?}", err);
+ log::error!(
+ "Error verifying bcrypt password: {:?}",
+ err
+ );
server_error(assets, templates, "Erreur serveur").await
}
},
@@ -88,7 +91,7 @@ pub async fn login(
async fn server_error(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
msg: &str,
) -> Response<Full<Bytes>> {
page(assets, templates, Some(msg)).await
@@ -96,7 +99,7 @@ async fn server_error(
async fn not_authorized(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
) -> Response<Full<Bytes>> {
page(
assets,
diff --git a/src/controller/payments.rs b/src/controller/payments.rs
index 8184015..b5c0256 100644
--- a/src/controller/payments.rs
+++ b/src/controller/payments.rs
@@ -3,7 +3,6 @@ use hyper::body::Bytes;
use hyper::header::CONTENT_TYPE;
use hyper::Response;
use std::collections::HashMap;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -27,17 +26,18 @@ pub async fn table(
let users = db::users::list(&wallet.pool).await;
let categories = db::categories::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Payments);
- context.insert("connected_user", &wallet.user);
- context.insert("payments", &payments);
- context.insert("page", &page);
- context.insert("max_page", &max_page);
- context.insert("query", &query);
- context.insert("count", &count.count);
- context.insert("total_cost", &count.total_cost);
- context.insert("users", &users);
- context.insert("categories", &categories);
+ let context = minijinja::context!(
+ header => templates::Header::Payments,
+ connected_user => wallet.user,
+ payments => payments,
+ page => page,
+ max_page => max_page,
+ query => query,
+ count => count.count,
+ total_cost => count.total_cost,
+ users => users,
+ categories => categories
+ );
utils::template(
&wallet.assets,
@@ -63,14 +63,15 @@ async fn create_form_feedback(
let users = db::users::list(&wallet.pool).await;
let categories = db::categories::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Payments);
- context.insert("connected_user", &wallet.user);
- context.insert("users", &users);
- context.insert("categories", &categories);
- context.insert("query", &query);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => templates::Header::Payments,
+ connected_user => wallet.user,
+ users => users,
+ categories => categories,
+ query => query,
+ form => form,
+ error => error
+ );
utils::template(
&wallet.assets,
@@ -142,16 +143,17 @@ async fn update_form_feedback(
let users = db::users::list(&wallet.pool).await;
let categories = db::categories::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Payments);
- context.insert("connected_user", &wallet.user);
- context.insert("id", &id);
- context.insert("payment", &payment);
- context.insert("users", &users);
- context.insert("categories", &categories);
- context.insert("query", &query);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => templates::Header::Payments,
+ connected_user => wallet.user,
+ id => id,
+ payment => payment,
+ users => users,
+ categories => categories,
+ query => query,
+ form => form,
+ error => error
+ );
utils::template(
&wallet.assets,
diff --git a/src/controller/statistics.rs b/src/controller/statistics.rs
index eb1e704..e57e2be 100644
--- a/src/controller/statistics.rs
+++ b/src/controller/statistics.rs
@@ -1,7 +1,6 @@
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -13,15 +12,13 @@ pub async fn get(wallet: &Wallet) -> Response<Full<Bytes>> {
let payments = db::payments::list_for_stats(&wallet.pool).await;
let incomes = db::incomes::total_each_month(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Statistics);
- context.insert("connected_user", &wallet.user);
- context.insert(
- "json_categories",
- &serde_json::to_string(&categories).unwrap(),
+ let context = minijinja::context!(
+ header => templates::Header::Statistics,
+ connected_user => wallet.user,
+ json_categories => serde_json::to_string(&categories).unwrap(),
+ json_payments => serde_json::to_string(&payments).unwrap(),
+ json_incomes => serde_json::to_string(&incomes).unwrap()
);
- context.insert("json_payments", &serde_json::to_string(&payments).unwrap());
- context.insert("json_incomes", &serde_json::to_string(&incomes).unwrap());
utils::template(
&wallet.assets,
diff --git a/src/controller/utils.rs b/src/controller/utils.rs
index 1b58c68..340a5c7 100644
--- a/src/controller/utils.rs
+++ b/src/controller/utils.rs
@@ -5,7 +5,6 @@ use hyper::header::{
};
use hyper::{Response, StatusCode};
use std::collections::HashMap;
-use tera::{Context, Tera};
use crate::controller::error;
@@ -23,29 +22,45 @@ pub fn with_headers(
pub fn template(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
path: &str,
- context: Context,
+ context: minijinja::Value,
) -> Response<Full<Bytes>> {
- let mut context = context;
- context.insert("assets", assets);
+ let context = minijinja::context! { ..context, ..minijinja::context! {
+ assets => assets
+ }};
- match templates.render(path, &context) {
+ match render_template(templates, path, context) {
Ok(template) => with_headers(
Response::new(template.into()),
vec![(CONTENT_TYPE, "text/html"), (CACHE_CONTROL, "no-cache")],
),
- Err(err) => server_error(
- assets,
- templates,
- &format!("Erreur lors de la préparation de la page : {:?}", err),
- ),
+ Err(err) => {
+ log::error!("ERROR template rendering {}\n{:?}", path, err);
+ server_error(
+ assets,
+ templates,
+ &format!(
+ "Erreur lors de la préparation de la page : {:?}",
+ err
+ ),
+ )
+ }
}
}
+fn render_template(
+ templates: &minijinja::Environment<'_>,
+ name: &str,
+ context: minijinja::Value,
+) -> Result<String, minijinja::Error> {
+ let template = templates.get_template(name)?;
+ template.render(context)
+}
+
fn server_error(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
msg: &str,
) -> Response<Full<Bytes>> {
with_headers(
diff --git a/src/controller/wallet.rs b/src/controller/wallet.rs
index 2a4a593..7537406 100644
--- a/src/controller/wallet.rs
+++ b/src/controller/wallet.rs
@@ -1,6 +1,5 @@
use sqlx::sqlite::SqlitePool;
use std::collections::HashMap;
-use tera::Tera;
use crate::model::user::User;
@@ -8,6 +7,6 @@ use crate::model::user::User;
pub struct Wallet {
pub pool: SqlitePool,
pub assets: HashMap<String, String>,
- pub templates: Tera,
+ pub templates: minijinja::Environment<'static>,
pub user: User,
}
diff --git a/src/db/jobs.rs b/src/db/jobs.rs
index a80ef68..7d9386a 100644
--- a/src/db/jobs.rs
+++ b/src/db/jobs.rs
@@ -48,6 +48,8 @@ WHERE
match res {
Ok(_) => (),
- Err(err) => log::error!("Error actualizing job last execution: {:?}", err),
+ Err(err) => {
+ log::error!("Error actualizing job last execution: {:?}", err)
+ }
}
}
diff --git a/src/db/payments.rs b/src/db/payments.rs
index b415a28..25b10f4 100644
--- a/src/db/payments.rs
+++ b/src/db/payments.rs
@@ -495,7 +495,8 @@ ORDER BY
Err(err) => {
log::error!(
"Error looking for the category of {}: {:?}",
- payment_name, err
+ payment_name,
+ err
);
None
}
@@ -523,7 +524,8 @@ LIMIT
Err(err) => {
log::error!(
"Error looking if category {} is used: {:?}",
- category_id, err
+ category_id,
+ err
);
false
}
diff --git a/src/jobs/mod.rs b/src/jobs/mod.rs
index 17df58c..a718d93 100644
--- a/src/jobs/mod.rs
+++ b/src/jobs/mod.rs
@@ -1,14 +1,17 @@
mod weekly_report;
use sqlx::sqlite::SqlitePool;
-use tera::Tera;
use tokio::time::{sleep, Duration};
use crate::db;
use crate::model::config::Config;
use crate::model::job::Job;
-pub async fn start(config: Config, pool: SqlitePool, templates: Tera) {
+pub async fn start(
+ config: Config,
+ pool: SqlitePool,
+ templates: minijinja::Environment<'_>,
+) {
loop {
if db::jobs::should_run(&pool, Job::WeeklyReport).await {
log::info!("Starting weekly report job");
diff --git a/src/jobs/weekly_report.rs b/src/jobs/weekly_report.rs
index 0c10143..5058c52 100644
--- a/src/jobs/weekly_report.rs
+++ b/src/jobs/weekly_report.rs
@@ -1,6 +1,5 @@
use sqlx::sqlite::SqlitePool;
use std::collections::HashMap;
-use tera::{Context, Tera};
use crate::db;
use crate::mail;
@@ -10,9 +9,9 @@ use crate::payer;
pub async fn send(
config: &Config,
pool: &SqlitePool,
- templates: &Tera,
+ env: &minijinja::Environment<'_>,
) -> bool {
- match get_weekly_report(pool, templates).await {
+ match get_weekly_report(pool, env).await {
Ok(report) => {
let users = db::users::list(pool).await;
mail::send(
@@ -30,7 +29,10 @@ pub async fn send(
.await
}
Err(err) => {
- log::error!("Error preparing weekly report from template: {:?}", err);
+ log::error!(
+ "Error preparing weekly report from template: {:?}",
+ err
+ );
false
}
}
@@ -38,8 +40,8 @@ pub async fn send(
async fn get_weekly_report(
pool: &SqlitePool,
- templates: &Tera,
-) -> Result<String, tera::Error> {
+ env: &minijinja::Environment<'_>,
+) -> Result<String, minijinja::Error> {
let users = db::users::list(pool).await;
let incomes_from = db::incomes::defined_for_all(pool).await;
let user_incomes = match incomes_from {
@@ -53,10 +55,11 @@ async fn get_weekly_report(
let last_week_payments = db::payments::last_week(pool).await;
let last_week_incomes = db::incomes::last_week(pool).await;
- let mut context = Context::new();
- context.insert("exceeding_payers", &exceeding_payers);
- context.insert("payments", &last_week_payments);
- context.insert("incomes", &last_week_incomes);
-
- templates.render("report/report.j2", &context)
+ let template = env.get_template("report/report.j2")?;
+ template.render(minijinja::context!(
+ name => "John",
+ exceeding_payers => exceeding_payers,
+ payments => last_week_payments,
+ incomes => last_week_incomes
+ ))
}
diff --git a/src/mail.rs b/src/mail.rs
index c77e2ad..b6db0cd 100644
--- a/src/mail.rs
+++ b/src/mail.rs
@@ -56,7 +56,9 @@ pub async fn send(
true
} else {
match String::from_utf8(output.stderr) {
- Ok(error) => log::error!("Error sending email: {}", error),
+ Ok(error) => {
+ log::error!("Error sending email: {}", error)
+ }
_ => log::error!("Error sending email"),
};
false
diff --git a/src/main.rs b/src/main.rs
index 2b3aebd..5fe8a94 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,7 +22,6 @@ use model::config;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
-
env_logger::init();
let config = config::from_env()
@@ -34,7 +33,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let assets = assets::get();
- let templates = templates::get();
+ let templates = templates::get()?;
tokio::spawn(jobs::start(config.clone(), pool.clone(), templates.clone()));
diff --git a/src/routes.rs b/src/routes.rs
index ae87d39..5f17ca5 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -5,7 +5,6 @@ use serde::Deserialize;
use sqlx::sqlite::SqlitePool;
use std::collections::HashMap;
use std::convert::Infallible;
-use tera::Tera;
use url::form_urlencoded;
use crate::controller;
@@ -20,7 +19,7 @@ pub async fn routes(
config: Config,
pool: SqlitePool,
assets: HashMap<String, String>,
- templates: Tera,
+ templates: minijinja::Environment<'static>,
request: Request<Incoming>,
) -> Result<Response<Full<Bytes>>, Infallible> {
let method = request.method();
diff --git a/src/templates.rs b/src/templates.rs
index 1f86717..c9a750b 100644
--- a/src/templates.rs
+++ b/src/templates.rs
@@ -1,9 +1,5 @@
use serde::Serialize;
-use serde_json::json;
-use serde_json::value::Value;
-use std::collections::HashMap;
-use tera::Tera;
-use tera::{Error, Result};
+use std::fs;
use crate::queries;
@@ -16,71 +12,123 @@ pub enum Header {
Statistics,
}
-pub fn get() -> Tera {
- let mut tera = match Tera::new("templates/**/*") {
- Ok(t) => t,
- Err(e) => {
- log::error!("Parsing error(s): {}", e);
- ::std::process::exit(1);
- }
+pub fn get() -> Result<minijinja::Environment<'static>, String> {
+ let mut env = minijinja::Environment::new();
+ for path in read_files_recursive("templates") {
+ let path = path
+ .to_str()
+ .ok_or("Error getting string of path: {path:?}")?
+ .to_string();
+ let content = fs::read_to_string(&path)
+ .map_err(|_| "Error reading template {path}")?;
+ let path_without_prefix = path
+ .strip_prefix("templates/")
+ .ok_or("Error removing prefix from template path")?
+ .to_string();
+ env.add_template_owned(path_without_prefix, content)
+ .map_err(|_| "Error adding template {path} to environment")?;
+ }
+
+ env.add_function("payments_params", payments_params);
+ env.add_function("pluralize", pluralize);
+ env.add_function("now", now);
+
+ env.add_filter("numeric", numeric);
+ env.add_filter("euros", euros);
+ env.add_filter("round", round);
+ env.add_filter("with_param", with_param);
+ env.add_filter("filter", filter);
+
+ Ok(env)
+}
+
+fn read_files_recursive(
+ path: impl AsRef<std::path::Path>,
+) -> Vec<std::path::PathBuf> {
+ let Ok(entries) = fs::read_dir(path) else {
+ return vec![];
};
- tera.register_function("payments_params", payments_params);
- tera.register_filter("numeric", numeric);
- tera.register_filter("euros", euros);
- tera
+ entries
+ .flatten()
+ .flat_map(|entry| {
+ let Ok(meta) = entry.metadata() else {
+ return vec![];
+ };
+ if meta.is_dir() {
+ return read_files_recursive(entry.path());
+ }
+ if meta.is_file() {
+ return vec![entry.path()];
+ }
+ vec![]
+ })
+ .collect()
}
-fn payments_params(args: &HashMap<String, Value>) -> Result<Value> {
- let q = json!({
- "page": args.get("page"),
- "name": args.get("name"),
- "cost": args.get("cost"),
- "frequency": args.get("frequency"),
- "highlight": args.get("highlight"),
- "user": args.get("user"),
- "category": args.get("category"),
- "start_date": args.get("start_date"),
- "end_date": args.get("end_date"),
- });
-
- match serde_json::from_value(q) {
- Ok(q) => Ok(json!(queries::payments_url(q))),
- Err(msg) => Err(Error::msg(msg)),
+fn payments_params(value: minijinja::Value) -> String {
+ let str = value.to_string().replace("none", "null");
+ match serde_json::from_str(&str) {
+ Ok(q) => queries::payments_url(q),
+ Err(err) => {
+ log::error!("Error parsing payments params {}: {:?}", str, err);
+ "".to_string()
+ }
}
}
-fn euros(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
- match value {
- Value::Number(n) => {
- if let Some(n) = n.as_i64() {
- let str = rgrouped(n.abs().to_string(), 3).join(" ");
- let sign = if n < 0 { "-" } else { "" };
- Ok(json!(format!("{}{} €", sign, str)))
- } else if let Some(n) = n.as_f64() {
- Ok(json!(format!("{} €", n)))
- } else {
- Err(Error::msg("Error parsing number"))
- }
- }
- _ => Err(Error::msg(format!("{:?} should be a number", value))),
+fn now(format: &str) -> String {
+ let date = chrono::Local::now();
+ format!("{}", date.format(format))
+}
+
+fn euros(n: i64) -> String {
+ let str = rgrouped(n.abs().to_string(), 3).join(" ");
+ let sign = if n < 0 { "-" } else { "" };
+ format!("{}{} €", sign, str)
+}
+
+fn numeric(n: i64) -> String {
+ let str = rgrouped(n.abs().to_string(), 3).join(" ");
+ let sign = if n < 0 { "-" } else { "" };
+ format!("{}{}", sign, str)
+}
+
+fn pluralize(n: i32, s: String) -> String {
+ if n > 0 {
+ format!("{s}s")
+ } else {
+ s
+ }
+}
+
+fn round(n: f32) -> i32 {
+ n.round() as i32
+}
+
+fn with_param(url: String, key: String, value: String) -> String {
+ if url.contains("?") {
+ format!("{url}&{key}={value}")
+ } else {
+ format!("{url}?{key}={value}")
}
}
-fn numeric(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
- match value {
- Value::Number(n) => {
- if let Some(n) = n.as_i64() {
- let str = rgrouped(n.abs().to_string(), 3).join(" ");
- let sign = if n < 0 { "-" } else { "" };
- Ok(json!(format!("{}{}", sign, str)))
- } else if let Some(n) = n.as_f64() {
- Ok(json!(format!("{}", n)))
- } else {
- Err(Error::msg("Error parsing number"))
+fn filter(
+ xs: Vec<minijinja::Value>,
+ key: &str,
+ value: String,
+) -> Vec<minijinja::Value> {
+ let mut res = vec![];
+ for x in xs {
+ if let Ok(v) = x.get_attr(key) {
+ if let Some(v) = v.as_str() {
+ if v == value {
+ res.push(x);
+ }
}
}
- _ => Err(Error::msg(format!("{:?} should be a number", value))),
}
+ res
}
fn rgrouped(str: String, n: usize) -> Vec<String> {
diff --git a/templates/balance.html b/templates/balance.html
index b97ea40..c7f68e8 100644
--- a/templates/balance.html
+++ b/templates/balance.html
@@ -11,7 +11,7 @@
<ul class="g-Balance__ExceedingPayers">
{% for exceeding_payer in exceeding_payers %}
<li class="g-Balance__ExceedingPayer">
- {{ exceeding_payer.0 }} : +{{ exceeding_payer.1 | euros() }}
+ {{ exceeding_payer[0] }} : +{{ exceeding_payer[1] | euros() }}
</li>
{% endfor %}
</ul>
@@ -35,14 +35,14 @@
{% for user_income in user_incomes %}
<div class="g-Table__Row">
<span class="g-Table__Cell">
- {{ user_income.0 }}
+ {{ user_income[0] }}
</span>
<span class="g-Table__Cell g-Table__NumericCell">
- {{ user_income.1 | euros() }}
+ {{ user_income[1] | euros() }}
</span>
<span class="g-Table__Cell g-Table__NumericCell">
{% if total_income > 0 %}
- {{ user_income.1 / total_income * 100 | round() }} %
+ {{ (user_income[1] / total_income * 100) | round }} %
{% else %}
{% endif %}
@@ -76,14 +76,14 @@
{% for user_payment in user_payments %}
<div class="g-Table__Row">
<span class="g-Table__Cell">
- {{ user_payment.0 }}
+ {{ user_payment[0] }}
</span>
<span class="g-Table__Cell g-Table__NumericCell">
- {{ user_payment.1 | euros() }}
+ {{ user_payment[1] | euros() }}
</span>
<span class="g-Table__Cell g-Table__NumericCell">
{% if total_payments > 0 %}
- {{ user_payment.1 / total_payments * 100 | round() }} %
+ {{ (user_payment[1] / total_payments * 100) | round }} %
{% else %}
{% endif %}
diff --git a/templates/base.html b/templates/base.html
index c7dc9f0..9865e16 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -4,8 +4,8 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Budget — {% block title %}{% endblock title %}</title>
-<link rel="stylesheet" href="{{ assets | get(key="main.css") }}">
-<link rel="icon" href="{{ assets | get(key="icon.png") }}">
+<link rel="stylesheet" href="{{ assets["main.css"] }}">
+<link rel="icon" href="{{ assets["icon.png"] }}">
{% if connected_user %}
<header class="g-Header">
@@ -78,5 +78,5 @@
{% block main %}{% endblock main %}
</main>
-<script src="{{ assets | get(key="main.js") }}">
+<script src="{{ assets["main.js"] }}">
</script>
diff --git a/templates/category/create.html b/templates/category/create.html
index af95e16..f5395bf 100644
--- a/templates/category/create.html
+++ b/templates/category/create.html
@@ -27,7 +27,7 @@
<input
name="name"
class="g-Form__Input"
- value="{{ form.name | default(value="") }}"
+ value="{{ form.name or "" }}"
required
{% if not form %} autofocus {% endif %}
/>
@@ -39,7 +39,7 @@
name="color"
type="color"
class="g-Form__Input g-Form__InputColor"
- value="{{ form.color | default(value="") }}"
+ value="{{ form.color or "" }}"
required
/>
</label>
diff --git a/templates/category/update.html b/templates/category/update.html
index 48dda06..544c583 100644
--- a/templates/category/update.html
+++ b/templates/category/update.html
@@ -35,7 +35,7 @@
<input
name="name"
class="g-Form__Input"
- value="{{ form.name | default(value=category.name) }}"
+ value="{{ form.name or category.name }}"
required
/>
</label>
@@ -46,7 +46,7 @@
name="color"
type="color"
class="g-Form__Input g-Form__InputColor"
- value="{{ form.color | default(value=category.color) }}"
+ value="{{ form.color or category.color }}"
required
/>
</label>
diff --git a/templates/income/create.html b/templates/income/create.html
index 3c899ca..a7755db 100644
--- a/templates/income/create.html
+++ b/templates/income/create.html
@@ -10,7 +10,7 @@
<p class="g-Paragraph">
<a
class="g-Link g-Media__Large"
- href="/incomes?page={{ query.page | default(value=1) }}"
+ href="/incomes?page={{ query.page or 1 }}"
>
Retour aux revenus
</a>
@@ -18,7 +18,7 @@
<form
class="g-Form"
- action="/income/create?page={{ query.page | default(value=1) }}"
+ action="/income/create?page={{ query.page or 1 }}"
method="POST"
>
<h1 class="g-H1">
@@ -35,13 +35,13 @@
name="amount"
type="number"
class="g-Form__Input"
- value="{{ form.amount | default(value="") }}"
+ value="{{ form.amount or "" }}"
required
{% if not form %} autofocus {% endif %}
/>
</label>
- {% set user_id = form.user_id | default(value="" ~ connected_user.id) %}
+ {% set user_id = form.user_id or connected_user.id %}
<label class="g-Form__Label">
Personne
@@ -57,7 +57,7 @@
</select>
</label>
- {% set month_index = form.month | default(value="" ~ current_month) %}
+ {% set month_index = form.month or current_month %}
<label class="g-Form__Label">
Mois
@@ -81,7 +81,7 @@
name="year"
type="number"
class="g-Form__Input"
- value="{{ form.year | default(value=now() | date(format="%Y")) }}"
+ value="{{ form.year or now("%Y") }}"
required
/>
</label>
diff --git a/templates/income/table.html b/templates/income/table.html
index 60cd6e0..34ee096 100644
--- a/templates/income/table.html
+++ b/templates/income/table.html
@@ -18,7 +18,7 @@
<a
class="g-Paragraph g-Button__Validate"
- href="/income?page={{ page | default(value=1) }}"
+ href="/income?page={{ page or 1 }}"
>
Ajouter un revenu
</a>
@@ -33,7 +33,7 @@
{% for income in incomes %}
<a
class="g-Table__Row {% if highlight == income.id %} g-Table__Row--Highlight {% endif %}"
- href="/income/{{ income.id }}?page={{ page | default(value=1) }}"
+ href="/income/{{ income.id }}?page={{ page or 1 }}"
>
<span class="g-Table__Cell g-Table__NumericCell">
{{ income.amount | euros() }}
@@ -44,7 +44,7 @@
{% endfor %}
</div>
- {{ paging::paging(
+ {{ paging.view(
url="/incomes",
page=page,
max_page=max_page
diff --git a/templates/income/update.html b/templates/income/update.html
index 855d5c4..f5f976e 100644
--- a/templates/income/update.html
+++ b/templates/income/update.html
@@ -10,7 +10,7 @@
<p class="g-Paragraph">
<a
class="g-Link g-Media__Large"
- href="/incomes?page={{ query.page | default(value=1) }}"
+ href="/incomes?page={{ query.page or 1 }}"
>
Retour aux revenus
</a>
@@ -39,12 +39,12 @@
name="amount"
type="number"
class="g-Form__Input"
- value="{{ form.amount | default(value=income.amount) }}"
+ value="{{ form.amount or income.amount }}"
required
/>
</label>
- {% set user_id = form.user_id | default(value="" ~ income.user_id) %}
+ {% set user_id = form.user_id or income.user_id %}
<label class="g-Form__Label">
Personne
@@ -60,7 +60,7 @@
</select>
</label>
- {% set month_index = form.month | default(value="" ~ income.month) %}
+ {% set month_index = form.month or income.month %}
<label class="g-Form__Label">
Mois
@@ -82,7 +82,7 @@
name="year"
type="number"
class="g-Form__Input"
- value="{{ form.year | default(value=income.year) }}"
+ value="{{ form.year or income.year }}"
required
/>
</label>
diff --git a/templates/macros/paging.html b/templates/macros/paging.html
index 59ba617..840e8f4 100644
--- a/templates/macros/paging.html
+++ b/templates/macros/paging.html
@@ -1,10 +1,4 @@
-{% macro paging(url, page, max_page) %}
- {% if url is containing("?") %}
- {% set sign = "&" %}
- {% else %}
- {% set sign = "?" %}
- {% endif %}
-
+{% macro view(url, page, max_page) %}
<div class="g-Paging">
{% if page > 1 %}
<a
@@ -15,7 +9,7 @@
</a>
<a
class="g-Paging__Link g-Paging__Link--Active"
- href="{{ url }}{{ sign }}page={{ page - 1 }}"
+ href="{{ url | with_param("page", page - 1) }}"
>
</a>
@@ -33,13 +27,13 @@
{% if page < max_page %}
<a
class="g-Paging__Link g-Paging__Link--Active"
- href="{{ url }}{{ sign }}page={{ page + 1 }}"
+ href="{{ url | with_param("page", page + 1) }}"
>
</a>
<a
class="g-Paging__Link g-Paging__Link--Active"
- href="{{ url }}{{ sign }}page={{ max_page }}"
+ href="{{ url | with_param("page", max_page) }}"
>
❭❭
</a>
@@ -52,4 +46,4 @@
</span>
{% endif %}
</div>
-{% endmacro paging %}
+{% endmacro %}
diff --git a/templates/payment/create.html b/templates/payment/create.html
index 4ac73de..7bcc536 100644
--- a/templates/payment/create.html
+++ b/templates/payment/create.html
@@ -44,7 +44,7 @@
<input
name="name"
class="g-Form__Input"
- value="{{ form.name | default(value="") }}"
+ value="{{ form.name or "" }}"
required
{% if not form %} autofocus {% endif %}
/>
@@ -56,12 +56,12 @@
name="cost"
type="number"
class="g-Form__Input"
- value="{{ form.cost | default(value="") }}"
+ value="{{ form.cost or "" }}"
required
/>
</label>
- {% set user_id = form.user_id | default(value="" ~ connected_user.id) %}
+ {% set user_id = form.user_id or connected_user.id %}
<label class="g-Form__Label">
Personne
@@ -79,7 +79,7 @@
</select>
</label>
- {% set category_id = form.category_id | default(value="") %}
+ {% set category_id = form.category_id or "" %}
<label class="g-Form__Label">
Catégorie
@@ -96,7 +96,7 @@
</select>
</label>
- {% set date = form.date | default(value=now() | date(format="%Y-%m-%d")) %}
+ {% set date = form.date or now("%Y-%m-%d") %}
{% if query.frequency != "Monthly" %}
<label class="g-Form__Label">
@@ -120,7 +120,7 @@
<input
type="hidden"
name="frequency"
- value="{{ query.frequency | default(value="Punctual") }}"
+ value="{{ query.frequency or "Punctual" }}"
/>
<div>
diff --git a/templates/payment/table.html b/templates/payment/table.html
index 450d84b..71bfb0a 100644
--- a/templates/payment/table.html
+++ b/templates/payment/table.html
@@ -20,7 +20,7 @@
</div>
{% else %}
<div class="g-Paragraph">
- {{ count | numeric }} paiement{{ count | pluralize }} comptabilisant {{ total_cost | euros() }}.
+ {{ count | numeric }} paiement{{ pluralize(count, "paiement") }} comptabilisant {{ total_cost | euros() }}.
</div>
{% endif %}
@@ -81,9 +81,9 @@
{{ payment.cost | euros() }}
</span>
<span class="g-Table__Cell">{{ payment.user }}</span>
- <span
+ <span
class="g-Table__Cell g-Media__Large"
- is="colored-category"
+ is="colored-category"
data-color="{{ payment.category_color }}"
>
{{ payment.category_name }}
@@ -97,7 +97,7 @@
{% endfor %}
</div>
- {{ paging::paging(
+ {{ paging.view(
url="/" ~ payments_params(
frequency=query.frequency,
name=query.name,
diff --git a/templates/payment/table/search.html b/templates/payment/table/search.html
index 9fedb78..cb72282 100644
--- a/templates/payment/table/search.html
+++ b/templates/payment/table/search.html
@@ -17,7 +17,7 @@
type="search"
name="name"
class="g-Form__Input"
- value="{{ query.name }}"
+ value="{{ query.name or '' }}"
/>
</label>
@@ -27,7 +27,7 @@
type="number"
name="cost"
class="g-Form__Input"
- value="{{ query.cost }}"
+ value="{{ query.cost or '' }}"
/>
</label>
@@ -67,7 +67,7 @@
type="date"
name="start_date"
class="g-Form__Input"
- value="{{ query.start_date }}"
+ value="{{ query.start_date or '' }}"
/>
</label>
@@ -77,7 +77,7 @@
type="date"
name="end_date"
class="g-Form__Input"
- value="{{ query.end_date }}"
+ value="{{ query.end_date or '' }}"
/>
</label>
diff --git a/templates/payment/update.html b/templates/payment/update.html
index 22a4d01..dfa0892 100644
--- a/templates/payment/update.html
+++ b/templates/payment/update.html
@@ -57,7 +57,7 @@
<input
name="name"
class="g-Form__Input"
- value="{{ form.name | default(value=payment.name) }}"
+ value="{{ form.name or payment.name }}"
required
/>
</label>
@@ -68,12 +68,12 @@
name="cost"
type="number"
class="g-Form__Input"
- value="{{ form.cost | default(value=payment.cost) }}"
+ value="{{ form.cost or payment.cost }}"
required
/>
</label>
- {% set user_id = form.user_id | default(value="" ~ payment.user_id) %}
+ {% set user_id = form.user_id or payment.user_id %}
<label class="g-Form__Label">
Personne
@@ -89,7 +89,7 @@
</select>
</label>
- {% set category_id = form.category_id | default(value="" ~ payment.category_id) %}
+ {% set category_id = form.category_id or payment.category_id %}
<label class="g-Form__Label">
Catégorie
@@ -105,7 +105,7 @@
</select>
</label>
- {% set date = form.date | default(value=payment.date) %}
+ {% set date = form.date or payment.date %}
{% if payment.frequency == "Punctual" %}
<label class="g-Form__Label">
diff --git a/templates/report/list.j2 b/templates/report/list.j2
index ef53244..d683879 100644
--- a/templates/report/list.j2
+++ b/templates/report/list.j2
@@ -2,13 +2,13 @@
{% if xs -%}
- {% set s = xs | length | pluralize -%}
+ {% set l = xs | length %}
- {{ xs | length }} {{ resource }}{{ s }} {{ action }}{{ s }} :
+ {{ xs | length }} {{ pluralize(l, resource) }} {{ pluralize(l, action) }} :
{% for x in xs -%}
- {{ x.date }} {{ x.name }} {{ x.amount | euros() }}
{% endfor %}
{% endif -%}
-{% endmacro paging %}
+{% endmacro %}
diff --git a/templates/report/report.j2 b/templates/report/report.j2
index d36f3ce..8711184 100644
--- a/templates/report/report.j2
+++ b/templates/report/report.j2
@@ -5,7 +5,7 @@
Équilibre :
{% for exceeding_payer in exceeding_payers -%}
- - {{ exceeding_payer.0 }} : +{{ exceeding_payer.1 | euros() }}
+ - {{ exceeding_payer[0] }} : +{{ exceeding_payer[1] | euros() }}
{% endfor %}
{% else -%}
@@ -13,38 +13,38 @@
{% endif %}{#
-#}{{ list::list(
+#}{{ list.list(
resource="paiement",
action="créé",
- xs=payments | filter(attribute="action", value="Created")
+ xs=payments | filter("action", "Created")
) }}{#
-#}{{ list::list(
+#}{{ list.list(
resource="paiement",
action="modifié",
- xs=payments | filter(attribute="action", value="Updated")
+ xs=payments | filter("action", "Updated")
) }}{#
-#}{{ list::list(
+#}{{ list.list(
resource="paiement",
action="supprimé",
- xs=payments | filter(attribute="action", value="Deleted")
+ xs=payments | filter("action", "Deleted")
) }}{#
-#}{{ list::list(
+#}{{ list.list(
resource="revenu",
action="créé",
- xs=incomes | filter(attribute="action", value="Created")
+ xs=incomes | filter("action", "Created")
) }}{#
-#}{{ list::list(
+#}{{ list.list(
resource="revenu",
action="modifié",
- xs=incomes | filter(attribute="action", value="Updated")
+ xs=incomes | filter("action", "Updated")
) }}{#
-#}{{ list::list(
+#}{{ list.list(
resource="revenu",
action="supprimé",
- xs=incomes | filter(attribute="action", value="Deleted")
+ xs=incomes | filter("action", "Deleted")
) }}
diff --git a/templates/statistics.html b/templates/statistics.html
index 21e8fcd..8f9b673 100644
--- a/templates/statistics.html
+++ b/templates/statistics.html
@@ -25,7 +25,7 @@
{{ json_payments }}
</div>
- <script src="{{ assets | get(key="chart.js") }}">
+ <script src="{{ assets["chart.js"] }}">
</script>
{% endblock main %}