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
@@ -0,0 +1,128 @@
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 CypherPython
22
+
23
+ # @@hasher = Digest::SHA256.new
24
+
25
+ @@docfileName = './ttdoc/cypherpython.log'
26
+
27
+ #====================================================================
28
+
29
+ attr_accessor :nodes, :edges
30
+ attr_accessor :fileName, :fileName
31
+ attr_accessor :user, :password
32
+ attr_accessor :cleanup
33
+
34
+ def initialize
35
+ @docfile = File.open(@@docfileName,'a+')
36
+ @docfile.puts "Starting up the Cypher in Python process"
37
+ @fileName = 'Neo4jCypherPython'
38
+ @user = 'neo4j'
39
+ @password = 'imthepwd2oo'
40
+ @nodes = []
41
+ @edges = []
42
+ @cleanup = false
43
+ end
44
+
45
+ def render
46
+ @file = File.open("./ttdoc/#{@fileName}.py",'w')
47
+ @file.puts '# -*- coding: latin-1 -*-'
48
+ @file.puts ' '
49
+ @file.puts 'from py2neo import Graph'
50
+ @file.puts "graph = Graph('http://localhost:7474/db/data', user='#{@user}', password='#{@password}')"
51
+ @file.puts ' '
52
+ renderNodes
53
+ renderEdges
54
+ @file.close
55
+ return @file
56
+ end
57
+
58
+ def renderNodes
59
+ csv = CSV.open("./ttdoc/#{@fileName}.py.nodes.csv",'w')
60
+ csv << ['Type','Name','UUID']
61
+ nodesCSV = Set.new
62
+ nodeCmds = SortedSet.new
63
+ nodesByType = Hash.new { |type,nodes| type[nodes] = [] }
64
+ @nodes.each do |node|
65
+ nodesCSV << [node.type, node.name, node.uuid]
66
+ nodeCmds << encodeNode('MERGE','node',node,';')
67
+ nodesByType[node.type] << node
68
+ end
69
+ if @cleanup
70
+ nodesByType.keys.each do |type|
71
+ @file.puts "DROP CONSTRAINT ON (node:#{type}) ASSERT node.uuid IS UNIQUE ;"
72
+ end
73
+ @file.puts "MATCH (n) DETACH DELETE n ;"
74
+ nodesByType.keys.each do |type|
75
+ @file.puts "CREATE CONSTRAINT ON (node:#{type}) ASSERT node.uuid IS UNIQUE ;"
76
+ end
77
+ @file.puts "//--"
78
+ end
79
+ nodesCSV.each do |rec|
80
+ csv << rec
81
+ end
82
+ nodeCmds.each do |cmd|
83
+ @file.puts cmd
84
+ end
85
+ csv.close
86
+ end
87
+ # USING PERIODIC COMMIT
88
+ # LOAD CSV WITH HEADERS FROM "file:://C:/tech/Tableau/Tableau Tools/Ruby/experiments/GraphElements.nodes.csv" AS row
89
+ # CREATE (:row.Type {name: row.Name, uuid: row.UUID});
90
+
91
+ def renderEdges
92
+ csv = CSV.open("./ttdoc/#{@fileName}.edges.csv",'w')
93
+ csv << ['Source Type' , 'Source Name' , 'Source UUID' , 'Relationship', 'Target Type', 'Target Name', 'Target UUID' ]
94
+ @edges.each do |edge|
95
+ relationship = edge.relationship.upcase.gsub(/[ ]+/,'_')
96
+ csv << [edge.from.type, edge.from.name, edge.from.uuid, relationship , edge.to.type , edge.to.name, edge.to.uuid ]
97
+ @file.puts ' '
98
+ @file.puts 'query = """'
99
+ @file.puts encodeEdge('MATCH', 'source', edge.from)
100
+ @file.puts encodeEdge('MATCH', 'target', edge.to)
101
+ @file.puts "%-8s (source)-[r:%s]->(target) " % ['MERGE', edge.relationship.upcase.gsub(/[ ]+/,'_')]
102
+ @file.puts "RETURN source.name, type(r), target.name ;"
103
+ @file.puts '"""'
104
+ @file.puts 'graph.run(query)'
105
+ @file.puts "print '-',"
106
+ end
107
+ csv.close
108
+ end
109
+
110
+ def encodeNode command, varName, node, terminator=''
111
+ # "g.run('MERGE (node:CalculatedField { name:\"YTD Cost Amount\", uuid: \"ccd4f66d0c8ee09eca10ab3a1adabe35\" } ) ');"
112
+ # "g.run"
113
+ "graph.run( \"%-8s (%s:%s { name:'%s', uuid: '%s' } ) %s\")\nprint '.'," % [command, varName, node.type, node.name.gsub('"','\"'), node.uuid, terminator ]
114
+ end
115
+
116
+ def encodeEdge command, varName, node, terminator=''
117
+ "%-8s (%s:%s { uuid: '%s' } ) %s" % [command, varName, node.type, node.uuid, terminator ]
118
+ end
119
+
120
+ def to_s
121
+ "file:#{@fileName}; #nodes:#{@nodes.length}; #edges:#{@edges.length}"
122
+ end
123
+
124
+
125
+ end
126
+
127
+ end # module Util
128
+ end # module Twb
@@ -0,0 +1,46 @@
1
+ # Copyright (C) 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
+ module Twb
17
+ module Util
18
+
19
+ class DocPrep
20
+
21
+ @@dir = './ttdoc'
22
+
23
+ attr_accessor :dir
24
+
25
+ def initialize
26
+ reset
27
+ end
28
+
29
+ def reset
30
+ @dir = @@dir
31
+ init
32
+ end
33
+
34
+ def prepFile fileName
35
+ "#{@dir}/#{fileName}"
36
+ end
37
+
38
+ def init dirName
39
+ Dir.mkdir @Name
40
+ end
41
+
42
+
43
+ end # class DocPrep
44
+
45
+ end # module Util
46
+ end # module Twb
@@ -0,0 +1,108 @@
1
+ # Copyright (C) 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
+ module Twb
17
+ module Util
18
+
19
+ require 'creek'
20
+ require 'csv'
21
+
22
+ class FieldDomainLoader
23
+
24
+ @@xmlLocation = './ttdoc'
25
+
26
+ attr_reader :domains
27
+ attr_accessor :xmllocation, :workboook, :datasource, :csvOption
28
+
29
+ def initialize
30
+ reset
31
+ end
32
+
33
+ def reset
34
+ @workbok = nil
35
+ @xmllocation = @@xmlLocation
36
+ @csvOption = 'w'
37
+ initCSV @csvOption
38
+ end
39
+
40
+ def initCSV opt
41
+ @csvFile = CSV.open(@xmllocation + '/TwbFieldDomains.csv', opt )
42
+ if 'w'.eql? opt
43
+ @csvFile << ['Workbook', 'Data Source', 'Field', 'Value']
44
+ end
45
+ end
46
+
47
+ def load
48
+ path = @xmllocation + '/*.xlsx'
49
+ Dir.glob(path) do |fileName|
50
+ end
51
+ end
52
+
53
+ def loadWorkbook twb
54
+ @workbook = twb.name
55
+ dsFieldDomains = {}
56
+ dss = twb.datasources
57
+ dsFieldDomains = {}
58
+ dss.each do |ds|
59
+ fieldDomains = loadDataSource ds
60
+ dsFieldDomains[ds.uiname] = fieldDomains
61
+ end
62
+ return dsFieldDomains
63
+ end
64
+
65
+ def loadDataSource ds
66
+ @datasource = ds.uiname
67
+ fieldDomains = loadxlsx(@xmllocation + '/' + @datasource + '.xlsx')
68
+ end
69
+
70
+ def loadxlsx fileName
71
+ fieldDomains = {}
72
+ if File.file?(fileName)
73
+ xlsx = Creek::Book.new fileName
74
+ sheets = xlsx.sheets
75
+ sheets.each do |sheet|
76
+ rows = sheet.rows.to_a
77
+ if rows.count > 1
78
+ fieldValues = parseRows(rows)
79
+ unless fieldValues.empty? || fieldValues.values.first.empty?
80
+ fieldDomains[fieldValues.keys.first] = fieldValues.values.first
81
+ end
82
+ end
83
+ end
84
+ else
85
+ puts "#### ALERT #### XLSX file does not exist: #{fileName}"
86
+ end
87
+ return fieldDomains
88
+ end
89
+
90
+ def parseRows rows
91
+ fieldValues = {}
92
+ unless rows.empty?
93
+ firstRow = rows[0].to_a[0]
94
+ fieldName = firstRow[1].to_s
95
+ fieldValues[fieldName] = SortedSet.new
96
+ values = rows[1..-1]
97
+ values.each do |row|
98
+ value = row.to_a[0][1].to_s
99
+ fieldValues[fieldName] << value
100
+ end
101
+ end
102
+ return fieldValues
103
+ end
104
+
105
+ end # class FieldDomainLoader
106
+
107
+ end # module Util
108
+ end # module Twb
@@ -0,0 +1,144 @@
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 'digest/md5'
17
+
18
+ module Twb
19
+ module Util
20
+
21
+ class GML
22
+
23
+ include TabTool
24
+
25
+ @@hasher = Digest::SHA256.new
26
+
27
+ @@docfileName = './ttdoc/gml.log'
28
+
29
+ #====================================================================
30
+ @@gmlHeader = <<GMLHEADER
31
+ graph [
32
+ directed 1
33
+ rankdir "LR"
34
+ splines "line"
35
+
36
+ GMLHEADER
37
+
38
+ @@gmlFooter = "\n]"
39
+
40
+ attr_accessor :nodes, :edges, :fileName, :gmlHeader
41
+
42
+ def initialize
43
+ # @docFile = File.open(@@docfileName,'w')
44
+ # @docFile.puts "Starting up the GML process"
45
+ @gmlHeader = @@gmlHeader
46
+ @fileName = 'gmlFile'
47
+ @nodes = SortedSet.new
48
+ @edges = SortedSet.new
49
+ end
50
+
51
+ # def gmlHeader=(text)
52
+ # @gmlHeader = text
53
+ # end
54
+
55
+ # def init fileName
56
+ # # do stuff for new GML file here
57
+ # @fileName = fileName
58
+ # prepare
59
+ # end
60
+
61
+ def prepare
62
+ @nodes = SortedSet.new
63
+ @edges = SortedSet.new
64
+ end
65
+
66
+ def render
67
+ # puts "GML.render - rendering into #{@fileName}"
68
+ file = File.open(docFile("#{@fileName}.gml"),'w')
69
+ file.puts @gmlHeader
70
+ renderNodes file
71
+ renderEdges file
72
+ file.puts @@gmlFooter
73
+ file.close
74
+ return file
75
+ end
76
+
77
+ def renderNodes file
78
+ nodes = Set.new
79
+ @nodes.each do |node|
80
+ gmlID = Digest::MD5.hexdigest(node.id)
81
+ gmlName = node.name.gsub('&','&amp;').gsub('"','&quot;')
82
+ nodes << "node [\n id \"#{gmlID}\" \n label \"#{gmlName}\" \n ]"
83
+ end
84
+ nodes.each do |node|
85
+ file.puts node
86
+ end
87
+ end
88
+
89
+ def renderEdges file
90
+ edges = Set.new
91
+ @edges.each do |edge|
92
+ gmlSourceID = Digest::MD5.hexdigest(edge.from.id)
93
+ gmlTargetID = Digest::MD5.hexdigest(edge.to.id)
94
+ edges << "edge [\n source \"#{gmlSourceID}\" \n target \"#{gmlTargetID}\" \n ]"
95
+ end
96
+ edges.each do |edge|
97
+ file.puts edge
98
+ end
99
+ end
100
+
101
+ def to_s
102
+ "file:#{@fileName}; #nodes:#{@nodes.length}; #edges:#{@edges.length}"
103
+ end
104
+
105
+
106
+ end
107
+
108
+ end # module Util
109
+ end # module Twb
110
+
111
+
112
+ # node [
113
+ # id 7
114
+ # label "5"
115
+ # edgeAnchor "corners"
116
+ # labelAnchor "n"
117
+ # graphics [
118
+ # center [ x 82.0000 y 42.0000 ]
119
+ # w 16.0000
120
+ # h 16.0000
121
+ # type "rectangle"
122
+ # fill "#000000"
123
+ # ]
124
+ # ]
125
+ # edge [
126
+ # label "24"
127
+ # labelAnchor "first"
128
+ # source 7
129
+ # target 15
130
+ # graphics [
131
+ # type "line"
132
+ # arrow "last"
133
+ # Line [
134
+ # point [ x 82.0000 y 42.0000 ]
135
+ # point [ x 10.0000 y 10.0000 ]
136
+ # point [ x 100.000 y 100.000 ]
137
+ # point [ x 80.0000 y 30.0000 ]
138
+ # point [ x 120.000 y 230.000 ]
139
+ # point [ x 73.0000 y 160.000 ]
140
+ # ]
141
+ # ]
142
+ # ]
143
+
144
+
@@ -0,0 +1,73 @@
1
+ # Copyright (C) 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
+ module Twb
17
+ module Util
18
+
19
+ class GMLedge
20
+
21
+ # @from - the origin node
22
+ # @to - the destination node
23
+ # @relationship - useful for categorizing the edge
24
+ # @properties - useful for categorizing the edge
25
+ attr_reader :from, :to, :relationship
26
+ attr_accessor :properties
27
+ attr_reader :cypherCreate
28
+
29
+ # Neo4J cypher variable quote character: `
30
+
31
+ def initialize (from:, to:, relationship:, properties: {})
32
+ raise ArgumentError.new("from: parameter must be a Graphnode, is a '#{from.class}'") unless from.is_a? Twb::Util::Graphnode
33
+ raise ArgumentError.new( "to: parameter must be a Graphnode, is a '#{to.class}'" ) unless to.is_a? Twb::Util::Graphnode
34
+ @from = from
35
+ @to = to
36
+ @relationship = relationship
37
+ @properties = properties
38
+ @cypherCreate = "CREATE #{cypher_s}"
39
+ end
40
+
41
+ def eql? other
42
+ @from == other.from && @to == other.to && @relationship == other.relationship && @properties == other.properties
43
+ end
44
+
45
+ def hash
46
+ [@from.hash, @to.hash, @relationship, @properties].hash
47
+ end
48
+
49
+ def to_s
50
+ "'#{@from.name}//{@from.id}' --#{@relationship}--> '#{@to.name}//#{@to.id}'"
51
+ end
52
+
53
+ def dot
54
+ "%s -> %s" % [dotquote(from.id), dotquote(to.id)]
55
+ end
56
+
57
+ def dotquote str
58
+ ns = str.gsub(/(["])/,'\\"')
59
+ return "\"#{ns}\""
60
+ end
61
+
62
+ def cypher_s
63
+ "(%s)-[:`%s`]->(%s)" % [@from.cypherID,@relationship,@to.cypherID]
64
+ end
65
+
66
+ def gml
67
+ "%s -> %s" % [dotquote(from.id), dotquote(to.id)]
68
+ end
69
+
70
+ end
71
+
72
+ end # module Util
73
+ end # module Twb