twb 1.0.5 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/twb.rb +10 -6
- data/lib/twb/analysis/CalculatedFields/CSVEmitter.rb +154 -0
- data/lib/twb/analysis/CalculatedFields/CalculatedFieldsAnalyzer.rb +527 -0
- data/lib/twb/analysis/CalculatedFields/MarkdownEmitter.rb +71 -0
- data/lib/twb/analysis/DataSources/DataSourceTableFieldsCSVEmitter.rb +117 -0
- data/lib/twb/analysis/Sheets/WorksheetDataStructureCSVEmitter.rb +192 -0
- data/lib/twb/calculatedfield.rb +47 -0
- data/lib/twb/columnfield.rb +94 -0
- data/lib/twb/dashboard.rb +1 -1
- data/lib/twb/datasource.rb +368 -121
- data/lib/twb/fieldcalculation.rb +157 -81
- data/lib/twb/localfield.rb +32 -29
- data/lib/twb/metadatafield.rb +57 -15
- data/lib/twb/util/ftpPublisher.rb +48 -0
- data/lib/twb/util/joinUtilities.rb +52 -0
- data/lib/twb/workbook.rb +27 -10
- data/lib/twb/worksheet.rb +146 -53
- metadata +11 -5
- data/lib/twb/analysis/calculatedfieldsanalyzer.rb +0 -508
- data/lib/twb/apps/X-Ray Dashboards.rb +0 -80
- data/lib/twb/countNodes.rb +0 -98
    
        data/lib/twb/dashboard.rb
    CHANGED
    
    | @@ -65,7 +65,7 @@ module Twb | |
| 65 65 | 
             
                  @minheight  = size.attr('minheight')
         | 
| 66 66 | 
             
                  @minwidth   = size.attr('minwidth')
         | 
| 67 67 | 
             
                  @rangesize  = size.attr('rangesize')
         | 
| 68 | 
            -
                  @dimensions = @minwidth.to_s  | 
| 68 | 
            +
                  @dimensions = "%s:%s:%s:%s" % [@minwidth.to_s, @minheight.to_s, @maxwidth.to_s, @maxheight.to_s]
         | 
| 69 69 | 
             
                end
         | 
| 70 70 |  | 
| 71 71 | 
             
              end
         | 
    
        data/lib/twb/datasource.rb
    CHANGED
    
    | @@ -20,36 +20,47 @@ require 'json' | |
| 20 20 | 
             
            module Twb
         | 
| 21 21 |  | 
| 22 22 | 
             
              class DataSource
         | 
| 23 | 
            -
             | 
| 23 | 
            +
              
         | 
| 24 24 | 
             
                @@hasher = Digest::SHA256.new
         | 
