diff --git a/README.md b/README.md index 59dc207..046a4e2 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ Typst må være installert for PDF-rapporten. Uten Typst kjører du med `--ingen | `enok klima --kommune Oslo` | Viser nøkkeltall for ett klimasted | | `enok skala smaahus --areal 140` | Viser karakterskalaen med arealkorreksjon | -Nyttige valg til `beregn`: `--tillegg FIL`, `--ingen-pdf`, `--ingen-timeserie`, `--enova-xml`, +Nyttige valg til `beregn`: `--tillegg FIL`, `--versjon VERSJON`, `--ingen-pdf`, `--ingen-timeserie`, `--enova-xml`, `--uten-scenarier`, `--ut KATALOG`. ### Utdata @@ -188,6 +188,18 @@ logo = "logo.svg" # vises øverst på forsiden farge = "#880000" # aksentfarge i overskrifter og tabeller avsender = "Firma · Adresse · Org.nr. 000 000 000" skrift_overskrift = "Futura Now Headline" +versjon = "v1.4.0" # prosjektets versjon, se nedenfor +``` + +Forsiden har et internkontrollfelt med hvilket prosjekt rapporten hører til, hvilken versjon +av prosjektet den er laget fra, oppdragsgiver og energirådgiver, datoen og hvilken +enok-versjon som regnet den. Opplysningene står der én gang, ikke i bunnteksten på hver side. + +Prosjektversjonen settes i `rapport.versjon`, eller med `--versjon` på `beregn`. Da kan +byggeserveren sende taggen på utgivelsen uten at noen fil må endres: + +```sh +enok beregn modell.ifc --tillegg prosjekt.toml --versjon "$GITHUB_REF_NAME" ``` Scenarier flettes inn i bygningsbeskrivelsen: tabeller flettes rekursivt, elementer i lister @@ -425,7 +437,7 @@ jobs: - name: Lag rapporten run: | chmod +x enok - ./enok beregn bygning.toml --ut dist + ./enok beregn bygning.toml --ut dist --versjon "$GITHUB_REF_NAME" ``` ## Kontroll av beregningen diff --git a/src/main.rs b/src/main.rs index d31daf3..f38cdcb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,6 +61,10 @@ struct Beregn { /// Tilleggsfil som flettes inn: det modellen ikke beskriver, og rettelser. #[arg(short, long, value_name = "FIL")] tillegg: Option, + /// Prosjektets versjon, som vises i internkontrollfeltet på forsiden. Overstyrer + /// `rapport.versjon`. Byggeserveren sender gjerne taggen på utgivelsen. + #[arg(long, value_name = "VERSJON")] + versjon: Option, } #[derive(Args)] @@ -174,7 +178,11 @@ fn les_modell( } fn beregn(a: Beregn, kilde: &Datakilde) -> Result<()> { - let (bygning, merknader, modell) = les_modell(&a.fil, a.tillegg.as_deref())?; + let (mut bygning, merknader, modell) = les_modell(&a.fil, a.tillegg.as_deref())?; + if let Some(v) = &a.versjon { + bygning.rapport.versjon = Some(v.clone()); + } + let bygning = bygning; for m in &merknader { eprintln!(" · {m}"); } diff --git a/src/modell/mod.rs b/src/modell/mod.rs index ee2f027..89c672e 100644 --- a/src/modell/mod.rs +++ b/src/modell/mod.rs @@ -102,6 +102,10 @@ pub struct Rapport { /// Skrift for overskrifter. Utelatt: enoks egen. #[serde(default, skip_serializing_if = "Option::is_none")] pub skrift_overskrift: Option, + /// Prosjektets versjon, typisk taggen på utgivelsen rapporten hører til, for eksempel + /// «v0.3.12». Vises i internkontrollfeltet på forsiden. Kan settes med `--versjon`. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub versjon: Option, } #[derive(Deserialize, Serialize, Clone, Debug)] diff --git a/src/rapport/typst_ut.rs b/src/rapport/typst_ut.rs index cf2d093..4e7461e 100644 --- a/src/rapport/typst_ut.rs +++ b/src/rapport/typst_ut.rs @@ -165,6 +165,35 @@ fn kilde(r: &Resultat, scenarier: &[(String, Resultat)]) -> String { } s.push_str(" ]\n)\n\n#v(0.6cm)\n"); + // Internkontroll: hvilket prosjekt rapporten hører til, hvilken versjon av det, og + // hva den er laget med. Står på forsiden i stedet for i bunnteksten på hver side. + let mut felter: Vec<(String, String)> = Vec::new(); + // Adressen står allerede under tittelen på forsiden. + felter.push(("Prosjekt".into(), l.navn.clone())); + if let Some(v) = &rap.versjon { + felter.push(("Prosjektversjon".into(), v.clone())); + } + if let Some(o) = &b.oppdragsgiver { + felter.push(("Oppdragsgiver".into(), o.clone())); + } + if let Some(ra) = &b.energiraadgiver { + felter.push(("Energirådgiver".into(), ra.clone())); + } + felter.push(("Dato".into(), i_dag())); + felter.push(( + "Beregnet med".into(), + format!("enok {}", env!("CARGO_PKG_VERSION")), + )); + s.push_str("#internkontroll((\n"); + for (navn, verdi) in &felter { + s.push_str(&format!( + " (\"{}\", \"{}\"),\n", + streng(navn), + streng(verdi) + )); + } + s.push_str("))\n\n#v(0.6cm)\n"); + // Nøkkeltall. let nøkkel = vec![ ("Bygningskategori".to_string(), e(&k.kategorinavn)), @@ -828,7 +857,7 @@ fn bunntekst(r: &Resultat) -> String { if let Some(e) = &b.energiraadgiver { deler.push(e.clone()); } - deler.push(format!("enok {} · {}", env!("CARGO_PKG_VERSION"), i_dag())); + // Versjon og dato står i internkontrollfeltet på forsiden, ikke på hver side. deler.join(" · ") } diff --git a/tests/rapport.rs b/tests/rapport.rs new file mode 100644 index 0000000..e66306e --- /dev/null +++ b/tests/rapport.rs @@ -0,0 +1,69 @@ +//! Kontroll av opplysningene rapporten skal bære: hvilket prosjekt, hvilken versjon og når. + +use enok::data::Datakilde; +use enok::{inndata, kjor, rapport}; +use std::path::{Path, PathBuf}; + +fn rapportkilde(versjon: Option<&str>, katalog: &str) -> String { + let mut b = inndata::les_bygning(Path::new("eksempler/referansebygg/smaahus.toml")).unwrap(); + b.rapport.versjon = versjon.map(str::to_string); + let r = kjor::kjor(&b, &Datakilde::default()).unwrap(); + let ut: PathBuf = std::env::temp_dir().join(katalog); + rapport::typst_ut::skriv(&r, &ut, &[]).unwrap(); + std::fs::read_to_string(ut.join("rapport.typ")).unwrap() +} + +fn oppsettlinjen(kilde: &str) -> String { + kilde + .lines() + .find(|l| l.starts_with("#show: oppsett.with(")) + .unwrap_or_default() + .to_string() +} + +/// Prosjektversjonen hører hjemme i internkontrollfeltet på forsiden. +#[test] +fn prosjektversjonen_staar_i_internkontrollfeltet() { + let kilde = rapportkilde(Some("v0.3.12"), "enok-rapport-med-versjon"); + assert!( + kilde.contains("#internkontroll(("), + "mangler internkontrollfeltet" + ); + assert!( + kilde.contains("(\"Prosjektversjon\", \"v0.3.12\")"), + "mangler prosjektversjonen" + ); + assert!(kilde.contains("(\"Prosjekt\","), "mangler prosjektnavnet"); + assert!(kilde.contains("(\"Dato\","), "mangler datoen"); + assert!( + kilde.contains("(\"Beregnet med\", \"enok "), + "mangler enok-versjonen" + ); +} + +/// Uten oppgitt versjon står feltet der, men uten en tom versjonslinje. +#[test] +fn uten_versjon_staar_ingen_versjonslinje() { + let kilde = rapportkilde(None, "enok-rapport-uten-versjon"); + assert!(kilde.contains("#internkontroll((")); + assert!( + !kilde.contains("Prosjektversjon"), + "skal ikke stå tom versjon" + ); +} + +/// Versjon og dato skal stå ett sted, på forsiden, ikke på hver eneste side. +#[test] +fn bunnteksten_gjentar_ikke_versjon_og_dato() { + let kilde = rapportkilde(Some("v0.3.12"), "enok-rapport-bunntekst"); + let linje = oppsettlinjen(&kilde); + assert!(!linje.is_empty(), "fant ikke oppsettlinjen"); + assert!( + !linje.contains("enok "), + "bunnteksten gjentar enok-versjonen: {linje}" + ); + assert!( + !linje.contains("v0.3.12"), + "bunnteksten gjentar prosjektversjonen" + ); +} diff --git a/typst/stil.typ b/typst/stil.typ index 20941e3..e835fe8 100644 --- a/typst/stil.typ +++ b/typst/stil.typ @@ -107,6 +107,32 @@ )[#tekst] } +// Internkontrollfelt på forsiden: hvilket prosjekt, hvilken versjon og når. +#let internkontroll(rader) = context { + block( + width: 100%, + inset: 9pt, + radius: 4pt, + fill: lys, + stroke: 0.4pt + kantlinje, + stack( + spacing: 6pt, + text( + font: overskriftsskrift(), + size: 7.5pt, + fill: aksent-valgt.get(), + tracking: 0.8pt, + )[INTERNKONTROLL], + ..rader.map(rad => grid( + columns: (3.8cm, 1fr), + gutter: 6pt, + text(size: 8.5pt, fill: mid)[#rad.at(0)], + text(size: 8.5pt)[#rad.at(1)], + )), + ), + ) +} + // Stor karakterrute på forsiden. #let karakterrute(bokstav, verdi, farge) = { box(