dircat 0.1.12 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/LICENSE.txt +1 -1
  2. data/README.md +31 -6
  3. data/bin/dircat +5 -1
  4. data/bin/scat +9 -0
  5. data/dircat.gemspec +48 -15
  6. data/examples/example.rb +2 -1
  7. data/lib/dircat.rb +29 -34
  8. data/lib/dircat/cat_on_sqlite/cat_on_sqlite.rb +177 -0
  9. data/lib/dircat/cat_on_sqlite/directory_visitor.rb +106 -0
  10. data/lib/dircat/cat_on_sqlite/migration/00_create_dircat_properties.rb +13 -0
  11. data/lib/dircat/cat_on_sqlite/migration/01_create_categories.rb +12 -0
  12. data/lib/dircat/cat_on_sqlite/migration/02_create_images.rb +16 -0
  13. data/lib/dircat/cat_on_sqlite/migration/03_create_items.rb +23 -0
  14. data/lib/dircat/cat_on_sqlite/migration/04_create_taggings.rb +13 -0
  15. data/lib/dircat/cat_on_sqlite/migration/05_create_tags.rb +13 -0
  16. data/lib/dircat/cat_on_sqlite/model/category.rb +16 -0
  17. data/lib/dircat/cat_on_sqlite/model/image.rb +24 -0
  18. data/lib/dircat/cat_on_sqlite/model/item.rb +19 -0
  19. data/lib/dircat/cat_on_sqlite/model/tag.rb +58 -0
  20. data/lib/dircat/cat_on_sqlite/model/tagging.rb +9 -0
  21. data/lib/dircat/cat_on_sqlite/simple_cataloger_error.rb +9 -0
  22. data/lib/dircat/cat_on_sqlite_cli/cli_cat.rb +25 -0
  23. data/lib/dircat/cat_on_sqlite_cli/cli_server.rb +65 -0
  24. data/lib/dircat/cat_on_sqlite_cli/cmd_create.rb +56 -0
  25. data/lib/dircat/cat_on_sqlite_cli/cmd_list.rb +72 -0
  26. data/lib/dircat/cat_on_sqlite_cli/cmd_server.rb +62 -0
  27. data/lib/dircat/cat_on_sqlite_cli/cmd_update.rb +49 -0
  28. data/lib/dircat/{cat.rb → cat_on_yaml/cat_on_yaml.rb} +22 -10
  29. data/lib/dircat/cat_on_yaml/entry.rb +64 -0
  30. data/lib/dircat/{cli → cat_on_yaml_cli}/cli_dircat.rb +0 -0
  31. data/lib/dircat/{cli → cat_on_yaml_cli}/command_build.rb +53 -12
  32. data/lib/dircat/{cli → cat_on_yaml_cli}/command_diff.rb +4 -4
  33. data/lib/dircat/{cli → cat_on_yaml_cli}/command_query.rb +16 -2
  34. data/lib/dircat/config.rb +25 -0
  35. data/lib/dircat/extensions.rb +26 -0
  36. data/lib/dircat/server/helpers.rb +60 -0
  37. data/lib/dircat/server/my_static.rb +40 -0
  38. data/lib/dircat/server/web_server.rb +176 -0
  39. data/lib/dircat/version.rb +2 -1
  40. data/lib/dircat_on_sqlite.rb +31 -0
  41. data/lib/dircat_on_sqlite_cli.rb +37 -0
  42. data/lib/simple_cataloger_dm/cli.rb +32 -0
  43. data/lib/simple_cataloger_dm/core.rb +43 -0
  44. data/lib/simple_cataloger_dm/core/catalog.rb +141 -0
  45. data/lib/simple_cataloger_dm/core/directory_visitor.rb +107 -0
  46. data/lib/simple_cataloger_dm/core/extensions.rb +26 -0
  47. data/lib/simple_cataloger_dm/core/simple_cataloger_error.rb +9 -0
  48. data/lib/simple_cataloger_dm/models/category.rb +21 -0
  49. data/lib/simple_cataloger_dm/models/image.rb +30 -0
  50. data/lib/simple_cataloger_dm/models/item.rb +24 -0
  51. data/lib/simple_cataloger_dm/models/tag.rb +62 -0
  52. data/lib/simple_cataloger_dm/models/tagging.rb +11 -0
  53. data/lib/simple_cataloger_dm/server/helpers.rb +60 -0
  54. data/lib/simple_cataloger_dm/server/my_static.rb +40 -0
  55. data/lib/simple_cataloger_dm/server/web_server.rb +171 -0
  56. data/spec/dircat/cat_on_sqlite/cat_on_sqlite_spec.rb +41 -0
  57. data/spec/dircat/cat_on_sqlite_web/web_server_spec.rb +30 -0
  58. data/spec/dircat/{cat_spec.rb → cat_on_yaml/cat_on_yaml_spec.rb} +20 -14
  59. data/spec/dircat/{cli → cat_on_yaml_cli}/cli_dircat_spec.rb +0 -0
  60. data/spec/dircat/{cli → cat_on_yaml_cli}/command_build_spec.rb +4 -4
  61. data/spec/dircat/{cli → cat_on_yaml_cli}/command_diff_spec.rb +0 -0
  62. data/spec/dircat/{cli → cat_on_yaml_cli}/command_query_spec.rb +1 -1
  63. data/spec/fixtures/certified_output/cat_dir1_20120811.yaml +25 -0
  64. data/spec/fixtures/certified_output/cat_dir2_20120811.yaml +34 -0
  65. data/spec/fixtures/certified_output/{dircat1.yaml → dircat1_version_0.1.yaml} +0 -0
  66. data/spec/fixtures/certified_output/{dircat2.yaml → dircat2_version_0.1.yaml} +0 -0
  67. data/spec/fixtures/dir4/file1.txt +1 -0
  68. data/spec/fixtures/dir4/file2.txt +1 -0
  69. data/spec/fixtures/dir4/subdir/file3.txt +1 -0
  70. data/spec/fixtures/films/A-Z/A/Arancia meccanica [Stanley Kubrick][1971]/folder.jpg +0 -0
  71. data/spec/fixtures/films/A-Z/P/Plan 9 [Edward Wood][1959]/folder.jpg +0 -0
  72. data/spec/fixtures/films/Directors/[Akira Kurosawa]/[1940] Dersu Uzala [5]/folder.jpg +0 -0
  73. data/spec/fixtures/films/Directors/[Akira Kurosawa]/[1965] Barbarossa [Toshiro Mifune][4]/folder.jpg +0 -0
  74. data/spec/fixtures/films/Directors/[Federico Fellini]/[1963] 8 e mezzo [Marcello Mastroianni][Claudia Cardinale][5]/folder.jpg +0 -0
  75. data/spec/fixtures/films/Directors/[Woody Allen]/[1977] Annie Hall [Diane Keaton][5]/folder.jpg +0 -0
  76. data/{.gemtest → spec/fixtures/readme.txt} +0 -0
  77. data/spec/fixtures/tmp/test_ar.sqlite3 +0 -0
  78. data/spec/fixtures/tmp/test_ar.yml +7 -0
  79. data/spec/generate_directories_to_catalog.rb +80 -0
  80. data/spec/generate_mysql_catalog.rb +124 -0
  81. data/spec/spec_helper.rb +29 -0
  82. data/tasks/rspec.rake +0 -2
  83. metadata +390 -112
  84. data/lib/dircat/entry.rb +0 -61
  85. data/lib/dircat/extension_md5.rb +0 -25
  86. data/lib/dircat/extension_numeric.rb +0 -15
  87. data/spec/dircat/md5_spec.rb +0 -8
  88. data/spec/dircat/numeric_spec.rb +0 -13
