forked from zalando/tech-radar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
transform.rb
executable file
·147 lines (119 loc) · 3.16 KB
/
transform.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env ruby
require "json"
require "liquid"
class Hash
def remap(hash={})
each { |k,v| yield hash, k, v }
hash
end
end
class Layout
OFFSET = {
"Data Mgt" => 0,
"Techniques; Frameworks & Tools" => 95,
"Platforms & Infrastructure" => 180,
"Languages" => 270,
}
def self.angles(start, step)
Proc.new do
Range.new(start, 90-start).step(step).to_a.shuffle +
Range.new(start + (step * 0.5).to_i, 90-start).step(step).to_a.shuffle
end
end
ANGLES = {
adopt: angles(10, 15),
trial: angles(8, 12),
assess: angles(6, 10),
hold: angles(4, 8),
}
def self.instance(quadrant, ring)
@instances ||= {}
@instances["#{quadrant}:#{ring.to_s}"] ||= Layout.new(quadrant, ring)
end
def initialize(quadrant, ring)
@offset = OFFSET[quadrant]
@angles = ANGLES[ring].call
end
def next_angle
@offset + @angles.shift.to_i
end
end
class Blip
attr_reader :name, :quadrant, :score
def initialize(name, quadrant, score)
@name, @quadrant, @score = name, quadrant, score
@moved = false
end
def moved!
@moved = true
end
def ring
return :adopt if score >= 1.5
return :trial if score >= 0
return :assess if score >= -1
return :hold
end
def radius
return (30..90).to_a.sample if ring == :adopt
# return (40 + (2 - score) * (60 / 0.5)).to_i if ring == :adopt
return (110 + (1.5 - score) * (80 / 1.5)).to_i if ring == :trial
return (210 + (0 - score) * (80 / 1)).to_i if ring == :assess
return (310 + (-1 - score) * (80 / 1)).to_i
end
def angle
Layout.instance(quadrant, ring).next_angle
end
def movement
@moved ? "t" : "c"
end
def as_json
{ name: name, pc: { r: radius, t: angle }, movement: movement }
end
end
class Radar
def initialize(path)
@blips = Radar.parse(path)
end
def [](name)
@blips[name]
end
def track_moves(previous)
@blips.each do |name, blip|
prev_ring = previous[name].ring rescue "nil"
if prev_ring != blip.ring
puts "#{name}: #{prev_ring.upcase} --> #{blip.ring.upcase}"
blip.moved!
end
end
end
# render blips as json into js template
def render
snippets = @blips.values.group_by(&:quadrant).remap do |hash, key, value|
short_key = key.scan(/\w+/).first.downcase
hash[short_key] = JSON.pretty_generate(value.sort_by(&:score).reverse.map(&:as_json))
end
template = Liquid::Template.parse(open("radar_data.js.liquid").read)
open("radar_data.js", "w") do |out|
out.puts template.render(snippets)
end
end
# parse tab-separated data (exported from google doc)
def self.parse(path)
blips = {}
open(path).each do |line|
cols = line.split("\t")
name, quadrant, score, skip = cols[0], cols[1], cols[3], cols[6]
raise "PLEASE DELETE HEADER LINE: #{path}" if score == "AVG"
next if skip == "TRUE"
next if score.nil? || score.strip.empty?
blip = Blip.new(name, quadrant, score.to_f)
blips[blip.name] = blip
end
blips
end
end
files = Dir["data/*.tsv"]
radar = Radar.new(files.pop)
previous = files.pop
radar.track_moves(Radar.new(previous)) if previous
radar.render