Class: Eco::API::Organization::TagTree

Inherits:
Object
  • Object
show all
Defined in:
lib/eco/api/organization/tag_tree.rb

Overview

Provides helpers to deal with tagtrees.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tagtree = [], depth: -1,, path: [], enviro: nil) ⇒ TagTree

Returns a new instance of TagTree.

Examples:

Node format:

{"tag": "NODE NAME", "nodes": subtree}

Tree/subtree format:

[[Node], ...]

Input format example:

tree = [{"tag" => "AUSTRALIA", "nodes" => [
    {"tag" => "SYDNEY", "nodes" => []}
]}]
tree = TagTree.new(tree.to_json)

Parameters:

  • tagtree (String) (defaults to: [])

    representation of the tagtree in json.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/eco/api/organization/tag_tree.rb', line 21

def initialize(tagtree = [], depth: -1, path: [], enviro: nil)
  case tagtree
  when String
    @source = JSON.parse(tagtree)
  else
    @source = tagtree
  end
  fatal("You are trying to initialize a TagTree with a null tagtree") if !@source
  fatal("Expecting Environment object. Given: #{enviro}") if enviro && !enviro.is_a?(API::Common::Session::Environment)
  @enviro = enviro

  @depth = depth
  @tag = @source.is_a?(Array) ? nil : @source.dig('tag')&.upcase

  @path  = path || []
  @path.push(@tag) unless !@tag

  nodes = @source.is_a?(Array) ? @source : @source.dig('nodes') || []
  @nodes = nodes.map {|cnode| TagTree.new(cnode, depth: @depth + 1, path: @path.dup, enviro: @enviro)}

  init_hashes
end

Instance Attribute Details

#depthObject (readonly)

Returns the value of attribute depth.



8
9
10
# File 'lib/eco/api/organization/tag_tree.rb', line 8

def depth
  @depth
end

#enviroObject (readonly)

Returns the value of attribute enviro.



9
10
11
# File 'lib/eco/api/organization/tag_tree.rb', line 9

def enviro
  @enviro
end

#nodesObject (readonly)

Returns the value of attribute nodes.



7
8
9
# File 'lib/eco/api/organization/tag_tree.rb', line 7

def nodes
  @nodes
end

#path(key = nil) ⇒ Array<String> (readonly)

Note:

the path is not relative to the subtree, but absolute to the entire tree.

Finds the path from a node key to its root node in the tree. If key is not specified, returns the path from current node to root.

Parameters:

  • key (String) (defaults to: nil)

    tag to find the path to.

Returns:

  • (Array<String>)


89
90
91
# File 'lib/eco/api/organization/tag_tree.rb', line 89

def path
  @path
end

#tagObject (readonly)

Returns the value of attribute tag.



7
8
9
# File 'lib/eco/api/organization/tag_tree.rb', line 7

def tag
  @tag
end

Instance Method Details

#default_tag(*values) ⇒ String

Helper to decide which among the tags will be the default.

  • take the deepest tag (the one that is further down in the tree)
  • if there are different options (several nodes at the same depth):
    • take the common node between them (i.e. you have Hamilton and Auckland -> take New Zealand)
    • if there's no common node between them, take the first (unless they are at top level of the tree)

Parameters:

  • values (Array<String>)

    list of tags.

Returns:

  • (String)

    default tag.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/eco/api/organization/tag_tree.rb', line 136

def default_tag(*values)
  values = filter_tags(values)
  nodes = []; depth = -1
  values.each do |tag|
    raise("Couldn't find the node of #{tag} in the tag-tree definition") unless cnode = node(tag)

    if cnode.depth > depth
      nodes = [cnode]
      depth = cnode.depth
    elsif cnode.depth == depth
      nodes.push(cnode)
    end
  end

  default_tag = nil
  if nodes.length > 1
    common      = nodes.reduce(self.tags.reverse) {|com, cnode| com & cnode.path.reverse}
    default_tag = common.first if common.length > 0 && depth > 0
  end
  default_tag = nodes.first&.tag if !default_tag && depth > 0
  default_tag