@@ -0,0 +1,43 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # std lib
4
+ #
5
+ require 'pp'
6
+ require 'optparse'
7
+ require 'fileutils'
8
+ require 'ostruct'
9
+
10
+ #
11
+ # rubygems
12
+ #
13
+ gem "treevisitor", "0.2.2"
14
+ require 'treevisitor'
15
+
16
+ gem "optparse-command", "0.1.7"
17
+ require 'optparse-command'
18
+
19
+ require 'dm-core'
20
+ require 'dm-migrations'
21
+
22
+ #
23
+ # simple catalog
24
+ #
25
+ require 'simple_cataloger/version'
26
+ require 'simple_cataloger/simple_cataloger_error'
27
+ require 'simple_cataloger/extensions'
28
+
29
+ #
30
+ # db models
31
+ #
32
+ require 'simple_cataloger/models/item'
33
+ require 'simple_cataloger/models/tag'
34
+ require 'simple_cataloger/models/tagging'
35
+ require 'simple_cataloger/models/category'
36
+ require 'simple_cataloger/models/image'
37
+
38
+ #
39
+ # app
40
+ #
41
+ require 'simple_cataloger/directory_visitor'
42
+ require 'simple_cataloger/catalog'
43
+
@@ -0,0 +1,141 @@
1
+ # -*- coding: utf-8 -*-
2
+ module SimpleCataloger
3
+ class Catalog
4
+
5
+ attr_reader :name
6
+
7
+ def initialize(name)
8
+ @name = name
9
+
10
+ # find directory where to store catalog data
11
+ home_dir = ENV['HOME'] || ENV['APPDATA']
12
+ if RUBY_PLATFORM =~ /linux/
13
+ catalogs_dir = File.expand_path(File.join(home_dir, ".simple_cataloger"))
14
+ else
15
+ catalogs_dir = File.expand_path(File.join(home_dir, "simple_cataloger"))
16
+ end
17
+ Dir.mkdir(catalogs_dir) unless File.directory?(catalogs_dir)
18
+
19
+ db_filepath = File.join(catalogs_dir, "#{name}.sqlite3")
20
+ db_log_filepath = File.join(catalogs_dir, "#{name}.log")
21
+ @config_filepath = File.join(catalogs_dir, "#{name}.yml")
22
+ setup_db(db_filepath, db_log_filepath)
23
+ end
24
+
25
+ #
26
+ # Create a new catalog
27
+ # @param array of directories
28
+ #
29
+ def create(*catalog_roots)
30
+ if File.exist? @config_filepath
31
+ raise SimpleCatalogerError, "cannot create already existent catalog '#{@name}'"
32
+ end
33
+ @config = {
34
+ :roots => catalog_roots,
35
+ :ignore => ['sub', 'subtitles', 'images'],
36
+ :version => SimpleCataloger::VERSION
37
+ }
38
+ write_config
39
+ update
40
+ end
41
+
42
+ #
43
+ # Rebuild catalog
44
+ #
45
+ def update
46
+ unless File.exist? @config_filepath
47
+ raise "cannot update catalog #{@name}"
48
+ end
49
+ read_config
50
+ #
51
+ # migrate db
52
+ #
53
+ DataMapper.auto_migrate!
54
+ #DataMapper.auto_upgrade!
55
+
56
+ #
57
+ # Initialize categories
58
+ #
59
+ if @config[:categories]
60
+ # pp @config[:categories]
61
+ @config[:categories].each_pair do |category, tags|
62
+ # puts category
63
+ cat = Category.first_or_create(:name => category)
64
+ tags.each do |name|
65
+ tag = Tag.first_or_create(:name => name)
66
+ tag.category = cat
67
+ tag.save
68
+ end
69
+ end
70
+ end
71
+
72
+ #
73
+ # read catalog root
74
+ #
75
+ @config[:roots].each do |root|
76
+ dtw = TreeRb::DirTreeWalker.new(root)
77
+ dtw.ignore /^\./
78
+ @config[:ignore].each do |i|
79
+ dtw.ignore i
80
+ end
81
+ dtw.visit_file=true
82
+ dtw.run(DirectoryVisitor.new(root))
83
+ end
84
+
85
+ #
86
+ # cache rating tag
87
+ #
88
+ rating = Category.first(:name => "rating")
89
+ if rating
90
+ Item.all.each do |item|
91
+ t = item.tags.find { |t| t.category == rating }
92
+ if t
93
+ item.rating = t.name.to_i
94
+ item.save
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def setup_db(db_path, db_log_filepath)
101
+ #TODO: logger usabile per loggare altri eventi oltre a quelli del db
102
+ DataMapper::Logger.new(db_log_filepath, :debug)
103
+ DataMapper.setup(:default, "sqlite3:#{db_path}")
104
+ DataMapper.finalize
105
+ end
106
+
107
+ #
108
+ # array of roots
109
+ #
110
+ def roots
111
+ read_config unless @config
112
+ @config[:roots]
113
+ end
114
+
115
+ #
116
+ # update config file with tag category list and association from category to tag name,
117
+ # so to not lose the association next time the catalog is rebuilt (updated)
118
+ #
119
+ def update_categories
120
+ h = {}
121
+ Category.all.each do |category|
122
+ next if ["rating", "year", "unknown"].include?(category.name)
123
+ h[category.name] = category.tags.collect { |tag| tag.name }
124
+ end
125
+ @config[:categories] = h
126
+ end
127
+
128
+ def write_config
129
+ File.open(@config_filepath, "w") { |f| f.write @config.to_yaml }
130
+ end
131
+
132
+ private
133
+
134
+ def read_config
135
+ #noinspection RubyResolve
136
+ @config = YAML.load(File.open(@config_filepath))
137
+ end
138
+
139
+ end # class
140
+
141
+ end # module SimpleCatalog
@@ -0,0 +1,107 @@
1
+ module SimpleCataloger
2
+
3
+ #
4
+ # Find directory without subdirectories
5
+ #
6
+ class DirectoryVisitor
7
+
8
+ def initialize(catalog_root)
9
+ super
10
+ @stack = []
11
+ @files = []
12
+ @nr = 0
13
+ @catalog_root = catalog_root
14
+ end
15
+
16
+ def enter_node(pathname)
17
+ # each directory is associated to a number,
18
+ # so when exit_tree_node is called and the number @nr is not increased
19
+ # the directory doesn't have subdirectories
20
+ @nr += 1
21
+
22
+ dirname = File.basename(pathname)
23
+ tags = Tag.extract_tags(dirname)
24
+ tags = tags.concat(@stack.last.tags).uniq unless @stack.empty?
25
+ info = OpenStruct.new(:nr => @nr, :pathname => pathname, :tags => tags, :name => Tag.extract_name(dirname))
26
+
27
+ @files = []
28
+ @stack.push(info)
29
+ end
30
+
31
+ def exit_node(pathname)
32
+ info = @stack.pop
33
+
34
+ if info.nr == @nr
35
+ # this directory doesn't have subdirectories
36
+ name = info.name
37
+ tag_names = info.tags
38
+ puts name
39
+ # pp tags
40
+
41
+ # every name must be unique, two films cannot have the some name
42
+ if Item.first( :name => name )
43
+ raise "item #{name} is not unique path #{pathname}"
44
+ end
45
+
46
+ item = Item.create(
47
+ :name => name,
48
+ :added_at => File.lstat(pathname).ctime,
49
+ :path => pathname,
50
+ :path_from_catalog_root => pathname[File.dirname(@catalog_root).length..-1]
51
+ )
52
+
53
+ #
54
+ # Associate item and tags
55
+ #
56
+ unknown = Category.first_or_create(:name => "unknown")
57
+ tag_names.each do |tag_name|
58
+ tag = Tag.first(:name => tag_name)
59
+ unless tag
60
+ tag = Tag.match_category(tag_name)
61
+ unless tag
62
+ tag = Tag.first_or_create(
63
+ :name => tag_name,
64
+ :category => unknown
65
+ )
66
+ end
67
+ end
68
+ Tagging.first_or_create(:item=>item, :tag=> tag)
69
+ end
70
+
71
+ #
72
+ # Searches images
73
+ #
74
+ @files.each do |pathname|
75
+ next unless pathname.match /(jpg|jpeg)$/
76
+
77
+ image = Image.first_or_create(
78
+ :path => pathname,
79
+ :path_from_catalog_root => pathname[File.dirname(@catalog_root).length..-1]
80
+ )
81
+ item.images << image
82
+
83
+ filename = File.basename(pathname)
84
+ tag_names = Tag.extract_tags(filename)
85
+ tag_names.each do |tag_name|
86
+ tag = Tag.first(:name => tag_name)
87
+ if tag
88
+ tag.images << image
89
+ tag.save
90
+ else
91
+ puts "WARNING: tag '#{tag_name}' not found"
92
+ end
93
+ end
94
+ end
95
+ item.save
96
+ end
97
+ end
98
+
99
+
100
+ #
101
+ # called when visit leaf node
102
+ #
103
+ def visit_leaf(pathname)
104
+ @files << pathname
105
+ end
106
+ end # class
107
+ end # module SimpleCataloger
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+ module SimpleCataloger
3
+ module CoreExtensions
4
+ module Array
5
+ def extract_options!
6
+ last.is_a?(::Hash) ? pop : {}
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ class Array
13
+ include SimpleCataloger::CoreExtensions::Array
14
+ end
15
+
16
+ module URI
17
+ class << self
18
+
19
+ def parse_with_safety(uri)
20
+ parse_without_safety uri.gsub('[', '%5B').gsub(']', '%5D')
21
+ end
22
+
23
+ # alias parse_without_safety parse
24
+ # alias parse parse_with_safety
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module SimpleCataloger
4
+ class SimpleCatalogerError < StandardError
5
+ def initialize(msg)
6
+ super
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ # -*- coding: utf-8 -*-
2
+ module SimpleCataloger
3
+
4
+ class Category
5
+ include DataMapper::Resource
6
+
7
+ property :id, Serial
8
+ property :name, String
9
+
10
+ has n, :tags, :order => [:name]
11
+
12
+ def self.order_by_name
13
+ all(:order => [:name])
14
+ end
15
+
16
+ def items
17
+ tags.collect { |t| t.items }.flatten
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ module SimpleCataloger
3
+
4
+ class Image
5
+ include DataMapper::Resource
6
+
7
+ property :id, Serial
8
+ property :path, String
9
+ property :path_from_catalog_root, String
10
+
11
+ belongs_to :item, :required => false
12
+ belongs_to :tag, :required => false
13
+
14
+ def to_json(*a)
15
+ {:item => if item then
16
+ item.name
17
+ else
18
+ nil
19
+ end,
20
+ :tag => if tag then
21
+ tag.name
22
+ else
23
+ nil
24
+ end,
25
+ :src => path_from_catalog_root}.to_json(*a)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,24 @@
1
+ # -*- coding: utf-8 -*-
2
+ module SimpleCataloger
3
+
4
+ class Item
5
+ include DataMapper::Resource
6
+ property :id, Serial
7
+ property :name, String
8
+
9
+ property :added_at, Date
10
+ property :path, String
11
+ property :path_from_catalog_root, String
12
+ property :rating, Integer
13
+ property :flagged_at, Date
14
+
15
+ has n, :taggings
16
+ has n, :tags, :through => :taggings
17
+ has n, :images
18
+
19
+ def to_s
20
+ @name
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,62 @@
1
+ # -*- coding: utf-8 -*-
2
+ module SimpleCataloger
3
+
4
+ class Tag
5
+ include DataMapper::Resource
6
+
7
+ property :id, Serial
8
+ property :name, String
9
+
10
+ belongs_to :category, 'Category', :child_key => [:category_id]
11
+ has n, :taggings
12
+ has n, :items, :through => :taggings, :order => [:name]
13
+ has n, :images
14
+
15
+ ##################################################
16
+ #
17
+
18
+ RE_TAG = /\[([^\]]+)\]/
19
+
20
+ # extracts tags from directory or file name
21
+ def self.extract_tags(dir_or_file_name)
22
+ tags = []
23
+ match_data = dir_or_file_name.match(RE_TAG)
24
+ while match_data
25
+ tags << match_data[1]
26
+ match_data = dir_or_file_name.match(RE_TAG, match_data.end(0))
27
+ end
28
+ tags
29
+ end
30
+
31
+ def self.extract_name(dir_or_file_name)
32
+ dir_or_file_name.gsub(Tag::RE_TAG, '').strip
33
+ end
34
+
35
+ RE_RATING = /^\d$/
36
+ RE_YEAR = /^\d\d\d\d$/
37
+
38
+ MATCHES = [
39
+ [RE_RATING, "rating", proc { |tag_name| tag_name.to_i }],
40
+ [RE_YEAR, "year", proc { |tag_name| tag_name.to_i }]
41
+ ]
42
+
43
+ #
44
+ # decode standard tags
45
+ #
46
+ def self.match_category(tag_name)
47
+ MATCHES.each do |re, category_name, convert|
48
+ if re.match tag_name
49
+ category = Category.first_or_create(:name => category_name)
50
+ tag = Tag.first_or_create(
51
+ :name => convert.call(tag_name),
52
+ :category => category
53
+ )
54
+ return tag
55
+ end
56
+ end
57
+ nil
58
+ end
59
+
60
+ end # class Tag
61
+
62
+ end # module SimpleCatalog