| 25 25 |  | 
| 26 26 | 
             
                @@connGNodeParamsJSON = %q(
         | 
| 27 | 
            -
                { "csv"           : { "label" : ["filename"], "id" : ["directory","filename"], "type" : " | 
| 28 | 
            -
                  "excel"         : { "label" : ["filename"], "id" : ["directory","filename"], "type" : " | 
| 29 | 
            -
                  "dataengine"    : { "label" : ["dbname"  ], "id" : ["directory","filename"], "type" : " | 
| 30 | 
            -
                  "msaccess"      : { "label" : ["filename"], "id" : ["directory","filename"], "type" : " | 
| 31 | 
            -
                  " | 
| 32 | 
            -
                  " | 
| 33 | 
            -
                  " | 
| 27 | 
            +
                { "csv"           : { "label" : ["filename"], "id" : ["directory","filename"], "type" : "CSV"        },
         | 
| 28 | 
            +
                  "excel"         : { "label" : ["filename"], "id" : ["directory","filename"], "type" : "Excel"      },
         | 
| 29 | 
            +
                  "dataengine"    : { "label" : ["dbname"  ], "id" : ["directory","filename"], "type" : "TDE"        },
         | 
| 30 | 
            +
                  "msaccess"      : { "label" : ["filename"], "id" : ["directory","filename"], "type" : "MS Access"  },
         | 
| 31 | 
            +
                  "oracle"        : { "label" : ["server"  ], "id" : [            "server"  ], "type" : "Oracle"     },
         | 
| 32 | 
            +
                  "postgres"      : { "label" : ["server"  ], "id" : [            "server"  ], "type" : "PostgreSQL" },
         | 
| 33 | 
            +
                  "textscan"      : { "label" : ["filename"], "id" : ["directory","filename"], "type" : "CSV / TSV"  },
         | 
| 34 | 
            +
                  "excel-direct"  : { "label" : ["filename"], "id" : [            "filename"], "type" : "Excel"      },
         | 
| 35 | 
            +
                  "salesforce"    : { "label" : ["server"  ], "id" : [            "server"  ], "type" : "Salesforce" }
         | 
| 34 36 | 
             
                }
         | 
| 35 37 | 
             
                )
         | 
| 36 38 | 
             
                @@cgNodeParams  = JSON.parse @@connGNodeParamsJSON
         | 
| 37 39 |  | 
| 38 40 |  | 
| 39 | 
            -
                attr_reader : | 
| 40 | 
            -
                attr_reader : | 
| 41 | 
            -
                attr_reader : | 
| 42 | 
            -
                attr_reader : | 
| 41 | 
            +
                attr_reader :workbook
         | 
| 42 | 
            +
                attr_reader :name,          :caption,         :uiname
         | 
| 43 | 
            +
                attr_reader :dsclass,       :isExtract
         | 
| 44 | 
            +
                attr_reader :connection,    :connHash
         | 
| 45 | 
            +
                attr_reader :tables,        :joinPairs      
         | 
| 46 | 
            +
                attr_reader :localFields,   :localFieldNames, :localField, :hasField 
         | 
| 47 | 
            +
                attr_reader :columnFields 
         | 
| 48 | 
            +
                attr_reader :metadataFields 
         | 
| 49 | 
            +
                attr_reader :dbFields
         | 
| 50 | 
            +
                attr_reader :mappedFields
         | 
| 51 | 
            +
                attr_reader :fieldUINames
         | 
| 52 | 
            +
                attr_reader :calculatedFields, :calculatedFieldNamesMap, :calculatedFieldNames, :calculatedField 
         | 
| 53 | 
            +
                attr_reader :allFields 
         | 
| 43 54 | 
             
                attr_reader :filters
         | 
| 44 55 | 
             
                attr_reader :node
         | 
| 45 56 |  | 
| 46 | 
            -
                def initialize dataSourceNode
         | 
| 47 | 
            -
                  @node | 
| 48 | 
            -
                  @ | 
| 49 | 
            -
                  @ | 
| 50 | 
            -
                  @ | 
| 57 | 
            +
                def initialize dataSourceNode, workbook
         | 
| 58 | 
            +
                  @node     = dataSourceNode
         | 
| 59 | 
            +
                  @workbook = workbook 
         | 
| 60 | 
            +
                  @name     = @node.attr('name')
         | 
| 61 | 
            +
                  @caption  = @node.attr('caption')
         | 
| 62 | 
            +
                  @uiname   = if @caption.nil? || @caption == '' then @name else @caption end
         | 
| 51 63 | 
             
                  processConnection
         | 
| 52 | 
            -
                  processFields
         | 
| 53 64 | 
             
                  processFilters
         | 
| 54 65 | 
             
                  loadTableFields
         | 
| 55 66 | 
             
                  return self
         | 
| @@ -88,6 +99,10 @@ module Twb | |
| 88 99 | 
             
                  @connHash = Digest::MD5.hexdigest(dsConnStr)
         | 
| 89 100 | 
             
                end
         | 
| 90 101 |  | 
| 102 | 
            +
                def isExtract
         | 
| 103 | 
            +
                  @isExtract ||= @isExtract = !@node.at_xpath('./extract').nil?
         | 
| 104 | 
            +
                end
         | 
| 105 | 
            +
             | 
| 91 106 | 
             
                def loadTables connection
         | 
| 92 107 | 
             
                  @tables = {}
         | 
| 93 108 | 
             
                  nodes = connection.xpath(".//relation[@type='table']")
         | 
| @@ -96,43 +111,115 @@ module Twb | |
| 96 111 | 
             
                  end
         | 
| 97 112 | 
             
                end
         | 
| 98 113 |  | 
| 114 | 
            +
                def joinPairs
         | 
| 115 | 
            +
                  @joinPairs ||= loadJoinPairs
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def loadJoinPairs
         | 
| 119 | 
            +
                  @joinPairs = Set.new
         | 
| 120 | 
            +
                  mainJoin   = @node.xpath("./connection/relation[@type='join']")
         | 
| 121 | 
            +
                  clauses    = @node.xpath(".//relation[@type='join']/clause") 
         | 
| 122 | 
            +
                  clauses.each do |clause|
         | 
| 123 | 
            +
                    leafs = clause.xpath('.//expression[not(node())]')
         | 
| 124 | 
            +
                    @joinPairs << [ pullTable(leafs[0]) , pullTable(leafs[1]) ]
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
                  return @joinPairs
         | 
| 127 | 
            +
                end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                def joinTree
         | 
| 130 | 
            +
                  @joinTree ||= loadJoinTree
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                def loadJoinTree
         | 
| 134 | 
            +
                  loadJoinPairs if @joinPairs.nil?
         | 
| 135 | 
            +
                  # puts "LJT::#{@uiname}::joinPairs:: #{@joinPairs.inspect}"
         | 
| 136 | 
            +
                  # @joinPairs.each { |jp| puts "JP::#{jp}" }
         | 
| 137 | 
            +
                  @joinTree = JoinTree.new(@name)
         | 
| 138 | 
            +
                  @joinPairs.each do |from,to|
         | 
| 139 | 
            +
                    # puts "from:#{from} -> to:#{to}"
         | 
| 140 | 
            +
                    tableFrom = JoinTable.new(from)
         | 
| 141 | 
            +
                    tableTo   = JoinTable.new(to)
         | 
| 142 | 
            +
                    @joinTree.add(tableFrom, tableTo)
         | 
| 143 | 
            +
                  end
         | 
| 144 | 
            +
                  # puts '---'
         | 
| 145 | 
            +
                  return @joinTree
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                def pullTable xml
         | 
| 149 | 
            +
                    code =xml.attribute('op').text
         | 
| 150 | 
            +
                    table = code.split('].[')[0][1..-1]
         | 
| 151 | 
            +
                end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
             | 
| 99 154 | 
             
                def Parameters?
         | 
| 100 155 | 
             
                  'Parameters'.eql? @name 
         | 
| 101 156 | 
             
                end
         | 
| 102 157 |  | 
| 103 | 
            -
                def  | 
| 104 | 
            -
                   | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
                   | 
| 109 | 
            -
                   | 
| 110 | 
            -
                   | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
                                 when 'sqlserver'  then './column'
         | 
| 114 | 
            -
                                 else                   './connection/relation/columns/column'
         | 
| 115 | 
            -
                                 end
         | 
| 116 | 
            -
                  nodes = @node.xpath(fxpath)
         | 
| 117 | 
            -
                  # puts "DATASOURCE ::=>> @node: connClass: '#{connClass.class}' ::: #{connClass.eql?('dataengine')} fxpath: #{fxpath}  :: #{nodes.length}"
         | 
| 118 | 
            -
                  nodes.each do |node|
         | 
| 119 | 
            -
                    field = Twb::LocalField.new(node)
         | 
| 120 | 
            -
                    @localfields[field.dbname] = field
         | 
| 158 | 
            +
                def columnFields
         | 
| 159 | 
            +
                  @columnFields ||= loadColumnFields
         | 
| 160 | 
            +
                end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                def loadColumnFields
         | 
| 163 | 
            +
                  @columnFields = Set.new
         | 
| 164 | 
            +
                  nodes = @node.xpath('./column')
         | 
| 165 | 
            +
                  nodes.each do |n|
         | 
| 166 | 
            +
                    field = Twb::ColumnField.new n
         | 
| 167 | 
            +
                    @columnFields << field
         | 
| 121 168 | 
             
                  end
         | 
| 122 | 
            -
                   | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
                   | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 169 | 
            +
                  return @columnFields
         | 
| 170 | 
            +
                end 
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                def localFields
         | 
| 173 | 
            +
                  @localFields ||= loadLocalFields
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                def loadLocalFields
         | 
| 177 | 
            +
                  @localFields = {}
         | 
| 178 | 
            +
                  unless @connection.nil?  # Parameters has no connection node, & no local fields
         | 
| 179 | 
            +
                    connClass    = @node.at_xpath('./connection').attribute('class').text
         | 
| 180 | 
            +
                    fxpath       = case connClass
         | 
| 181 | 
            +
                                   when 'dataengine' then './column'
         | 
| 182 | 
            +
                                   when 'sqlserver'  then './column'
         | 
| 183 | 
            +
                                   else                   './connection/relation/columns/column'
         | 
| 184 | 
            +
                                   end
         | 
| 185 | 
            +
                    nodes = @node.xpath(fxpath)
         | 
| 186 | 
            +
                    nodes.each do |node|
         | 
| 187 | 
            +
                      field = Twb::LocalField.new(node)
         | 
| 188 | 
            +
                      @localFields[field.name] = field
         | 
| 189 | 
            +
                    end
         | 
| 129 190 | 
             
                  end
         | 
| 130 | 
            -
                   | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
                   | 
| 135 | 
            -
             | 
| 191 | 
            +
                  return @localFields
         | 
| 192 | 
            +
                end
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                def metadataFields
         | 
| 195 | 
            +
                  @metadataFields ||= loadMetadataFields
         | 
| 196 | 
            +
                end
         | 
| 197 | 
            +
             | 
| 198 | 
            +
                def loadMetadataFields
         | 
| 199 | 
            +
                  @metadataFields = Set.new
         | 
| 200 | 
            +
                  unless @connection.nil?  # Parameters has no connection node, & no metadata fields
         | 
| 201 | 
            +
                    # nodes = @node.xpath(".//metadata-record[@class='column']")
         | 
| 202 | 
            +
                    # # note: there are other nodes "<metadata-record class='capability'>" whose nature is unclear
         | 
| 203 | 
            +
                    # #       these nodes have no value for their <name node, so are not loaded
         | 
| 204 | 
            +
                    nodes = @node.xpath('./connection//metadata-record')
         | 
| 205 | 
            +
                    nodes.each do |node|
         | 
| 206 | 
            +
                      field = Twb::MetadataField.new(node)
         | 
| 207 | 
            +
                      field.source = :db
         | 
| 208 | 
            +
                      @metadataFields << field
         | 
| 209 | 
            +
                    end
         | 
| 210 | 
            +
                    nodes = @node.xpath('./extract//metadata-record')
         | 
| 211 | 
            +
                    nodes.each do |node|
         | 
| 212 | 
            +
                      field = Twb::MetadataField.new(node)
         | 
| 213 | 
            +
                      field.source = :extract
         | 
| 214 | 
            +
                      @metadataFields << field
         | 
| 215 | 
            +
                    end
         | 
| 216 | 
            +
                  end
         | 
| 217 | 
            +
                  return @metadataFields
         | 
| 218 | 
            +
                end
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                def updateTime
         | 
| 221 | 
            +
                  attr = @node.xpath('.//@update-time').first
         | 
| 222 | 
            +
                  @updateTime = attr.nil? ? nil : attr.value
         | 
| 136 223 | 
             
                end
         | 
| 137 224 |  | 
| 138 225 | 
             
                def fieldUIName fieldName
         | 
| @@ -163,12 +250,58 @@ module Twb | |
| 163 250 | 
             
                  end
         | 
| 164 251 | 
             
                end
         | 
| 165 252 |  | 
| 166 | 
            -
                def  | 
| 253 | 
            +
                def dbFields
         | 
| 167 254 | 
             
                  return @mappedFields unless @mappedFields.nil?
         | 
| 168 255 | 
             
                  loadTableFields
         | 
| 169 256 | 
             
                  return @mappedFields
         | 
| 170 257 | 
             
                end
         | 
| 171 258 |  | 
| 259 | 
            +
                def mappedFields
         | 
| 260 | 
            +
                  loadTableFields if @mappedFields.nil?
         | 
| 261 | 
            +
                  return @mappedFields
         | 
| 262 | 
            +
                end
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                # NOTE: Calculated Fields are mapped by their UI name
         | 
| 265 | 
            +
                def calculatedFieldsMap
         | 
| 266 | 
            +
                  @calculatedFieldsMap ||= loadCalculatedFields
         | 
| 267 | 
            +
                end
         | 
| 268 | 
            +
             | 
| 269 | 
            +
                def calculatedFields
         | 
| 270 | 
            +
                  loadCalculatedFields if @calculatedFieldsMap.nil?
         | 
| 271 | 
            +
                  return @calculatedFieldsMap.values
         | 
| 272 | 
            +
                end
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                def calculatedFieldNames
         | 
| 275 | 
            +
                  loadCalculatedFields if @calculatedFieldsMap.nil?
         | 
| 276 | 
            +
                  return @calculatedFieldsMap.keys
         | 
| 277 | 
            +
                end
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                def calculatedField(name)
         | 
| 280 | 
            +
                  loadCalculatedFields if @calculatedFieldsMap.nil?
         | 
| 281 | 
            +
                  return @calculatedFieldsMap[name]
         | 
| 282 | 
            +
                end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                def loadCalculatedFields
         | 
| 285 | 
            +
                  @calculatedFieldsMap = {}
         | 
| 286 | 
            +
                  cfnodes = @node.xpath("./column[calculation]")
         | 
| 287 | 
            +
                  cfnodes.each do |node|
         | 
| 288 | 
            +
                    calcField = Twb::CalculatedField.new node, self
         | 
| 289 | 
            +
                    @calculatedFieldsMap[calcField.uiname] = calcField
         | 
| 290 | 
            +
                  end
         | 
| 291 | 
            +
                  return @calculatedFieldsMap
         | 
| 292 | 
            +
                end
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                def allFields
         | 
| 295 | 
            +
                  return @allFields unless @allFields.nil?
         | 
| 296 | 
            +
                  @allFields = SortedSet.new
         | 
| 297 | 
            +
                  dbf = dbFields
         | 
| 298 | 
            +
                  @allFields << dbf.keys
         | 
| 299 | 
            +
                end
         | 
| 300 | 
            +
             | 
| 301 | 
            +
                def has_field? fieldName
         | 
| 302 | 
            +
                  dbFields.has_key? fieldName
         | 
| 303 | 
            +
                end
         | 
| 304 | 
            +
             | 
| 172 305 | 
             
                def fieldTable fieldName
         | 
| 173 306 | 
             
                  loadTableFields if @mappedFields.nil?
         | 
| 174 307 | 
             
                  tf = @mappedFields[fieldName]
         | 
| @@ -200,11 +333,11 @@ module Twb | |
| 200 333 | 
             
                  end
         | 
| 201 334 | 
             
                end
         | 
| 202 335 |  | 
| 203 | 
            -
            =begin
         | 
| 204 | 
            -
              <filter class='categorical' column='[enforcement_type]' filter-group='2'>
         | 
| 205 | 
            -
                <groupfilter function='member' level='[enforcement_type]' member='"towing"' user:ui-domain='database' user:ui-enumeration='inclusive' user:ui-marker='enumerate' />
         | 
| 206 | 
            -
              </filter>
         | 
| 207 | 
            -
             | 
| 336 | 
            +
                # =begin
         | 
| 337 | 
            +
                #  <filter class='categorical' column='[enforcement_type]' filter-group='2'>
         | 
| 338 | 
            +
                #    <groupfilter function='member' level='[enforcement_type]' member='"towing"' user:ui-domain='database' user:ui-enumeration='inclusive' user:ui-marker='enumerate' />
         | 
| 339 | 
            +
                #  </filter>
         | 
| 340 | 
            +
                # end
         | 
| 208 341 | 
             
                def processFilters
         | 
| 209 342 | 
             
                  if @filters.nil?
         | 
| 210 343 | 
             
                    @filters = {}
         | 
| @@ -224,69 +357,183 @@ module Twb | |
| 224 357 | 
             
                    end
         | 
| 225 358 | 
             
                  end
         | 
| 226 359 | 
             
                end
         | 
| 227 | 
            -
             | 
| 228 | 
            -
              
         | 
| 229 | 
            -
             | 
| 230 | 
            -
              #  | 
| 231 | 
            -
             | 
| 232 | 
            -
              #  | 
| 233 | 
            -
              # | 
| 234 | 
            -
              #  | 
| 235 | 
            -
              # | 
| 236 | 
            -
              # | 
| 237 | 
            -
              # | 
| 238 | 
            -
              #  | 
| 239 | 
            -
              #  | 
| 240 | 
            -
              # | 
| 241 | 
            -
              #  | 
| 242 | 
            -
               | 
| 243 | 
            -
             | 
| 244 | 
            -
             | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 247 | 
            -
             | 
| 248 | 
            -
             | 
| 249 | 
            -
             | 
| 250 | 
            -
             | 
| 251 | 
            -
               | 
| 252 | 
            -
             | 
| 253 | 
            -
               | 
| 254 | 
            -
             | 
| 255 | 
            -
             | 
| 256 | 
            -
             | 
| 257 | 
            -
             | 
| 258 | 
            -
             | 
| 259 | 
            -
             | 
| 260 | 
            -
             | 
| 261 | 
            -
                   | 
| 262 | 
            -
             | 
| 263 | 
            -
             | 
| 264 | 
            -
             | 
| 265 | 
            -
             | 
| 266 | 
            -
             | 
| 267 | 
            -
             | 
| 268 | 
            -
             | 
| 269 | 
            -
             | 
| 270 | 
            -
             | 
| 271 | 
            -
             | 
| 272 | 
            -
             | 
| 273 | 
            -
             | 
| 274 | 
            -
             | 
| 275 | 
            -
             | 
| 276 | 
            -
             | 
| 277 | 
            -
               | 
| 278 | 
            -
             | 
| 279 | 
            -
               | 
| 280 | 
            -
             | 
| 281 | 
            -
             | 
| 282 | 
            -
             | 
| 283 | 
            -
             | 
| 284 | 
            -
             | 
| 285 | 
            -
             | 
| 286 | 
            -
             | 
| 287 | 
            -
             | 
| 288 | 
            -
             | 
| 289 | 
            -
               | 
| 290 | 
            -
             | 
| 291 | 
            -
             | 
| 292 | 
            -
            end
         | 
| 360 | 
            +
             | 
| 361 | 
            +
              end # class DataSource
         | 
| 362 | 
            +
             | 
| 363 | 
            +
              # DataSource Utilities
         | 
| 364 | 
            +
             | 
| 365 | 
            +
              # # Generates and returns a set of graph node-edge-node triplets.
         | 
| 366 | 
            +
              # # The intention is to create a graph that can be standalone used as a subgraph by the function's caller.
         | 
| 367 | 
            +
              # # The initial implementation only considers a linear list of node names as input, resulting in 
         | 
| 368 | 
            +
              # # the creation of a single-path structure, e.g.
         | 
| 369 | 
            +
              # #  [inNode] -> node1 -> node2 -> ... -> nodeN
         | 
| 370 | 
            +
              # # this may manifest as a tree when there are siblings at any level, e.g.
         | 
| 371 | 
            +
              # #  [inNode] -> node1 -> node11 -> ... -> nodeX
         | 
| 372 | 
            +
              # #                    -> node22 -> ... -> nodeY
         | 
| 373 | 
            +
              # #           -> node2 -> node21 -> ... -> nodeZ
         | 
| 374 | 
            +
              # # Params:
         | 
| 375 | 
            +
              # # +inNodes+::   the node(s) to which this function's generated graph is to be linked, may be nil:
         | 
| 376 | 
            +
              # #   + , or multiple)
         | 
| 377 | 
            +
              # # +nodesList+:: list of node types by name to be built and linked together, in the order named  
         | 
| 378 | 
            +
              # def graphNodes nodesList
         | 
| 379 | 
            +
              #   graph     = Twb::Util::Graphedges.new()
         | 
| 380 | 
            +
              #   nodesList.each do |nName|
         | 
| 381 | 
            +
              #   end
         | 
| 382 | 
            +
              #   case @dsclass
         | 
| 383 | 
            +
              #     when 'federated'
         | 
| 384 | 
            +
              #       graph = processFederatedSource nodesList
         | 
| 385 | 
            +
              #   end
         | 
| 386 | 
            +
              #   return graph
         | 
| 387 | 
            +
              # end
         | 
| 388 | 
            +
             | 
| 389 | 
            +
              # def processFederatedSource nodesList
         | 
| 390 | 
            +
              #   emit false, "       (federated)  #{dataSource.uiname}"
         | 
| 391 | 
            +
              #   dsGNode     = Twb::Util::Graphnode.new(name: @uiname, id: @name, type: 'Data Connection')
         | 
| 392 | 
            +
              #   graph       = Twb::Util::Graphedges.new(dsGNode)
         | 
| 393 | 
            +
              #   edges       = []
         | 
| 394 | 
            +
              #   dsNode      = dataSource.node
         | 
| 395 | 
            +
              #   connections = dsNode.xpath('./connection/named-connections/named-connection/connection')
         | 
| 396 | 
            +
              #   connections.each do |conn|
         | 
| 397 | 
            +
              #     connClass  = conn.attribute('class').text
         | 
| 398 | 
            +
              #     emit true, "CONN CLASS: #{connClass}"
         | 
| 399 | 
            +
              #     # -- Generating Source Node
         | 
| 400 | 
            +
              #     cgParams = @@cgNodeParams[connClass]
         | 
| 401 | 
            +
              #     # emit true, "cgparams  : #{cgParams}"
         | 
| 402 | 
            +
              #     cLabel       = buildConnGraphPart( conn, cgParams['label'])
         | 
| 403 | 
            +
              #     cID          = buildConnGraphPart( conn, cgParams['id'])
         | 
| 404 | 
            +
              #     cType        = cgParams['type']
         | 
| 405 | 
            +
              #     # emit true, "   label  : #{cLabel}"
         | 
| 406 | 
            +
              #     # emit true, "      id  : #{cID}"
         | 
| 407 | 
            +
              #     # emit true, "    type  : #{cType}"
         | 
| 408 | 
            +
              #     srcNode   = Twb::Util::Graphnode.new(name: cLabel,  id: cID,     type: 'Data Source')
         | 
| 409 | 
            +
              #     graphEdge = Twb::Util::Graphedge.new(from: dsGNode, to: srcNode, relationship: 'is located at')
         | 
| 410 | 
            +
              #     graph.edges << graphEdge
         | 
| 411 | 
            +
              #   end
         | 
| 412 | 
            +
              #   return graph
         | 
| 413 | 
            +
              # end
         | 
| 414 | 
            +
             | 
| 415 | 
            +
              # def buildConnGraphPart connNode, attributes
         | 
| 416 | 
            +
              #   return connNode.attribute(attributes) if attributes.is_a? String
         | 
| 417 | 
            +
              #   emit false, "ATTRIBUTES :: #{attributes}"
         | 
| 418 | 
            +
              #   str = ''
         | 
| 419 | 
            +
              #   attributes.each do |attName|
         | 
| 420 | 
            +
              #     attrib = connNode.attribute(attName)
         | 
| 421 | 
            +
              #     emit false, " -#{attName}\t-> #{attrib}"
         | 
| 422 | 
            +
              #     str   += attrib.text unless attrib.nil?
         | 
| 423 | 
            +
              #   end
         | 
| 424 | 
            +
              #   return str
         | 
| 425 | 
            +
              # end
         | 
| 426 | 
            +
             | 
| 427 | 
            +
             | 
| 428 | 
            +
              class JoinTablePair
         | 
| 429 | 
            +
                attr_reader :from, :to
         | 
| 430 | 
            +
                def initialize(from, to)
         | 
| 431 | 
            +
                  raise ParamaterException.new("'From' cannot be nil.") if from.nil?
         | 
| 432 | 
            +
                  raise ParamaterException.new("'To' cannot be nil.")   if to.nil?
         | 
| 433 | 
            +
                  @from = from
         | 
| 434 | 
            +
                  @to   = to
         | 
| 435 | 
            +
                end
         | 
| 436 | 
            +
                def to_s
         | 
| 437 | 
            +
                  "#{@from} -> #{@to}"
         | 
| 438 | 
            +
                end
         | 
| 439 | 
            +
              end # class JoinTablePair
         | 
| 440 | 
            +
             | 
| 441 | 
            +
             | 
| 442 | 
            +
              class JoinTable
         | 
| 443 | 
            +
                include Comparable
         | 
| 444 | 
            +
             | 
| 445 | 
            +
                attr_reader   :name,     :datasource #, :str
         | 
| 446 | 
            +
                attr_accessor :children, :depth
         | 
| 447 | 
            +
             | 
| 448 | 
            +
                def initialize(name, datasource=nil)
         | 
| 449 | 
            +
                  @name       = name
         | 
| 450 | 
            +
                  @datasource = datasource
         | 
| 451 | 
            +
                  # @str        = nil
         | 
| 452 | 
            +
                  @children   = {}
         | 
| 453 | 
            +
                  @depth      = 0
         | 
| 454 | 
            +
                end
         | 
| 455 | 
            +
             | 
| 456 | 
            +
                def to_s
         | 
| 457 | 
            +
                  str = "[#{@datasource}].[#{@name}] :: (#{@depth}) :: #{@children.length} :: "
         | 
| 458 | 
            +
                  @children.each { |n,c| str << "#{n}, " }
         | 
| 459 | 
            +
                  return str
         | 
| 460 | 
            +
                end
         | 
| 461 | 
            +
             | 
| 462 | 
            +
                def <=>(anOther)
         | 
| 463 | 
            +
                  @str <=> anOther.str
         | 
| 464 | 
            +
                end
         | 
| 465 | 
            +
             | 
| 466 | 
            +
                def addChild child
         | 
| 467 | 
            +
                  # puts "#{@name}.addChild(#{child.name})"
         | 
| 468 | 
            +
                  # puts "children: #{@children}"
         | 
| 469 | 
            +
                  @children[child.name] = child if @children[child.name].nil?
         | 
| 470 | 
            +
                  # puts "children: #{@children}"
         | 
| 471 | 
            +
                end
         | 
| 472 | 
            +
             | 
| 473 | 
            +
                def child name
         | 
| 474 | 
            +
                  @children[name]
         | 
| 475 | 
            +
                end
         | 
| 476 | 
            +
             | 
| 477 | 
            +
              end # class JoinTable
         | 
| 478 | 
            +
             | 
| 479 | 
            +
             | 
| 480 | 
            +
              class JoinTree
         | 
| 481 | 
            +
             | 
| 482 | 
            +
                attr_reader  :datasource, :root, :maxdepth, :tables
         | 
| 483 | 
            +
             | 
| 484 | 
            +
                def initialize  datasource
         | 
| 485 | 
            +
                  @datasource = datasource
         | 
| 486 | 
            +
                  @root       = nil
         | 
| 487 | 
            +
                  @maxdepth   = 0
         | 
| 488 | 
            +
                  @tables     = {}
         | 
| 489 | 
            +
                end
         | 
| 490 | 
            +
             | 
| 491 | 
            +
                def add host, dest
         | 
| 492 | 
            +
                  # puts "\nJT add() host: #{host}"
         | 
| 493 | 
            +
                  # puts "           dest: #{dest}"
         | 
| 494 | 
            +
                  from = @tables[host.name].nil? ? host : @tables[host.name]
         | 
| 495 | 
            +
                  to   = @tables[dest.name].nil? ? dest : @tables[dest.name]
         | 
| 496 | 
            +
                  from.addChild(to)
         | 
| 497 | 
            +
                  @tables[from.name] = from
         | 
| 498 | 
            +
                  @tables[to.name]   = to
         | 
| 499 | 
            +
                  if @root.nil?  || @root.name.eql?(to.name)
         | 
| 500 | 
            +
                    @root = from
         | 
| 501 | 
            +
                  end
         | 
| 502 | 
            +
                  setDepth(@root,1)
         | 
| 503 | 
            +
                end
         | 
| 504 | 
            +
             | 
| 505 | 
            +
                def table tableName
         | 
| 506 | 
            +
                  @tables[tableName]
         | 
| 507 | 
            +
                end
         | 
| 508 | 
            +
             | 
| 509 | 
            +
                def setDepth table, depth
         | 
| 510 | 
            +
                  # puts "-- setDepth(#{table.class}[#{table.name}] \t, #{depth})"
         | 
| 511 | 
            +
                  @tables[table.name].depth = depth
         | 
| 512 | 
            +
                  childrenDepth = depth+1
         | 
| 513 | 
            +
                  table.children.each { |n,c| setDepth(c,childrenDepth)}
         | 
| 514 | 
            +
                end
         | 
| 515 | 
            +
             | 
| 516 | 
            +
                def tableDepth tableName
         | 
| 517 | 
            +
                  @tables[tableName].nil? ? 0 : @tables[tableName].depth
         | 
| 518 | 
            +
                end
         | 
| 519 | 
            +
             | 
| 520 | 
            +
                def disp table
         | 
| 521 | 
            +
                  puts "%s %s %s  (%d)" % [' ' * table.depth, '-' * table.depth, table.name, table.depth]
         | 
| 522 | 
            +
                  table.children.each { |n,c| disp c}
         | 
| 523 | 
            +
                end
         | 
| 524 | 
            +
             | 
| 525 | 
            +
                def iterate
         | 
| 526 | 
            +
                  disp @root
         | 
| 527 | 
            +
                end
         | 
| 528 | 
            +
             | 
| 529 | 
            +
                def to_s
         | 
| 530 | 
            +
                  str = "root: #{@root}\ntables"
         | 
| 531 | 
            +
                  @tables.each do |t| 
         | 
| 532 | 
            +
                    str << " tbl:: #{t}"
         | 
| 533 | 
            +
                  end
         | 
| 534 | 
            +
                  return str
         | 
| 535 | 
            +
                end
         | 
| 536 | 
            +
             | 
| 537 | 
            +
              end # class JoinTree  
         | 
| 538 | 
            +
             | 
| 539 | 
            +
            end # module Twb
         |