Class: Labimotion::OntologyTerms

Inherits:
Object
  • Object
show all
Defined in:
lib/labimotion/libs/ontology_terms.rb

Overview

Resolve the selectable vocabulary behind an "ontology-select" field.

A template field pins ONE ontology root (its ontology_term); the picker in chem-generic-ui then offers that root and everything below it. The AI data-fill needs the same list — otherwise the model invents a plausible-sounding term that is not in the ontology and the field renders a value the user could never have chosen themselves.

The whole subtree arrives in ONE request via ?hierarchicalAncestor=, rather than the dozens of round trips a /children walk costs; the result set is the same terms, de-duplicated (a term with two parents appears twice in a walk).

What comes back is handed to Labimotion::OntologyStore, which is what actually keeps it — so the picker and the auto-fill read one list instead of resolving the same subtree separately.

Every failure path returns [] — no vocabulary simply means the field falls back to the free-text behaviour it had before, never a failed fill.

Constant Summary collapse

DEFAULT_BASE_URL =
'https://api.terminology.tib.eu/api/v2'
PAGE_SIZE =

One page, sized past the largest root anyone points a field at (the biggest CHMO branch is ~650 terms).

1000
REQUEST_TIMEOUT =
20
MAX_TERMS_PER_FIELD =

Per-field and per-request budgets. A field aimed at a very general root would otherwise push tens of thousands of labels into the prompt and crowd out the document the model is supposed to be reading. Both limits SKIP a field rather than truncate its list: a partial vocabulary is the worst of both worlds — the missing terms become unproposable AND a correct answer naming one gets thrown away by validation. Sized well past any real root (the largest CHMO branch is ~650 terms).

1500
MAX_TERMS_TOTAL =
3000

Class Method Summary collapse

Class Method Details

.base_urlObject

Public because the store records which service a file came from, so a subtree stored against one host can be told apart from another's.



83
84
85
86
87
88
89
90
# File 'lib/labimotion/libs/ontology_terms.rb', line 83

def base_url
  cfg = (Rails.configuration.labimotion_ai if defined?(Rails) && Rails.respond_to?(:configuration))
  (cfg.is_a?(Hash) && cfg[:ontology_base_url].presence) ||
    ENV['LABIMOTION_ONTOLOGY_BASE_URL'].presence ||
    DEFAULT_BASE_URL
rescue StandardError
  DEFAULT_BASE_URL
end

.labels_for(ontology_term) ⇒ Array<String>

Selectable labels for one field's ontology_term, root included, sorted and de-duplicated. Accepts the shapes the designer writes: a { 'label', 'value' } hash, a bare string, or an ARRAY of either (a field may pin several roots).

Returns:

  • (Array<String>)

    [] when nothing is configured, the subtree is beyond MAX_TERMS_PER_FIELD, or the service fails



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/labimotion/libs/ontology_terms.rb', line 48

def labels_for(ontology_term)
  roots = parse(ontology_term)
  return [] if roots.empty?

  labels = roots.flat_map { |root| Labimotion::OntologyStore.labels(root) }
                .uniq(&:downcase).sort_by(&:downcase)
  return labels if labels.size <= MAX_TERMS_PER_FIELD

  log("subtree of #{roots.map { |r| r[:iri] }.join(', ')} has #{labels.size} terms " \
      "(over #{MAX_TERMS_PER_FIELD}); leaving the field unconstrained")
  []
end

.parse(ontology_term) ⇒ Object

Split a configured ontology_term into [{ ontology:, iri: }, ...]. Mirrors parseOntologyTerm in chem-generic-ui/src/utils/ontologyApi.js, which is what actually wrote these strings.



77
78
79
# File 'lib/labimotion/libs/ontology_terms.rb', line 77

def parse(ontology_term)
  wrap_terms(ontology_term).filter_map { |term| parse_one(term) }
end

.subtree(root) ⇒ Object

Every node of one root's subtree, the root included, as the store keeps them: { 'iri', 'label', 'description', 'parent' }. The root is selectable in the picker, so it has to be in the list the model is offered too.

Two requests: the root is not in its own descendant query.



66
67
68
69
70
71
72
# File 'lib/labimotion/libs/ontology_terms.rb', line 66

def subtree(root)
  node = root_node(root)
  return [] if node.nil?

  nodes = ([node] + descendant_nodes(root)).uniq { |n| n['iri'] }
  resolve_parents(nodes, node['iri'])
end