///|
fn profile_increment(map : Map[String, Int], key : String) -> Unit {
map.update_or_default(key, 1, count => count + 1)
}
///|
fn profile_entity_family(entity : Entity) -> String {
entity_family(entity)
}
///|
/// Build a reusable semantic profile without decoding every geometry mesh.
pub fn model_profile(model : Model) -> ModelProfile {
let index = model.index()
let schema = schema_version(model)
let profile = schema_profile(schema)
let family_counts : Map[String, Int] = Map([])
let type_counts : Map[String, Int] = Map([])
let mut known = 0
let mut unknown = 0
let mut relationships = 0
let mut geometry_count = 0
for entity in model.entities {
let family = profile_entity_family(entity)
profile_increment(family_counts, family)
profile_increment(type_counts, entity.name.to_upper())
if schema_entity_is_known(profile, entity.name) {
known += 1
} else {
unknown += 1
}
if is_relationship(entity) {
relationships += 1
}
if is_geometry_entity(entity) {
geometry_count += 1
}
}
{
schema,
entity_count: model.entities.length(),
product_count: model.products().length(),
family_counts,
type_counts,
known_entity_count: known,
unknown_entity_count: unknown,
relationship_count: relationships,
geometry_count,
property_set_count: index.ids_named("IFCPROPERTYSET").length(),
}
}
///|
pub fn ModelProfile::family_count(self : ModelProfile, family : String) -> Int {
self.family_counts.get(family).unwrap_or(0)
}
///|
pub fn ModelProfile::type_count(self : ModelProfile, name : String) -> Int {
self.type_counts.get(name.to_upper()).unwrap_or(0)
}
///|
pub fn ModelProfile::known_ratio(self : ModelProfile) -> Double {
if self.entity_count == 0 {
1.0
} else {
self.known_entity_count.to_double() / self.entity_count.to_double()
}
}
///|
pub fn ModelProfile::is_extension_heavy(self : ModelProfile) -> Bool {
self.unknown_entity_count > self.known_entity_count / 2
}
///|
pub fn ModelProfile::family_names(self : ModelProfile) -> Array[String] {
let result : Array[String] = []
for family, _ in self.family_counts {
result.push(family)
}
result
}
///|
pub fn ModelProfile::top_types(
self : ModelProfile,
limit : Int,
) -> Array[(String, Int)] {
let rows : Array[(String, Int)] = []
for name, count in self.type_counts {
rows.push((name, count))
}
let result = rows.copy()
let maximum = if limit <= 0 || limit > result.length() {
result.length()
} else {
limit
}
for i in 0.. result[selected].1 ||
(result[j].1 == result[selected].1 && result[j].0 < result[selected].0) {
selected = j
}
}
if selected != i {
let item = result[i]
result[i] = result[selected]
result[selected] = item
}
}
if maximum < result.length() {
result[:maximum].to_owned()
} else {
result
}
}
///|
pub fn ModelProfile::headline(self : ModelProfile) -> String {
schema_profile_summary(schema_profile(self.schema)) +
" entities=" +
self.entity_count.to_string() +
" products=" +
self.product_count.to_string() +
" known=" +
self.known_entity_count.to_string() +
" unknown=" +
self.unknown_entity_count.to_string() +
" geometry=" +
self.geometry_count.to_string()
}
///|
pub fn ModelProfile::to_json(self : ModelProfile) -> String {
let types = self
.top_types(20)
.map(row => {
"{\"name\":\"" + row.0 + "\",\"count\":" + row.1.to_string() + "}"
})
.join(",")
"{\"schema\":\"" +
schema_profile_summary(schema_profile(self.schema)) +
"\",\"entity_count\":" +
self.entity_count.to_string() +
",\"product_count\":" +
self.product_count.to_string() +
",\"known_entity_count\":" +
self.known_entity_count.to_string() +
",\"unknown_entity_count\":" +
self.unknown_entity_count.to_string() +
",\"relationship_count\":" +
self.relationship_count.to_string() +
",\"geometry_count\":" +
self.geometry_count.to_string() +
",\"property_set_count\":" +
self.property_set_count.to_string() +
",\"top_types\":[" +
types +
"]}"
}
///|
pub fn ModelProfile::to_markdown(self : ModelProfile) -> String {
let builder = StringBuilder()
builder <+ "# Model profile\n\n"
builder <+
"- Schema: \{schema_profile_summary(schema_profile(self.schema))}\n"
builder <+ "- Entities: \{self.entity_count}\n"
builder <+ "- Products: \{self.product_count}\n"
builder <+ "- Known entities: \{self.known_entity_count}\n"
builder <+ "- Unknown entities: \{self.unknown_entity_count}\n"
builder <+ "- Relationships: \{self.relationship_count}\n"
builder <+ "- Geometry entities: \{self.geometry_count}\n"
builder <+ "- Property sets: \{self.property_set_count}\n\n"
builder <+ "| Type | Count |\n| --- | ---: |\n"
for row in self.top_types(20) {
builder <+ "| \{row.0} | \{row.1} |\n"
}
builder.to_string()
}
///|
pub fn model_profile_type_histogram(model : Model) -> Map[String, Int] {
model_profile(model).type_counts
}
///|
pub fn model_profile_family_histogram(model : Model) -> Map[String, Int] {
model_profile(model).family_counts
}
///|
pub fn model_profile_summary(model : Model) -> Array[String] {
let profile = model_profile(model)
[
profile.headline(),
"known_ratio=" + profile.known_ratio().to_string(),
"extension_heavy=" + profile.is_extension_heavy().to_string(),
]
}