end

#empty?Boolean

Returns true if there are tags in the node, false otherwise.

Returns:

  • (Boolean)

    true if there are tags in the node, false otherwise.



45
46
47
# File 'lib/eco/api/organization/tag_tree.rb', line 45

def empty?
  @has_tags.empty?
end

#filter_tags(list) ⇒ Array<String>

Filters tags out that do not belong to the tree

Parameters:

  • list (Array<String>)

    source tags.

Returns:

  • (Array<String>)


79
80
81
82
# File 'lib/eco/api/organization/tag_tree.rb', line 79

def filter_tags(list)
  return [] unless list && list.is_a?(Array)
  list.select {|str| tag?(str)}
end

#node(key) ⇒ TagTree?

Finds a subtree node.

Parameters:

  • key (String)

    parent node of subtree.

Returns:

  • (TagTree, nil)

    if the tag key is a node, returns that node.



71
72
73
74
# File 'lib/eco/api/organization/tag_tree.rb', line 71

def node(key)
  return nil unless tag?(key)
  @hash_tags[key.upcase]
end

#tag?(key) ⇒ Boolean

Verifies if a tag exists in the tree.

Parameters:

  • key (String)

    tag to verify.

Returns:

  • (Boolean)


64
65
66
# File 'lib/eco/api/organization/tag_tree.rb', line 64

def tag?(key)
  @hash_tags.key?(key&.upcase)
end

#tags(depth: nil) ⇒ Array<String>

Parameters:

  • depth (Integer) (defaults to: nil)

    if empty, returns the list of tag nodes of that level. Otherwise the list of tag nodes of the entire subtree.

Returns:

  • (Array<String>)


51
52
53
54
55
56
57
58
59
# File 'lib/eco/api/organization/tag_tree.rb', line 51

def tags(depth: nil)
  if !depth || depth < 0
    @hash_tags.keys
  else
    @hash_tags.select do |t, n|
      n.depth == depth
    end.keys
  end
end

#user_tags(initial: [], final: [], preserve_custom: true, add_custom: false) ⇒ Array<String>

Helper to assign tags to a person account.

  • It preserves the :initial order, in case the :final tags are the same

Examples:

Usage example:

tree = [{"tag" => "Australia", "nodes" => [
     {"tag" => "SYDNEY", "nodes" => []},
     {"tag" => "MELBOURNE", "nodes" => []}
]}]

tree = TagTree.new(tree.to_json)
original = ["SYDNEY", "RISK"]
final    = ["MELBOURNE", "EVENT"]

tree.user_tags(initial: original, final: final) # out: ["MELBOURNE", "RISK"]
tree.user_tags(initial: original, final: final, preserve_custom: false) # out: ["MELBOURNE"]
tree.user_tags(initial: original, final: final, add_custom: true) # out: ["MELBOURNE", "RISK", "EVENT"]
tree.user_tags(initial: original, final: final, preserve_custom: false, add_custom: true) # out: ["MELBOURNE", "EVENT"]

Parameters:

  • initial (Array<String>) (defaults to: [])

    original tags a person has in their account.

  • final (Array<String>) (defaults to: [])

    target tags the person should have in their account afterwards.

  • preserve_custom (Boolean) (defaults to: true)

    indicates if original tags that are not in the tree should be added/preserved.

  • add_custom (Boolean) (defaults to: false)

    indicates if target tags that are not in the tree should be really added.

Returns:

  • (Array<String>)

    with the treated final tags.



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/eco/api/organization/tag_tree.rb', line 117

def user_tags(initial: [], final: [], preserve_custom: true, add_custom: false)
  initial = [initial].flatten.compact
  final   = [final].flatten.compact
  raise "Expected Array for initial: and final:" unless initial.is_a?(Array) && final.is_a?(Array)
  final    = filter_tags(final) unless add_custom
  custom   = initial - filter_tags(initial)
  final    = final + custom     if preserve_custom
  new_tags = final - initial
  # keep same order as they where
  (initial & final) + new_tags
end