twb 2.2.1 → 3.7.2

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/twb.rb +13 -1
  3. data/lib/twb/action.rb +5 -1
  4. data/lib/twb/analysis/AnnotatedFieldsCSVEmitter.rb +3 -0
  5. data/lib/twb/analysis/CalculatedFields/CalculatedFieldsAnalyzer.rb +276 -287
  6. data/lib/twb/analysis/CalculatedFields/MarkdownEmitter.rb +48 -34
  7. data/lib/twb/analysis/DataSources/DataSourceFieldsCSVEmitter.rb +103 -103
  8. data/lib/twb/analysis/DataSources/googlesheetdatasourcesanalyzer.rb +79 -0
  9. data/lib/twb/analysis/DocumentedFieldsMarkdownEmitter.rb +1 -1
  10. data/lib/twb/analysis/Sheets/sheetfieldsanalyzer.rb +82 -0
  11. data/lib/twb/analysis/Sheets/sheetfiltersanalyzer.rb +214 -0
  12. data/lib/twb/calculatedfield.rb +20 -5
  13. data/lib/twb/codedfield.rb +87 -0
  14. data/lib/twb/columnfield.rb +21 -2
  15. data/lib/twb/connection.rb +33 -0
  16. data/lib/twb/dashboard.rb +5 -1
  17. data/lib/twb/datasource.rb +131 -20
  18. data/lib/twb/dbfield.rb +4 -0
  19. data/lib/twb/field.rb +5 -1
  20. data/lib/twb/fieldcalculation.rb +134 -78
  21. data/lib/twb/localfield.rb +5 -1
  22. data/lib/twb/mappedfield.rb +5 -1
  23. data/lib/twb/metadatafield.rb +5 -1
  24. data/lib/twb/storyboard.rb +5 -1
  25. data/lib/twb/tabclass.rb +71 -0
  26. data/lib/twb/tabtest.rb +31 -0
  27. data/lib/twb/tabtool.rb +63 -0
  28. data/lib/twb/twbcodedfield.rb +87 -0
  29. data/lib/twb/util/cypher.rb +112 -0
  30. data/lib/twb/util/cypherpython.rb +128 -0
  31. data/lib/twb/util/docprep.rb +46 -0
  32. data/lib/twb/util/fielddomainloader.rb +108 -0
  33. data/lib/twb/util/gml.rb +144 -0
  34. data/lib/twb/util/gmledge.rb +73 -0
  35. data/lib/twb/util/graph.rb +30 -0
  36. data/lib/twb/util/graphedge.rb +8 -9
  37. data/lib/twb/util/graphnode.rb +46 -29
  38. data/lib/twb/util/tabgraph.rb +30 -0
  39. data/lib/twb/window.rb +5 -1
  40. data/lib/twb/workbook.rb +18 -5
  41. data/lib/twb/worksheet.rb +5 -1
  42. data/test/fieldAliases.rb +10 -0
  43. data/test/testFieldAliases.rb +65 -0
  44. data/test/testFieldDomainLoaded.rb +14 -0
  45. data/test/testFieldDomainLoader.rb +131 -0
  46. metadata +22 -1
@@ -19,7 +19,7 @@ module Twb
19
19
 
20
20
  # Assumption: A field can only be either a MetadataField or a LocalField, not both in a given Workbook data connection.
21
21
 
22
- class LocalField
22
+ class LocalField < TabClass
23
23
 
24
24
  attr_reader :node, :type, :datatype, :name, :uiname, :hidden, :ordinal, :properties
25
25
 
@@ -44,6 +44,10 @@ module Twb
44
44
  # return self
45
45
  end
46
46
 
47
+ def id
48
+ @id ||= @id = @name.hash
49
+ end
50
+
47
51
  def properties
48
52
  @properties ||= loadProperties
49
53
  end
@@ -17,7 +17,7 @@ require 'nokogiri'
17
17
 
18
18
  module Twb
19
19
 
20
- class DbField
20
+ class DbField < TabClass
21
21
 
22
22
  include Comparable
23
23
 
@@ -37,6 +37,10 @@ module Twb
37
37
  @id = "#{dataSource}::#{dbname}::#{dbtable}::#{uiname}"
38
38
  end
39
39
 
40
+ def id
41
+ @id ||= @id = @name.hash
42
+ end
43
+
40
44
  def properties
41
45
  @properties ||= loadProperties
42
46
  end
@@ -17,7 +17,7 @@ require 'nokogiri'
17
17
 
18
18
  module Twb
19
19
 
20
- class MetadataField
20
+ class MetadataField < TabClass
21
21
 
22
22
  include Comparable
23
23
 
@@ -78,6 +78,10 @@ module Twb
78
78
  @id = "'%s::%s' " % [@table,@remoteName]
