///|
pub enum FieldRequirement {
Required
Optional
} derive(Debug, Eq)
///|
pub fn required() -> FieldRequirement {
Required
}
///|
pub fn optional() -> FieldRequirement {
Optional
}
///|
pub struct TemplateField {
name : String
requirement : FieldRequirement
} derive(Debug, Eq)
///|
pub fn template_field(
name : String,
requirement : FieldRequirement,
) -> TemplateField {
{ name, requirement }
}
///|
pub struct TemplateValue {
name : String
value : String
} derive(Debug, Eq)
///|
pub fn template_value(name : String, value : String) -> TemplateValue {
{ name, value }
}
///|
pub struct ReportTemplate {
name : String
fields : Array[TemplateField]
} derive(Debug, Eq)
///|
pub fn report_template(
name : String,
fields : Array[TemplateField],
) -> ReportTemplate {
{ name, fields }
}
///|
fn template_find(values : Array[TemplateValue], name : String) -> String? {
for value in values {
if value.name == name {
return Some(value.value)
}
}
None
}
///|
pub fn ReportTemplate::field_count(self : ReportTemplate) -> Int {
self.fields.length()
}
///|
pub fn ReportTemplate::required_count(self : ReportTemplate) -> Int {
let mut count = 0
for field in self.fields {
if field.requirement is Required {
count = count + 1
}
}
count
}
///|
pub fn ReportTemplate::validate(
self : ReportTemplate,
values : Array[TemplateValue],
) -> Array[String] {
let errors : Array[String] = []
for field in self.fields {
match template_find(values, field.name) {
Some(value) =>
if field.requirement is Required && value.trim().length() == 0 {
errors.push(field.name + " must not be empty")
}
None =>
if field.requirement is Required {
errors.push(field.name + " is required")
}
}
}
errors
}
///|
pub fn ReportTemplate::render(
self : ReportTemplate,
values : Array[TemplateValue],
) -> String {
let lines : Array[String] = ["# " + self.name, ""]
for field in self.fields {
let value = template_find(values, field.name).unwrap_or("")
lines.push("- **" + field.name + "**: " + value)
}
lines.join("\n")
}
///|
pub fn ReportTemplate::to_report(
self : ReportTemplate,
values : Array[TemplateValue],
metadata : ReportMetadata,
) -> ChemReport {
let assumptions : Array[Assumption] = []
let inputs : Array[InputValue] = []
for field in self.fields {
let value = template_find(values, field.name).unwrap_or("")
inputs.push({ name: field.name, value, unit: None, note: None })
}
report(
metadata,
self.render(values),
assumptions,
inputs,
[],
[],
[],
[],
[{ title: self.name, kind: Notes, body: self.render(values) }],
["template", self.name],
)
}
///|
pub fn ReportTemplate::missing_fields(
self : ReportTemplate,
values : Array[TemplateValue],
) -> Array[String] {
self.validate(values)
}
///|
pub fn ReportTemplate::has_field(self : ReportTemplate, name : String) -> Bool {
for field in self.fields {
if field.name == name {
return true
}
}
false
}
///|
pub fn ReportTemplate::field_names(self : ReportTemplate) -> Array[String] {
self.fields.map(fn(field) { field.name })
}
///|
pub fn ReportTemplate::add_field(
self : ReportTemplate,
field : TemplateField,
) -> ReportTemplate {
let fields = self.fields.copy()
fields.push(field)
{ ..self, fields, }
}
///|
pub fn ReportTemplate::with_name(
self : ReportTemplate,
name : String,
) -> ReportTemplate {
{ ..self, name, }
}
///|
pub struct TemplateCatalog {
templates : Array[ReportTemplate]
} derive(Debug, Eq)
///|
pub fn template_catalog(templates : Array[ReportTemplate]) -> TemplateCatalog {
{ templates, }
}
///|
pub fn TemplateCatalog::count(self : TemplateCatalog) -> Int {
self.templates.length()
}
///|
pub fn TemplateCatalog::find(
self : TemplateCatalog,
name : String,
) -> ReportTemplate? {
for template in self.templates {
if template.name == name {
return Some(template)
}
}
None
}
///|
pub fn TemplateCatalog::names(self : TemplateCatalog) -> Array[String] {
self.templates.map(fn(template) { template.name })
}
///|
pub fn TemplateCatalog::add(
self : TemplateCatalog,
template : ReportTemplate,
) -> TemplateCatalog {
let templates = self.templates.copy()
templates.push(template)
{ templates, }
}
///|
pub fn TemplateCatalog::render_index(self : TemplateCatalog) -> String {
let lines : Array[String] = ["# Report templates", ""]
for template in self.templates {
lines.push(
"- " +
template.name +
" (" +
template.field_count().to_string() +
" fields)",
)
}
lines.join("\n")
}
///|
pub fn standard_operations_template() -> ReportTemplate {
report_template("operations", [
template_field("owner", Required),
template_field("shift", Optional),
template_field("process_unit", Required),
template_field("scenario", Required),
template_field("summary", Required),
])
}
///|
pub fn standard_safety_template() -> ReportTemplate {
report_template("safety-review", [
template_field("owner", Required),
template_field("hazard", Required),
template_field("safeguard", Required),
template_field("status", Required),
template_field("next_review", Optional),
])
}
///|
pub fn standard_balance_template() -> ReportTemplate {
report_template("balance-review", [
template_field("owner", Required),
template_field("basis", Required),
template_field("inlet", Required),
template_field("outlet", Required),
template_field("residual", Required),
])
}
///|
pub fn standard_template_catalog() -> TemplateCatalog {
template_catalog([
standard_operations_template(),
standard_safety_template(),
standard_balance_template(),
])
}
///|
pub fn template_values_from_inputs(
inputs : Array[InputValue],
) -> Array[TemplateValue] {
inputs.map(fn(item) { template_value(item.name, item.value) })
}
///|
pub fn template_completeness(
template : ReportTemplate,
values : Array[TemplateValue],
) -> Float {
let total = template.field_count()
if total == 0 {
1.0
} else {
Float::from_int(total - template.validate(values).length()) /
Float::from_int(total)
}
}
///|
pub fn template_quality_gate(
template : ReportTemplate,
values : Array[TemplateValue],
) -> Bool {
template.validate(values).length() == 0
}