Graphiclious 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/README.txt +126 -0
  2. data/Rakefile +28 -0
  3. data/bin/graphiclious +3 -0
  4. data/doc/cloud.jpg +0 -0
  5. data/doc/index.html +32 -0
  6. data/doc/screenshots/default.css +44 -0
  7. data/doc/screenshots/original/blue-style-1.html +37 -0
  8. data/doc/screenshots/original/blue-style-1.jpg +0 -0
  9. data/doc/screenshots/original/cornflower.html +37 -0
  10. data/doc/screenshots/original/cornflower.jpg +0 -0
  11. data/doc/screenshots/original/custom-style.html +37 -0
  12. data/doc/screenshots/original/custom-style.jpg +0 -0
  13. data/doc/screenshots/original/sample-graph.html +37 -0
  14. data/doc/screenshots/original/sample-graph.jpg +0 -0
  15. data/doc/screenshots/original/user-interface.html +37 -0
  16. data/doc/screenshots/original/user-interface.jpg +0 -0
  17. data/doc/screenshots/thumb.html +46 -0
  18. data/doc/screenshots/thumb/t_blue-style-1.jpg +0 -0
  19. data/doc/screenshots/thumb/t_cornflower.jpg +0 -0
  20. data/doc/screenshots/thumb/t_custom-style.jpg +0 -0
  21. data/doc/screenshots/thumb/t_sample-graph.jpg +0 -0
  22. data/doc/screenshots/thumb/t_user-interface.jpg +0 -0
  23. data/doc/style.css +60 -0
  24. data/gemspec.rb +45 -0
  25. data/lib/graphiclious.rb +78 -0
  26. data/lib/graphiclious/delicious2yaml.rb +235 -0
  27. data/lib/graphiclious/dot_attributes.rb +35 -0
  28. data/lib/graphiclious/environment.rb +36 -0
  29. data/lib/graphiclious/gui.rb +260 -0
  30. data/lib/graphiclious/help.rb +22 -0
  31. data/lib/graphiclious/require_fox.rb +12 -0
  32. data/lib/graphiclious/url_graph.rb +135 -0
  33. data/lib/graphiclious/utf8.rb +19 -0
  34. data/lib/graphiclious/version.rb +8 -0
  35. data/lib/graphiclious/yaml-links2html.rb +476 -0
  36. data/options.yaml +7 -0
  37. data/styles/blue-style-1/dot-attributes.yaml +21 -0
  38. data/styles/blue-style-1/extern.gif +0 -0
  39. data/styles/blue-style-1/style.css +128 -0
  40. data/styles/cornflower/cornflower.png +0 -0
  41. data/styles/cornflower/dot-attributes.yaml +21 -0
  42. data/styles/cornflower/extern.gif +0 -0
  43. data/styles/cornflower/h3.gif +0 -0
  44. data/styles/cornflower/menuitem.png +0 -0
  45. data/styles/cornflower/menuselected.png +0 -0
  46. data/styles/cornflower/style.css +149 -0
  47. data/styles/cornflower/tr-edge.png +0 -0
  48. data/test/test_delicious2yaml.rb +97 -0
  49. data/wickie +249 -0
  50. metadata +119 -0
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ #$Id: utf8.rb,v 1.2 2005/05/05 13:37:59 wsdng Exp $
3
+
4
+ module Graphiclious
5
+ def self.utf8_as_string(aString)
6
+ return aString.unpack("U*").pack("C*")
7
+ end
8
+
9
+ def self.string_as_utf8(aString)
10
+ aString.unpack("C*").pack("U*")
11
+ end
12
+
13
+ # Convert link as provided by del.icio.us via rubilicious
14
+ # to link containing utf8 strings for description and extended
15
+ # information
16
+ def self.link_with_utf8_strings(link)
17
+ link
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+ #$Id: version.rb,v 1.1 2005/04/03 14:45:26 wsdng Exp $
3
+
4
+ module Graphiclious
5
+ def self.version
6
+ '0.1.0'
7
+ end
8
+ end
@@ -0,0 +1,476 @@
1
+ #!/usr/bin/env ruby
2
+ #$Id: yaml-links2html.rb,v 1.20 2006/03/09 22:57:52 wsdng Exp $
3
+
4
+ require 'yaml'
5
+ require 'ftools'
6
+ require 'graphiclious/environment'
7
+ require 'graphiclious/url_graph'
8
+
9
+ class YamlLinksToHtml
10
+ DEL_ICIO_US_URL = "http://del.icio.us"
11
+ STYLES_FOLDER = File.join(GRAPHICLIOUS_PATH, "styles")
12
+ MAX_STYLES_IN_CLOUD = 10
13
+ CLOUD_STYLES = (1..MAX_STYLES_IN_CLOUD).collect{ |size| 'cloud-' + size.to_s }
14
+
15
+ attr_accessor(:link_to_delicious_if_possible, :use_bundles, :include_graph_views, :build_graph_thumbs, :include_private)
16
+
17
+ # Lazy require things which some users won't use
18
+ def check_prerequisites
19
+ require 'RMagick' if @build_graph_thumbs
20
+ end
21
+
22
+ def self.guess_styles
23
+ guess_styles_within(STYLES_FOLDER)
24
+ end
25
+
26
+ def self.guess_styles_within(folder)
27
+ Dir[File.join(folder, '*', 'dot-attributes.yaml')].collect {
28
+ |filename|
29
+ File.dirname(filename)
30
+ }
31
+ end
32
+
33
+ def self.guess_users_within(folder)
34
+ Dir[File.join(folder, '*', 'delicious.yaml')].collect {
35
+ |filename|
36
+ File.dirname(filename)
37
+ }
38
+ end
39
+
40
+ def set_working_dir(new_working_dir)
41
+ @working_dir = new_working_dir
42
+ set_user(@user)
43
+ end
44
+
45
+ def set_user(user)
46
+ @user = user
47
+ @input_file = File.join(@working_dir, @user, 'delicious.yaml')
48
+ @html_output_path = File.join(@working_dir, @user, 'html')
49
+ @bundles_file = File.join(@working_dir, @user, 'bundles.yaml')
50
+ end
51
+
52
+ def set_style style
53
+ @style = style
54
+ @dot_attribut_file = "#{@style}/dot-attributes.yaml"
55
+ end
56
+
57
+ def initialize
58
+ @include_private = false
59
+ @working_dir = Dir.getwd
60
+ set_style(YamlLinksToHtml.guess_styles[0])
61
+ set_user(YamlLinksToHtml.guess_users_within(@working_dir)[0])
62
+ @link_to_delicious_if_possible = false
63
+ @use_bundles = false
64
+ @include_graph_views = false
65
+ @build_graph_thumbs = @include_graph_views
66
+ end
67
+
68
+ def copy_style_to_user
69
+ ['dot-attributes.yaml', '*.jpg', '*.png', '*.gif', '*.css'].each do
70
+ |pattern|
71
+ Dir[File.join(@style, pattern)].each do
72
+ |template_file|
73
+ write_line_on_protokol("Copy #{template_file} to #{@user}")
74
+ File.copy(template_file, File.join(@working_dir, @user))
75
+ end
76
+ end
77
+ end
78
+
79
+ def set_protocol_block(aProc)
80
+ @protocol_block = aProc
81
+ end
82
+
83
+ def write_line_on_protokol(lineString)
84
+ puts lineString
85
+ unless @protocol_block.nil?
86
+ @protocol_block.call(lineString)
87
+ end
88
+ end
89
+
90
+ # methods for html-generation
91
+
92
+ def os_file_join(dir, file)
93
+ if RUBY_PLATFORM =~ /win32/
94
+ File.join(dir, file).gsub(File::SEPARATOR, "\\")
95
+ else
96
+ File.join(dir, file)
97
+ end
98
+ end
99
+
100
+ def downscale(val)
101
+ (2 * Math.log(val)).round
102
+ end
103
+
104
+ def get_cloud_style(val, maxval)
105
+ quot = downscale(maxval)
106
+ quot = 1 if quot == 0
107
+ index = (MAX_STYLES_IN_CLOUD - 1) * downscale(val) / quot
108
+ if(index >= MAX_STYLES_IN_CLOUD)
109
+ puts "Corrected index of cloud style for #{val} from #{index} to #{MAX_STYLES_IN_CLOUD}"
110
+ index = MAX_STYLES_IN_CLOUD
111
+ end
112
+ CLOUD_STYLES[index]
113
+ end
114
+
115
+ def delicious_tag_url(tag1, *other_tags)
116
+ url = "#{DEL_ICIO_US_URL}/tag/#{tag1}"
117
+ other_tags.each { |tag| url = "#{url}+#{tag}" }
118
+ url + '?settagview=cloud'
119
+ end
120
+
121
+ def delicious_user_url(tag1, *other_tags)
122
+ url = "#{DEL_ICIO_US_URL}/#{@user}/#{tag1}"
123
+ other_tags.each { |tag| url = "#{url}+#{tag}" }
124
+ url + '?settagview=cloud'
125
+ end
126
+
127
+ def write_html_head_on(fp)
128
+ fp.puts '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">'
129
+ fp.puts '<html><head>'
130
+ fp.puts '<meta http-equiv="content-type" content="text/html; charset=UTF-8">'
131
+ fp.puts '<link rel="stylesheet" type="text/css" href="../style.css">'
132
+ fp.puts '</head><body>'
133
+ end
134
+
135
+ def write_html_tail_on(fp)
136
+ fp.puts '</body></html>'
137
+ end
138
+
139
+ def menuitem(name, id, url)
140
+ if(name == id)
141
+ "<div class=\"menuselected\">#{name}</div>"
142
+ else
143
+ "<div class=\"menuitem\"><a href=\"#{url}\">#{name}</a></div>"
144
+ end
145
+ end
146
+
147
+ def link_index_on(fp, id='none', bundles={})
148
+ fp.puts '<div class="mainmenu">'|| @checkBundles.checked?
149
+ fp.puts menuitem('cloud (all)', id, 'index.htm')
150
+ unless bundles.empty?
151
+ bundles.keys.sort.each do
152
+ |bundle|
153
+ unless bundle == 'unbundled'
154
+ fp.puts menuitem(bundle, id, "bundle_#{bundle}.htm")
155
+ end
156
+ end
157
+ unless bundles['unbundled'].empty?
158
+ fp.puts menuitem('unbundled', id, "bundle_unbundled.htm")
159
+ end
160
+ end
161
+ fp.puts '</div>'
162
+ end
163
+
164
+ def write_overview(filename, cloud_name, list, bundles, links_by_tag)
165
+ valid_list = list.reject { |tag|
166
+ links_by_tag[tag].nil? # doesn't make sense but seems to be possible
167
+ }
168
+ File.open(File.join(@html_output_path, filename), 'w') do
169
+ | fp |
170
+ write_html_head_on(fp)
171
+ fp.puts "<h1>Tag overview: #{cloud_name}</h1>"
172
+ link_index_on(fp, cloud_name, bundles)
173
+ fp.puts '<div class="main">'
174
+ fp.puts "<h3>Tags, size representing number of links</h3>"
175
+ max_num_links = valid_list.collect{ |tag| links_by_tag[tag].size }.max
176
+ valid_list.each do
177
+ |tag|
178
+ style = get_cloud_style(links_by_tag[tag].size, max_num_links)
179
+ fp.puts(UrlGraph.node_ankor(tag, style))
180
+ end
181
+ if(@include_graph_views)
182
+ name = cloud_name_to_graph_name(cloud_name)
183
+ fp.puts "<h3>Graph of tag relations</h3>"
184
+
185
+ link_desc = if(@build_graph_thumbs)
186
+ "<img src=\"#{convert_to_thumbnail_filename(name + '.gif')}\">"
187
+ else
188
+ 'view it'
189
+ end
190
+ fp.puts "<a href=\"#{name}.htm\">#{link_desc}</a>"
191
+ fp.puts '</div>'
192
+ end
193
+ end
194
+ end
195
+
196
+ def sorted_links(links)
197
+ links.sort { |l1, l2| l1['description'] <=> l2['description'] }
198
+ end
199
+
200
+ def write_link_on(link, fp, skip_tags = [])
201
+ fp.puts(UrlGraph.ankor(link['href'], link['description']))
202
+ info = link['extended']
203
+ if info
204
+ fp.puts('<br>')
205
+ fp.puts("#{info}")
206
+ end
207
+ other_tags = link['tags'].reject { |t| skip_tags.include?(t) }
208
+ unless other_tags.empty?
209
+ fp.puts('<br>')
210
+ fp.puts('More tags: ')
211
+ other_tags.each { |t|
212
+ fp.puts(UrlGraph.node_ankor(t))
213
+ }
214
+ end
215
+ end
216
+
217
+ # create an empty graph with dot attributes of chosen style
218
+ def init_tag_graph
219
+ @tag_graph = UrlGraph.new
220
+ if File.exists?(@dot_attribut_file)
221
+ @tag_graph.fixed_dot_attributes = File.open(@dot_attribut_file, 'r') { |io|
222
+ YAML.load(io)
223
+ }
224
+ else
225
+ # write out the default-attribute-file to make
226
+ # things easy to configure
227
+ File.open(@dot_attribut_file, 'w') { |out|
228
+ YAML.dump(@tag_graph.fixed_dot_attributes, out)
229
+ }
230
+ write_line_on_protokol "Wrote #{@dot_attribut_file}, change to adopt different style."
231
+ end
232
+ end
233
+
234
+ def load_links
235
+ # load yaml-file containing links as a hash
236
+ cache = File.open(@input_file, 'r') { |io|
237
+ YAML.load(io)
238
+ }
239
+ links = cache.values
240
+ unless(@include_private)
241
+ links = links.reject { |l| l['shared'] =~ /no/ }
242
+ end
243
+ @links_by_tag = Hash.new
244
+ @links_by_edge = Hash.new
245
+ links.each do
246
+ |link|
247
+ tagsOfLink = link['tags']
248
+ n = tagsOfLink.size - 1
249
+ (0..n).each do
250
+ |i|
251
+ tag = tagsOfLink[i]
252
+ unless @links_by_tag.has_key?(tag)
253
+ @links_by_tag[tag] = Array.new
254
+ @tag_graph.add_vertex(tag)
255
+ end
256
+ @links_by_tag[tag].push(link)
257
+ (i+1..n).each do
258
+ |j|
259
+ otherTag = tagsOfLink[j]
260
+ # write_line_on_protokol "\t#{tag}<->#{otherTag}"
261
+ @tag_graph.add_edge(tag, otherTag)
262
+ edge_name = UrlGraph.edge_name(tag, otherTag)
263
+ unless @links_by_edge.has_key?(edge_name)
264
+ @links_by_edge[edge_name] = Array.new
265
+ end
266
+ @links_by_edge[edge_name].push(link)
267
+ end
268
+ end
269
+ end
270
+
271
+ # load bundles file
272
+ @bundles = {}
273
+ if @use_bundles
274
+ @bundles = File.open(@bundles_file, 'r') { |io|
275
+ YAML.load(io)
276
+ }
277
+ set_of_unbundled_tags = {}
278
+ @tag_graph.each { |tag| set_of_unbundled_tags[tag] = tag }
279
+ @bundles.keys.each { |bundle|
280
+ @bundles[bundle].each { |tag|
281
+ set_of_unbundled_tags.delete(tag)
282
+ }
283
+ }
284
+ @bundles['unbundled'] = set_of_unbundled_tags.values.sort
285
+ end
286
+ end
287
+
288
+ def fatal(msg)
289
+ write_line_on_protokol(msg)
290
+ exit(1)
291
+ end
292
+
293
+ def thumbnail(filename)
294
+ thumb_name = convert_to_thumbnail_filename(filename)
295
+ write_line_on_protokol("Writing thumbnail #{thumb_name} for #{filename}")
296
+ img = Magick::Image.read(filename)[0]
297
+ thumb = img.thumbnail(100, 100)
298
+ thumb.write(thumb_name)
299
+ end
300
+
301
+ # name.extension will result in name.thumb.extension
302
+ def convert_to_thumbnail_filename(filename)
303
+ parts = filename.split('.')
304
+ ext = parts.pop
305
+ parts[0..-1].push('thumb').push(ext).join('.')
306
+ end
307
+
308
+ def do_graphviz(tag_graph, cloud_name)
309
+ return unless @include_graph_views
310
+ name = cloud_name_to_graph_name(cloud_name)
311
+ # Write Graphviz-File
312
+ tmpDotFile = 'graph.dot~'
313
+ tag_graph.print_dotted_on_file(tmpDotFile)
314
+ write_line_on_protokol "Wrote #{tmpDotFile} for temporary use, feel free to delete later."
315
+
316
+ # Use Graphviz to create HTML with client-side imagemap
317
+ os_gif_file = os_file_join(@html_output_path, "#{name}.gif")
318
+ dot_command = "dot -Tgif #{tmpDotFile} -o #{os_gif_file}"
319
+ system(dot_command) || fatal("Error can't execute #{dot_command}\nPlease check your graphviz installation")
320
+ write_line_on_protokol "Wrote #{os_gif_file}."
321
+ graph_htm = File.join(@html_output_path, "#{name}.htm")
322
+ os_graph_htm = os_file_join(@html_output_path, "#{name}.htm")
323
+ File.open(graph_htm, 'w') { |fp|
324
+ write_html_head_on(fp)
325
+ fp.puts("<h1>Tag graph: #{cloud_name} </h1>")
326
+ link_index_on(fp, '', @bundles)
327
+ fp.puts '<div class="main">'
328
+ fp.puts '<IMG SRC="' + "#{name}.gif" + '" USEMAP=#UrlGraph>'
329
+ fp.puts '</div>'
330
+ }
331
+ dot_command = "dot -Tcmapx #{tmpDotFile} -o graph.htm~"
332
+ system(dot_command) || fatal("Failed system call: #{dot_command}")
333
+ File.open(graph_htm, 'a') { |fp|
334
+ File.open('graph.htm~'){ |fp2| fp2.each_line { |line| fp.puts(line) }}
335
+ write_html_tail_on(fp)
336
+ }
337
+ write_line_on_protokol "Wrote #{os_graph_htm} (which uses #{os_gif_file})."
338
+
339
+ return unless @build_graph_thumbs
340
+ gif_file = File.join(@html_output_path, "#{name}.gif")
341
+ thumbnail(gif_file)
342
+ end
343
+
344
+ def cloud_name_to_graph_name(cloud)
345
+ UrlGraph.tag2file(cloud) + '__go'
346
+ end
347
+
348
+ def write_all_overviews
349
+ # Create a file containing a cloud of all tags
350
+ list = @links_by_tag.keys.sort
351
+ filename = 'index.htm'
352
+ cloud_name = 'cloud (all)'
353
+ write_overview(filename, cloud_name, list, @bundles, @links_by_tag)
354
+ do_graphviz(@tag_graph, cloud_name)
355
+
356
+ # The same for all bundles
357
+ @bundles.keys.each do
358
+ | bundle |
359
+ list = @bundles[bundle]
360
+ filename = "bundle_#{bundle}.htm"
361
+ cloud_name = bundle
362
+ write_overview(filename, cloud_name, list, @bundles, @links_by_tag)
363
+ @tag_graph.filter_set = Set.new(list)
364
+ do_graphviz(@tag_graph, cloud_name)
365
+ @tag_graph.filter_set = nil
366
+ end
367
+ end
368
+
369
+ # Create one Html-File for each tag
370
+ def write_single_tag_files
371
+ @links_by_tag.keys.sort.each do
372
+ |tag|
373
+ File.open(File.join(@html_output_path, UrlGraph.node_url(tag)), 'w') do
374
+ |fp|
375
+ write_html_head_on(fp)
376
+ fp.puts "<h1>Tag: #{tag}</h1>"
377
+ link_index_on(fp, 'none', @bundles)
378
+ fp.puts '<div class="main">'
379
+ fp.puts "<h3>Subcategories for tag #{tag}</h3>"
380
+ subcloud = @tag_graph.adjacent_vertices(tag).sort
381
+ max_num_links = subcloud.collect{ |n| @links_by_edge[UrlGraph.edge_name(tag, n)].size }.max
382
+ subcloud.each do
383
+ |neighbor|
384
+ edge_name = UrlGraph.edge_name(tag, neighbor)
385
+ edgePage = UrlGraph.edge_url(tag, neighbor)
386
+ style = get_cloud_style(@links_by_edge[edge_name].size, max_num_links)
387
+ fp.puts(UrlGraph.intern_ankor(UrlGraph.edge_url(tag, neighbor), neighbor, style))
388
+ end
389
+ list = @links_by_tag[tag].select { |l| l['tags'].size == 1 }
390
+ unless list.empty?
391
+ fp.puts("<h3>Links with the single tag #{tag}</h3>")
392
+ fp.puts('<ul>')
393
+ sorted_links(list).each do
394
+ |link|
395
+ fp.puts('<li>')
396
+ write_link_on(link, fp, [tag])
397
+ fp.puts('</li>')
398
+ end
399
+ fp.puts('</ul>')
400
+ end
401
+ if @link_to_delicious_if_possible
402
+ fp.puts("<h3>#{UrlGraph.ankor(DEL_ICIO_US_URL, 'del.icio.us')} links with tag #{tag}</h3>")
403
+ fp.puts(UrlGraph.ankor(delicious_user_url(tag), "#{@user}"))
404
+ fp.puts(UrlGraph.ankor(delicious_tag_url(tag), "all"))
405
+ end
406
+ fp.puts '</div>'
407
+ write_html_tail_on(fp)
408
+ end
409
+ end
410
+ write_line_on_protokol "Wrote one html-file for each tag to directory #{@html_output_path}."
411
+ end
412
+
413
+ def write_double_tag_files
414
+ # Create one Html-File for each edge between two tags
415
+ @tag_graph.each_edge do
416
+ |tag1,tag2|
417
+ edge_name = UrlGraph.edge_name(tag1, tag2)
418
+ edge_url = UrlGraph.edge_url(tag1, tag2)
419
+ File.open(File.join(@html_output_path, edge_url), 'w') do
420
+ |fp|
421
+ write_html_head_on(fp)
422
+ fp.puts "<h1>Tags: #{tag1} AND #{tag2}</h1>"
423
+ link_index_on(fp, 'none', @bundles)
424
+ fp.puts '<div class="main">'
425
+ ankor1 = UrlGraph.node_ankor(tag1)
426
+ ankor2 = UrlGraph.node_ankor(tag2)
427
+ fp.puts "<h3>Links for tag combination #{ankor1}/#{ankor2}</h3>"
428
+ fp.puts '<ul>'
429
+ sorted_links(@links_by_edge[edge_name]).each do
430
+ |link|
431
+ fp.puts('<li>')
432
+ write_link_on(link, fp, [tag1, tag2])
433
+ fp.puts('<br></li>')
434
+ end
435
+ fp.puts '</ul>'
436
+ if @link_to_delicious_if_possible
437
+ fp.puts("<h3>#{UrlGraph.ankor(DEL_ICIO_US_URL, 'del.icio.us')} links with tag combination #{ankor1}/#{ankor2}</h3>")
438
+ fp.puts(UrlGraph.ankor(delicious_user_url(tag1, tag2), @user))
439
+ fp.puts(UrlGraph.ankor(delicious_tag_url(tag1, tag2), 'all'))
440
+ end
441
+ fp.puts '</div>'
442
+ write_html_tail_on(fp)
443
+ end
444
+ end
445
+ write_line_on_protokol "Wrote one html-file for each combination of two tags to directory #{@html_output_path}."
446
+ end
447
+
448
+ # make all run-variables garbage collectable
449
+ def reset_build_state
450
+ @tag_graph = nil
451
+ @links_by_tag = nil
452
+ @links_by_edge = nil
453
+ @bundles = nil
454
+ end
455
+
456
+ # run the complete batch
457
+ def run
458
+ unless File.exists?(@input_file)
459
+ raise "Missing input file #{@input_file}"
460
+ end
461
+ check_prerequisites
462
+ Dir.mkdir(@html_output_path) unless File.directory?(@html_output_path)
463
+ copy_style_to_user unless @user == @style
464
+ init_tag_graph
465
+ load_links
466
+ write_all_overviews
467
+ write_single_tag_files
468
+ write_double_tag_files
469
+ reset_build_state
470
+ write_line_on_protokol "Finished."
471
+ end
472
+ end
473
+
474
+ if $0 == __FILE__
475
+ YamlLinksToHtml.new.run
476
+ end