79
79
  end
80
80
 
81
+ def id
82
+ @id ||= @id = @name.hash
83
+ end
84
+
81
85
  def properties
82
86
  @properties ||= loadProperties
83
87
  end
@@ -18,7 +18,7 @@ require 'digest/md5'
18
18
 
19
19
  module Twb
20
20
 
21
- class Storyboard
21
+ class Storyboard < TabClass
22
22
 
23
23
  @@hasher = Digest::SHA256.new
24
24
 
@@ -31,6 +31,10 @@ module Twb
31
31
  loadSheets
32
32
  end
33
33
 
34
+ def id
35
+ @id ||= @id = @name.hash
36
+ end
37
+
34
38
  def loadSheets
35
39
  @sheets = {}
36
40
  sheets = @node.xpath('.//story-point').to_a
@@ -0,0 +1,71 @@
1
+ # Copyright (C) 2014, 2015, 2016 Chris Gerrard
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'logger'
17
+
18
+ module Twb
19
+
20
+ class TabClass
21
+
22
+ attr_accessor :uuid, :type, :id, :properties, :ttlogfile, :ttlogfilename
23
+
24
+ @@ttlogfilename = 'TableauTools.ttlog'
25
+ @@ttdocdir = './ttdoc'
26
+ @localEmit = false
27
+
28
+ def initialize
29
+ @ttlogfile = 'TableauTools.ttlog'
30
+ @logger = Logger.new(@ttlogfile)
31
+ @logger.level = Logger::DEBUG
32
+ end
33
+
34
+ def type
35
+ @type ||= @type = self.class.to_s.split(/(?=[A-Z])/).join(' ')
36
+ end
37
+
38
+ def uuid
39
+ @uuid ||= @uuid = SecureRandom.uuid
40
+ end
41
+
42
+ def properties
43
+ {}
44
+ end
45
+
46
+ def setDocFileName
47
+ docFile @@ttlogfilename
48
+ end
49
+
50
+ def docFile name
51
+ @ttdocdir.nil? ? name : "#{@@ttdocdir}/#{name}"
52
+ end
53
+
54
+ def emit(local=@localEmit, stuff)
55
+ # puts "\nstuff.class #{stuff.class} :: #{stuff}" if local
56
+ if stuff.is_a? String then
57
+ lines = stuff.split(/\n/)
58
+ lines.each do |line|
59
+ @logger.debug "#{@emitPrefix}#{line}"
60
+ puts "#{@emitPrefix}#{line}" if local
61
+ end
62
+ else
63
+ @logger.debug "#{@emitPrefix}#{stuff}"
64
+ puts "#{@emitPrefix}#{stuff}" if local
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
@@ -0,0 +1,31 @@
1
+ # Copyright (C) 2014, 2015, 2016 Chris Gerrard
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'tabtool'
17
+
18
+ module Twb
19
+
20
+ class TabToolTest
21
+ include TabTool
22
+
23
+ def initialize
24
+ @ttlogfile = 'TableauTools.ttlog'
25
+ @logger = Logger.new(@ttlogfile)
26
+ @logger.level = Logger::DEBUG
27
+ end
28
+
29
+ end # class TabToolTest
30
+
31
+ end # module Twb
@@ -0,0 +1,63 @@
1
+ # Copyright (C) 2014, 2018 Chris Gerrard
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'logger'
17
+
18
+ module TabTool
19
+
20
+ attr_accessor :ttdocdir, :logger, :loglevel, :logfilename
21
+ attr_reader :funcdoc, :docfiles
22
+
23
+ TTDOCDIR = './ttdoc'
24
+
25
+ @funcdoc = {:class=>nil, :blurb=>nil, :description=>nil,}
26
+ @docfiles = {}
27
+
28
+ @ttdocdir = './ttdoc'
29
+ @logfilename = 'TabTools.ttlog'
30
+ @loglevel = Logger::DEBUG
31
+ @localEmit = false
32
+
33
+ def funcdoc
34
+ @funcdoc.nil? ? {:class=>'n/a', :blurb=>'generic TabTool blurb', :description=>'A useful Tableau Tool.'} : @funcdoc
35
+ end
36
+
37
+ def docFile name
38
+ # emit "docFile: TTDOCDIR: #{TTDOCDIR} name:'#{name}'"
39
+ TTDOCDIR.nil? ? name : "#{TTDOCDIR}/#{name}"
40
+ end
41
+
42
+ def emit(local=@localEmit, stuff)
43
+ initLogger if @logger.nil?
44
+ if stuff.is_a? String then
45
+ lines = stuff.split(/\n/)
46
+ lines.each do |line|
47
+ @logger.debug "#{@emitPrefix}#{line}"
48
+ puts "#{@emitPrefix}#{line}" if local
49
+ end
50
+ else
51
+ @logger.debug "#{@emitPrefix}#{stuff}"
52
+ puts "#{@emitPrefix}#{stuff}" if local
53
+ end
54
+ end
55
+
56
+ def initLogger
57
+ lfn = docFile("#{self.class.to_s.split('::').last}.ttlog")
58
+ @logger = Logger.new(lfn)
59
+ @logger.level = Logger::DEBUG
60
+ end
61
+
62
+ end # module TabTool
63
+
@@ -0,0 +1,87 @@
1
+ class CodedField
2
+
3
+ include Comparable
4
+
5
+ attr_reader :name, :techCode
6
+ attr_reader :uiname, :uiCode
7
+ attr_reader :dataSource, :dataSourceName, :dataSourceRef, :dataSourceExists
8
+ attr_reader :fqName, :type
9
+ attr_reader :techUIdiff
10
+
11
+ def initialize code, datasource
12
+ # puts "\n\nCalculationField :: %-25s | %s " % [datasource.uiname, code]
13
+ @dataSource = datasource
14
+ @dataSourceName = datasource.uiname
15
+ @dataSourceRef = :local
16
+ @dataSourceExists = true
17
+ @techUIdiff = false
18
+ @uiname = ''
19
+ rawCode = code.gsub(/^\[|\]$/,'')
20
+ parts = rawCode.split('].[')
21
+ #puts "Field: #{code} \t parts: #{parts.length} - #{parts.inspect}"
22
+ if parts.length == 1
23
+ @name = parts[0]
24
+ @techCode = "[#{parts[0]}]"
25
+ #puts "@name: #{@name}"
26
+ if datasource.nil?
27
+ # puts 'a'
28
+ @uiname = @name
29
+ @uiCode = @techCode
30
+ @techUIdiff = false
31
+ else # !datasource.nil?
32
+ # puts 'b'
33
+ #puts "b - found uiname for '#{@name}'?: #{!datasource.fieldUIName(@name).nil?} \t is:#{datasource.fieldUIName(@name)} "
34
+ @uiname = datasource.fieldUIName(@name).nil? ? @name : datasource.fieldUIName(@name)
35
+ @uiCode = @uiname.nil? ? @techCode : "[#{@uiname}]"
36
+ @techUIdiff = !@techCode.eql?(@uiCode)
37
+ # puts ":b #{datasource.fieldUIName(@name).nil?} ... #{@name} ... #{@uiname}"
38
+ # puts "CalculationField :: uin: %-25s | @name:%-s" % [@uiname,@name]
39
+ end
40
+ else # parts.length <> 1
41
+ # puts 'c'
42
+ rdstech = parts[0]
43
+ calcField = parts[1]
44
+ @uiname = calcField
45
+ @dataSourceName = rdstech
46
+ @dataSourceRef = :remote
47
+ @techCode = "[#{rdstech}].[#{calcField}]"
48
+ workbook = datasource.workbook
49
+ @dataSource = workbook.nil? ? nil : workbook.datasource(rdstech)
50
+ # puts "\t twb: #{workbook.class} / remoteds: #{remoteds.class} : #{remoteds.nil? ? "<<NOT FOUND:#{rdstech}:>>" : remoteds.uiname} "
51
+ #--
52
+ if @dataSource.nil? || @dataSource.fieldUIName(calcField).nil?
53
+ # puts 'd'
54
+ @uiname = calcField
55
+ @uiCode = "[<<NOT FOUND>>#{rdstech}].[#{calcField}]"
56
+ @techUIdiff = true
57
+ @dataSourceExists = false
58
+ else # !remoteds.nil?
59
+ # puts 'e'
60
+ @dataSourceName = @dataSource.uiname
61
+ @uiname = @dataSource.fieldUIName(calcField)
62
+ @uiCode = "[#{@dataSourceName}].[#{@uiname}]"
63
+ @techUIdiff = !@techCode.eql?(@uiCode)
64
+ @dataSourceExists = true
65
+ end
66
+ end
67
+ # puts "\t dsName: #{@dataSourceName}"
68
+ # puts "\t @name: #{@name}"
69
+ # puts "\t uiname: #{@uiname}"
70
+ @fqName = "#{@dataSourceName}::#{@uiname}"
71
+ @type = @dataSource.calculatedField(@uiname).nil? ? :DatabaseField : :CalculatedField
72
+ end # initialize
73
+
74
+ def id
75
+ @id ||= @id = "#{@dataSourceName}::#{@uiname}"
76
+ end
77
+
78
+ def <=>(other)
79
+ # myName = @uiname.nil? ? '' : @uiname
80
+ # otherName = other.uiName.nil? ? "" : other.uiName
81
+ # # puts "#{@uiname} / #{myName} <=> #{otherName} / #{other.uiName}"
82
+ # # puts "#{@uiname.nil?} // #{other.uiName.nil?}"
83
+ # myName <=> otherName
84
+ @fqName <=> other.fqName
85
+ end
86
+
87
+ end # class CalculationField
@@ -0,0 +1,112 @@
1
+ # Copyright (C) 2012, 2015 Chris Gerrard
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'csv'
17
+
18
+ module Twb
19
+ module Util
20
+
21
+ class Cypher
22
+
23
+ include TabTool
24
+
25
+ @@hasher = Digest::SHA256.new
26
+
27
+ #====================================================================
28
+
29
+ attr_accessor :nodes, :edges, :fileName, :fileName
30
+ attr_accessor :cleanup
31
+
32
+ def initialize
33
+ emit "Cypher.initialize"
34
+ @fileName = 'Neo4jCommands'
35
+ @nodes = []
36
+ @edges = []
37
+ @cleanup = false
38
+ end
39
+
40
+ def render
41
+ @file = File.open(docFile("#{@fileName}.cypher"),'w')
42
+ renderNodes
43
+ renderEdges
44
+ @file.close
45
+ return @file
46
+ end
47
+
48
+ def renderNodes
49
+ csv = CSV.open(docFile("#{@fileName}.nodes.csv"),'w')
50
+ csv << ['Type','Name','UUID']
51
+ nodesCSV = Set.new
52
+ nodeCmds = SortedSet.new
53
+ nodesByType = Hash.new { |type,nodes| type[nodes] = [] }
54
+ @nodes.each do |node|
55
+ nodesCSV << [node.type, node.name, node.uuid]
56
+ nodeCmds << encode('MERGE','node',node,';')
57
+ nodesByType[node.type] << node
58
+ end
59
+ if @cleanup
60
+ nodesByType.keys.each do |type|
61
+ @file.puts "DROP CONSTRAINT ON (node:#{type}) ASSERT node.uuid IS UNIQUE ;"
62
+ end
63
+ @file.puts "MATCH (n) DETACH DELETE n ;"
64
+ nodesByType.keys.each do |type|
65
+ @file.puts "CREATE CONSTRAINT ON (node:#{type}) ASSERT node.uuid IS UNIQUE ;"
66
+ end
67
+ @file.puts "//--"
68
+ end
69
+ nodesCSV.each do |rec|
70
+ csv << rec
71
+ end
72
+ nodeCmds.each do |cmd|
73
+ @file.puts cmd
74
+ end
75
+ csv.close
76
+ end
77
+ # USING PERIODIC COMMIT
78
+ # LOAD CSV WITH HEADERS FROM "file:://C:/tech/Tableau/Tableau Tools/Ruby/experiments/GraphElements.nodes.csv" AS row
79
+ # CREATE (:row.Type {name: row.Name, uuid: row.UUID});
80
+
81
+ def renderEdges
82
+ csv = CSV.open("./ttdoc/#{@fileName}.edges.csv",'w')
83
+ csv << ['Source Type' , 'Source Name' , 'Source UUID' , 'Relationship', 'Target Type', 'Target Name', 'Target UUID' ]
84
+ @edges.each do |edge|
85
+ relationship = edge.relationship.upcase.gsub(/[ ]+/,'_')
86
+ csv << [edge.from.type, edge.from.name, edge.from.uuid, relationship , edge.to.type , edge.to.name, edge.to.uuid ]
87
+ @file.puts ' '
88
+ @file.puts encodeEdge('MATCH', 'source', edge.from)
89
+ @file.puts encodeEdge('MATCH', 'target', edge.to)
90
+ @file.puts "%-8s (source)-[r:%s]->(target) " % ['MERGE', edge.relationship.upcase.gsub(/[ ]+/,'_')]
91
+ @file.puts "RETURN source.name, type(r), target.name ;"
92
+ end
93
+ csv.close
94
+ end
95
+
96
+ def encode command, varName, node, terminator=''
97
+ "%-8s (%s:%s { name:'%s', uuid: '%s' } ) %s" % [command, varName, node.type, node.name, node.uuid, terminator ]
98
+ end
99
+
100
+ def encodeEdge command, varName, node, terminator=''
101
+ "%-8s (%s:%s { uuid: '%s' } ) %s" % [command, varName, node.type, node.uuid, terminator ]
102
+ end
103
+
104
+ def to_s
105
+ "file:#{@fileName}; #nodes:#{@nodes.length}; #edges:#{@edges.length}"
106
+ end
107
+
108
+
109
+ end
110
+
111
+ end # module Util
112
+ end # module Twb