Ta vare på manuelle endringer ved ny import
«enok tillegg modell.ifc bygning.toml» skriver forskjellen mellom modellen og en håndredigert bygningsbeskrivelse som en tilleggsfil: endrede verdier, nye oppføringer, «slett = true» for det bare modellen har, og scenarioene. Kommandoen kontrollerer at tillegget gjenskaper filen det er laget av, og sier fra om noe ikke lar seg uttrykke, siden fletting ikke kan fjerne et enkeltfelt. «enok import» nekter å skrive over en fil enok ikke har laget selv, og viser til kommandoen over. --overskriv går forbi. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GeH3y8fwTaTudaD88P57o
This commit is contained in:
+102
@@ -31,6 +31,8 @@ enum Kommando {
|
||||
Beregn(Beregn),
|
||||
/// Importerer en bygningsmodell fra gbXML, IFC eller regneark og skriver bygning.toml.
|
||||
Import(Import),
|
||||
/// Lager en tilleggsfil av forskjellen mellom en modell og en ferdig bygningsbeskrivelse.
|
||||
Tillegg(TilleggAv),
|
||||
/// Viser tilgjengelige klimasteder og nøkkeltall for ett sted.
|
||||
Klima(Klima),
|
||||
/// Viser energikarakterskalaen for en bygningskategori.
|
||||
@@ -72,6 +74,20 @@ struct Import {
|
||||
/// Utdatafil. Standard: samme navn med endelsen .toml.
|
||||
#[arg(short, long, value_name = "FIL")]
|
||||
ut: Option<PathBuf>,
|
||||
/// Skriver over en utdatafil enok ikke har laget selv.
|
||||
#[arg(long)]
|
||||
overskriv: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct TilleggAv {
|
||||
/// BIM-modellen: .ifc, .gbxml eller .csv.
|
||||
modell: PathBuf,
|
||||
/// Bygningsbeskrivelsen med de manuelle endringene, som tillegget skal gjenskape.
|
||||
bygning: PathBuf,
|
||||
/// Utdatafil. Standard: prosjekt.toml ved siden av bygningsbeskrivelsen.
|
||||
#[arg(short, long, value_name = "FIL")]
|
||||
ut: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
@@ -100,6 +116,7 @@ fn main() -> Result<()> {
|
||||
match cli.kommando {
|
||||
Kommando::Beregn(a) => beregn(a, &kilde),
|
||||
Kommando::Import(a) => import(a),
|
||||
Kommando::Tillegg(a) => tillegg(a),
|
||||
Kommando::Klima(a) => klima(a),
|
||||
Kommando::Skala(a) => skala(a, &kilde),
|
||||
}
|
||||
@@ -280,9 +297,26 @@ fn skriv_sammendrag(r: &kjor::Resultat, scenarier: &[(String, kjor::Resultat)])
|
||||
println!();
|
||||
}
|
||||
|
||||
const GENERERT: &str = "# Generert av enok";
|
||||
|
||||
fn import(a: Import) -> Result<()> {
|
||||
let (bygning, merknader, _) = les_modell(&a.fil, a.tillegg.as_deref())?;
|
||||
let ut = a.ut.unwrap_or_else(|| a.fil.with_extension("toml"));
|
||||
// En fil som ikke er skrevet av enok, kan være redigert for hånd. Da er det bedre å
|
||||
// stoppe enn å skrive over arbeidet.
|
||||
if !a.overskriv && ut.exists() {
|
||||
let gammel = std::fs::read_to_string(&ut).unwrap_or_default();
|
||||
if !gammel.starts_with(GENERERT) {
|
||||
bail!(
|
||||
"{} er ikke laget av enok og kan være redigert for hånd. \n\
|
||||
Ta vare på endringene som en tilleggsfil med «enok tillegg {} {} --ut prosjekt.toml», \n\
|
||||
og importer med «--tillegg prosjekt.toml». Bruk --overskriv for å skrive over.",
|
||||
ut.display(),
|
||||
a.fil.display(),
|
||||
ut.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
let innhold = skriv_bygning(&bygning, &merknader, &a.fil, a.tillegg.as_deref())?;
|
||||
std::fs::write(&ut, innhold)?;
|
||||
println!("Skrev {}", ut.display());
|
||||
@@ -322,6 +356,74 @@ fn skriv_bygning(
|
||||
Ok(innhold)
|
||||
}
|
||||
|
||||
/// Skriver forskjellen mellom modellen og en ferdig bygningsbeskrivelse som en tilleggsfil.
|
||||
fn tillegg(a: TilleggAv) -> Result<()> {
|
||||
let (importert, _, modell) = les_modell(&a.modell, None)?;
|
||||
if !modell {
|
||||
bail!("{} er ikke en BIM-modell", a.modell.display());
|
||||
}
|
||||
let manuell = inndata::les_bygning(&a.bygning)?;
|
||||
let fra = toml::Value::try_from(&importert).context("serialiserer den importerte bygningen")?;
|
||||
let til = toml::Value::try_from(&manuell).context("serialiserer bygningsbeskrivelsen")?;
|
||||
let (patch, detaljer) = inndata::differanse(&fra, &til);
|
||||
|
||||
// Felter modellen har og filen ikke, kan et tillegg ikke fjerne. De er som regel
|
||||
// standardverdier importen fyller ut, så de samles i én merknad i stedet for én hver.
|
||||
let (ufjernbare, mut merknader): (Vec<String>, Vec<String>) = detaljer
|
||||
.into_iter()
|
||||
.partition(|m| m.contains("finnes i modellen"));
|
||||
if !ufjernbare.is_empty() {
|
||||
let felter: Vec<&str> = ufjernbare
|
||||
.iter()
|
||||
.filter_map(|m| m.split('«').nth(1)?.split('»').next())
|
||||
.collect();
|
||||
merknader.push(format!(
|
||||
"{} felter finnes i modellen, men ikke i bygningsbeskrivelsen, og blir stående: {}{}. En tilleggsfil kan ikke fjerne et felt.",
|
||||
felter.len(),
|
||||
felter.iter().take(3).copied().collect::<Vec<_>>().join(", "),
|
||||
if felter.len() > 3 { " …" } else { "" }
|
||||
));
|
||||
}
|
||||
|
||||
// Kontroller at tillegget faktisk gjenskaper filen det er laget av.
|
||||
let (gjenskapt, bom) = inndata::flett_tillegg(&importert, &patch)?;
|
||||
merknader.extend(
|
||||
bom.into_iter()
|
||||
.map(|m| format!("Navnemønsteret «{m}» traff ingenting.")),
|
||||
);
|
||||
if toml::Value::try_from(&gjenskapt).ok().as_ref() != Some(&til) {
|
||||
merknader.push(
|
||||
"Tillegget gjenskaper ikke bygningsbeskrivelsen helt. Gå gjennom filen før den brukes."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
let ut =
|
||||
a.ut.unwrap_or_else(|| a.bygning.with_file_name("prosjekt.toml"));
|
||||
let mut innhold = format!(
|
||||
"# Tillegg til «{}», laget av forskjellen mot «{}».\n\
|
||||
# Alt som ikke står i modellen, ligger her, og flettes inn ved hver import:\n\
|
||||
# enok beregn \"{}\" --tillegg {}\n",
|
||||
a.modell.display(),
|
||||
a.bygning.display(),
|
||||
a.modell.display(),
|
||||
ut.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("prosjekt.toml")
|
||||
);
|
||||
for m in &merknader {
|
||||
innhold.push_str(&format!("# {m}\n"));
|
||||
}
|
||||
innhold.push('\n');
|
||||
innhold.push_str(&toml::to_string_pretty(&patch).context("serialiserer tillegget")?);
|
||||
std::fs::write(&ut, innhold)?;
|
||||
println!("Skrev {}", ut.display());
|
||||
for m in &merknader {
|
||||
println!(" • {m}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn klima(a: Klima) -> Result<()> {
|
||||
if a.liste {
|
||||
for k in enok::klima::tmy::kommuner()? {
|
||||
|
||||
Reference in New Issue
Block a user