twb 2.2.1 → 3.7.2

Sign up to get free protection for your applications and to get access to all the features.
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