use crate::assembly::AssemblyReference;
use crate::life_cycle_base::{ImpactCategoryKey, Impacts, LifeCycleModule};
use crate::shared::{MetaData, Unit};
use lcax_core::country::Country;
use lcax_core::utils::get_version;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg(feature = "jsbindings")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "jsbindings")]
use tsify_next::Tsify;
#[cfg(feature = "pybindings")]
use pyo3::exceptions::PyTypeError;
#[cfg(feature = "pybindings")]
use pyo3::prelude::*;
#[cfg(feature = "pybindings")]
use pyo3::types::PyType;
#[cfg(feature = "jsbindings")]
use strum::IntoEnumIterator;
#[cfg(feature = "jsbindings")]
use strum_macros::EnumIter;
#[derive(Deserialize, Serialize, JsonSchema, Default, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(
feature = "jsbindings",
derive(Tsify),
tsify(into_wasm_abi, from_wasm_abi)
)]
#[cfg_attr(feature = "pybindings", pyclass(get_all, set_all))]
pub struct Project {
pub id: String,
pub name: String,
pub description: Option<String>,
pub comment: Option<String>,
pub location: Location,
pub owner: Option<String>,
pub format_version: String,
pub lcia_method: Option<String>,
pub classification_systems: Option<Vec<String>>,
pub reference_study_period: Option<u8>,
pub life_cycle_modules: Vec<LifeCycleModule>,
pub impact_categories: Vec<ImpactCategoryKey>,
pub assemblies: Vec<AssemblyReference>,
pub results: Option<Impacts>,
pub project_info: Option<BuildingInfo>,
pub project_phase: ProjectPhase,
pub software_info: SoftwareInfo,
pub meta_data: Option<MetaData>,
}
#[cfg_attr(feature = "pybindings", pymethods)]
impl Project {
#[cfg(feature = "pybindings")]
#[new]
#[pyo3(signature=(name, location, project_phase, software_info, life_cycle_modules, impact_categories, assemblies, id=None, description=None, comment=None, owner=None, format_version=None, lcia_method=None, classification_systems=None, reference_study_period=None, results=None, project_info=None, meta_data=None ))]
pub fn new_py(
name: &str,
location: Location,
project_phase: ProjectPhase,
software_info: SoftwareInfo,
life_cycle_modules: Vec<LifeCycleModule>,
impact_categories: Vec<ImpactCategoryKey>,
assemblies: Vec<AssemblyReference>,
id: Option<String>,
description: Option<String>,
comment: Option<String>,
owner: Option<String>,
format_version: Option<String>,
lcia_method: Option<String>,
classification_systems: Option<Vec<String>>,
reference_study_period: Option<u8>,
results: Option<Impacts>,
project_info: Option<BuildingInfo>,
meta_data: Option<MetaData>,
) -> Project {
Project::new(
id,
name,
description,
comment,
location,
owner,
format_version,
lcia_method,
classification_systems,
reference_study_period,
life_cycle_modules,
impact_categories,
assemblies,
results,
project_info,
project_phase,
software_info,
meta_data,
)
}
#[cfg(feature = "pybindings")]
#[classmethod]
#[pyo3(name = "loads")]
pub fn loads_py(_cls: &Bound<'_, PyType>, value: &str) -> PyResult<Self> {
match Project::loads(value) {
Ok(project) => Ok(project),
Err(error) => Err(PyTypeError::new_err(error.to_string())),
}
}
#[cfg(feature = "pybindings")]
#[pyo3(name = "dumps")]
pub fn dumps_py(&self) -> PyResult<String> {
match Project::dumps(self) {
Ok(project) => Ok(project),
Err(error) => Err(PyTypeError::new_err(error.to_string())),
}
}
#[cfg(feature = "pybindings")]
fn __repr__(&self) -> String {
format!("Project: {}", self.id)
}
#[cfg(feature = "pybindings")]
fn __str__(&self) -> String {
self.__repr__()
}
}
impl Project {
pub fn new(
id: Option<String>,
name: &str,
description: Option<String>,
comment: Option<String>,
location: Location,
owner: Option<String>,
format_version: Option<String>,
lcia_method: Option<String>,
classification_systems: Option<Vec<String>>,
reference_study_period: Option<u8>,
life_cycle_modules: Vec<LifeCycleModule>,
impact_categories: Vec<ImpactCategoryKey>,
assemblies: Vec<AssemblyReference>,
results: Option<Impacts>,
project_info: Option<BuildingInfo>,
project_phase: ProjectPhase,
software_info: SoftwareInfo,
meta_data: Option<MetaData>,
) -> Self {
let _id = id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
let _format_version = format_version.unwrap_or_else(|| get_version().to_string());
Project {
id: _id,
name: name.to_string(),
description,
comment,
location,
owner,
format_version: _format_version,
lcia_method,
classification_systems,
reference_study_period,
life_cycle_modules,
impact_categories,
assemblies,
results,
project_info,
project_phase,
software_info,
meta_data,
}
}
pub fn loads(value: &str) -> Result<Project, serde_json::Error> {
serde_json::from_str(value)
}
pub fn dumps(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(self)
}
pub fn resolve_impact_categories(&mut self) {
self.impact_categories = match self.results {
Some(ref results) => {
let mut keys = Vec::new();
for (key, _) in results.iter() {
keys.push(key.clone());
}
keys
}
None => vec![],
}
}
pub fn resolve_life_cycle_stages(&mut self) {
self.life_cycle_modules = match self.results {
Some(ref results) => {
let mut keys = Vec::new();
for (_, value) in results.iter() {
for (key, _) in value.iter() {
if keys.contains(key) {
continue;
}
keys.push(key.clone());
}
}
keys
}
None => vec![],
}
}
}
#[derive(Deserialize, Serialize, JsonSchema, Default, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "jsbindings", derive(Tsify))]
#[cfg_attr(feature = "pybindings", pyclass(get_all, set_all))]
pub struct SoftwareInfo {
pub lca_software: String,
pub lca_software_version: Option<String>,
pub goal_and_scope_definition: Option<String>,
pub calculation_type: Option<String>,
}
#[cfg_attr(feature = "pybindings", pymethods)]
impl SoftwareInfo {
#[cfg(feature = "pybindings")]
#[new]
#[pyo3(signature=(lca_software, lca_software_version=None, goal_and_scope_definition=None, calculation_type=None))]
pub fn new_py(
lca_software: String,
lca_software_version: Option<String>,
goal_and_scope_definition: Option<String>,
calculation_type: Option<String>,
) -> SoftwareInfo {
SoftwareInfo::new(
lca_software,
lca_software_version,
goal_and_scope_definition,
calculation_type,
)
}
}
impl SoftwareInfo {
pub fn new(
lca_software: String,
lca_software_version: Option<String>,
goal_and_scope_definition: Option<String>,
calculation_type: Option<String>,
) -> Self {
SoftwareInfo {
lca_software,
lca_software_version,
goal_and_scope_definition,
calculation_type,
}
}
}
#[derive(Deserialize, Serialize, JsonSchema, Default, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
#[cfg_attr(
feature = "jsbindings",
derive(Tsify, EnumIter),
tsify(into_wasm_abi, from_wasm_abi)
)]
#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
pub enum ProjectPhase {
#[allow(non_camel_case_types)]
STRATEGIC_DESIGN,
#[allow(non_camel_case_types)]
CONCEPT_DESIGN,
#[allow(non_camel_case_types)]
TECHNICAL_DESIGN,
CONSTRUCTION,
#[allow(non_camel_case_types)]
POST_COMPLETION,
#[allow(non_camel_case_types)]
IN_USE,
#[default]
OTHER,
}
#[cfg(feature = "jsbindings")]
#[allow(non_snake_case)]
#[wasm_bindgen]
pub fn projectPhases() -> Vec<ProjectPhase> {
ProjectPhase::iter().collect()
}
#[derive(Deserialize, Serialize, JsonSchema, Default, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "jsbindings", derive(Tsify))]
#[cfg_attr(feature = "pybindings", pyclass(get_all, set_all))]
pub struct Location {
pub country: Country,
pub city: Option<String>,
pub address: Option<String>,
}
#[cfg_attr(feature = "pybindings", pymethods)]
impl Location {
#[cfg(feature = "pybindings")]
#[new]
#[pyo3(signature=(country, city=None, address=None))]
pub fn new_py(country: Country, city: Option<String>, address: Option<String>) -> Location {
Location::new(country, city, address)
}
}
impl Location {
pub fn new(country: Country, city: Option<String>, address: Option<String>) -> Self {
Location {
country,
city,
address,
}
}
}
#[derive(Deserialize, Serialize, JsonSchema, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "jsbindings", derive(Tsify))]
#[cfg_attr(feature = "pybindings", pyclass(get_all, set_all))]
pub struct BuildingInfo {
pub building_type: BuildingType,
pub building_typology: Vec<BuildingTypology>,
pub certifications: Option<Vec<String>>,
pub building_mass: Option<ValueUnit>,
pub building_height: Option<ValueUnit>,
pub gross_floor_area: Option<AreaType>,
pub heated_floor_area: Option<AreaType>,
pub building_footprint: Option<ValueUnit>,
pub floors_above_ground: u16,
pub floors_below_ground: Option<u16>,
pub roof_type: Option<RoofType>,
pub frame_type: Option<String>,
pub building_completion_year: Option<u16>,
pub building_permit_year: Option<u16>,
pub energy_demand_heating: Option<f64>,
pub energy_supply_heating: Option<f64>,
pub energy_demand_electricity: Option<f64>,
pub energy_supply_electricity: Option<f64>,
pub exported_electricity: Option<f64>,
pub general_energy_class: GeneralEnergyClass,
pub local_energy_class: Option<String>,
pub building_users: Option<u32>,
pub building_model_scope: Option<Vec<BuildingModelScope>>,
}
#[derive(Deserialize, Serialize, JsonSchema, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "jsbindings", derive(Tsify))]
#[cfg_attr(feature = "pybindings", pyclass(get_all, set_all))]
pub struct AreaType {
pub value: f64,
pub unit: Unit,
pub definition: String,
}
#[derive(Deserialize, Serialize, JsonSchema, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "jsbindings", derive(Tsify))]
#[cfg_attr(feature = "pybindings", pyclass(get_all, set_all))]
pub struct ValueUnit {
pub value: f64,
pub unit: Unit,
}
#[derive(Deserialize, Serialize, JsonSchema, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
#[cfg_attr(
feature = "jsbindings",
derive(Tsify, EnumIter),
tsify(into_wasm_abi, from_wasm_abi)
)]
#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
pub enum RoofType {
FLAT,
PITCHED,
SADDLE,
PYRAMID,
UNKNOWN,
OTHER,
}
#[cfg(feature = "jsbindings")]
#[allow(non_snake_case)]
#[wasm_bindgen]
pub fn roofTypes() -> Vec<RoofType> {
RoofType::iter().collect()
}
#[derive(Deserialize, Serialize, JsonSchema, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
#[cfg_attr(
feature = "jsbindings",
derive(Tsify, EnumIter),
tsify(into_wasm_abi, from_wasm_abi)
)]
#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
pub enum GeneralEnergyClass {
EXISTING,
STANDARD,
ADVANCED,
UNKNOWN,
}
#[cfg(feature = "jsbindings")]
#[allow(non_snake_case)]
#[wasm_bindgen]
pub fn generalEnergyClasses() -> Vec<GeneralEnergyClass> {
GeneralEnergyClass::iter().collect()
}
impl From<&String> for GeneralEnergyClass {
fn from(class: &String) -> Self {
match class.to_ascii_lowercase().as_str() {
"lowenergy" => GeneralEnergyClass::ADVANCED,
_ => GeneralEnergyClass::UNKNOWN,
}
}
}
#[derive(Deserialize, Serialize, JsonSchema, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
#[cfg_attr(
feature = "jsbindings",
derive(Tsify, EnumIter),
tsify(into_wasm_abi, from_wasm_abi)
)]
#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
pub enum BuildingModelScope {
#[allow(non_camel_case_types)]
FACILITATING_WORKS,
SUBSTRUCTURE,
#[allow(non_camel_case_types)]
SUPERSTRUCTURE_FRAME,
#[allow(non_camel_case_types)]
SUPERSTRUCTURE_ENVELOPE,
#[allow(non_camel_case_types)]
SUPERSTRUCTURE_INTERNAL_ELEMENTS,
FINISHES,
#[allow(non_camel_case_types)]
BUILDING_SERVICES,
#[allow(non_camel_case_types)]
EXTERNAL_WORKS,
#[allow(non_camel_case_types)]
FF_E,
}
#[cfg(feature = "jsbindings")]
#[allow(non_snake_case)]
#[wasm_bindgen]
pub fn buildingModelScopes() -> Vec<BuildingModelScope> {
BuildingModelScope::iter().collect()
}
#[derive(Deserialize, Serialize, JsonSchema, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
#[cfg_attr(
feature = "jsbindings",
derive(Tsify, EnumIter),
tsify(into_wasm_abi, from_wasm_abi)
)]
#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
pub enum BuildingType {
#[allow(non_camel_case_types)]
NEW_CONSTRUCTION_WORKS,
DEMOLITION,
#[allow(non_camel_case_types)]
DECONSTRUCTION_AND_NEW_CONSTRUCTION_WORKS,
#[allow(non_camel_case_types)]
RETROFIT_WORKS,
#[allow(non_camel_case_types)]
EXTENSION_WORKS,
#[allow(non_camel_case_types)]
RETROFIT_AND_EXTENSION_WORKS,
#[allow(non_camel_case_types)]
FIT_OUT_WORKS,
OPERATIONS,
UNKNOWN,
OTHER,
}
#[cfg(feature = "jsbindings")]
#[allow(non_snake_case)]
#[wasm_bindgen]
pub fn buildingTypes() -> Vec<BuildingType> {
BuildingType::iter().collect()
}
#[derive(Deserialize, Serialize, JsonSchema, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
#[cfg_attr(
feature = "jsbindings",
derive(Tsify, EnumIter),
tsify(into_wasm_abi, from_wasm_abi)
)]
#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
pub enum BuildingTypology {
OFFICE,
RESIDENTIAL,
PUBLIC,
COMMERCIAL,
INDUSTRIAL,
INFRASTRUCTURE,
AGRICULTURAL,
EDUCATIONAL,
HEALTH,
UNKNOWN,
OTHER,
}
#[cfg(feature = "jsbindings")]
#[allow(non_snake_case)]
#[wasm_bindgen]
pub fn buildingTypologies() -> Vec<BuildingTypology> {
BuildingTypology::iter().collect()
}
impl From<&String> for BuildingTypology {
fn from(unit: &String) -> Self {
match unit.to_ascii_lowercase().as_str() {
"office" => BuildingTypology::OFFICE,
"residential" => BuildingTypology::RESIDENTIAL,
"public" => BuildingTypology::PUBLIC,
"commercial" => BuildingTypology::COMMERCIAL,
"industrial" => BuildingTypology::INDUSTRIAL,
"infrastructure" => BuildingTypology::INFRASTRUCTURE,
"agricultural" => BuildingTypology::AGRICULTURAL,
"educational" => BuildingTypology::EDUCATIONAL,
"health" => BuildingTypology::HEALTH,
_ => BuildingTypology::OTHER,
}
}
}