Skip to content

Commit

Permalink
photo-index initial checkin
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-wallis committed Apr 15, 2018
1 parent 9187895 commit 0127f13
Show file tree
Hide file tree
Showing 19 changed files with 5,856 additions and 0 deletions.
5 changes: 5 additions & 0 deletions photo_index/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Photo Index

An index of photos. It will contain metadata about the images.

Applications may be written to use this metadata,e.g.for finding multiple copies of the same image.
7 changes: 7 additions & 0 deletions photo_index/bin/add_files.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'photo_index'

options = PhotoIndex::Options.new(:index_in, :index_out).config
photolib = PhotoIndex::Index.new
photolib.load_from_xml(options.index_in)
photolib.add_from_stdin
photolib.save_to_xml(options.index_out)
10 changes: 10 additions & 0 deletions photo_index/bin/collect_metadata.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'photo_index'

options = PhotoIndex::Options.new(:index_in, :index_out).config
photolib = PhotoIndex::Index.new
photolib.load_from_xml(options.index_in)
photolib.collect_metadata
photolib.save_to_xml(options.index_out)



6 changes: 6 additions & 0 deletions photo_index/bin/copy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require 'photo_index'

options = PhotoIndex::Options.new(:index_in, :index_out).config
photolib = PhotoIndex::Index.new
photolib.load_from_xml(options.index_in)
photolib.save_to_xml(options.index_out)
6 changes: 6 additions & 0 deletions photo_index/bin/show-possible-dups.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require 'photo_index'

options = PhotoIndex::Options.new(:index_in).config
photolib = PhotoIndex::Index.new
photolib.load_from_xml(options.index_in)
photolib.show_possible_dups
5 changes: 5 additions & 0 deletions photo_index/lib/photo_index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require_relative 'photo_index/options'
require_relative 'photo_index/photo'
require_relative 'photo_index/index'
require_relative 'photo_index/show_possible_dups'
require_relative 'photo_index/photo/metadata'
46 changes: 46 additions & 0 deletions photo_index/lib/photo_index/index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

require 'nokogiri'

module PhotoIndex
class Index
def initialize
@photos = {} # key is absolute filename
end
def add(photo)
@photos[photo.filename] = photo
end
def add_from_stdin
ARGF.each {|line|
file = File.absolute_path(line.strip)
unless @photos.has_key?(file)
add(Photo.new(file))
end
}
end
def load_from_xml(fname)
if File.exist?(fname)
doc = File.open(fname) { |f| Nokogiri::XML(f) }
doc.css('//photo').each { |ph|
add(Photo::from_xml(ph))
#puts ph['filename']
}
end
end
def save_to_xml(fname)
File.open(fname, "w") {|f| f.write(to_xml)}
end
def to_xml
builder = Nokogiri::XML::Builder.new {|xml|
xml.photolib {|xml|
@photos.keys.sort.each {|k|
@photos[k].to_xml(xml)
}
}
}
builder.to_xml
end
def collect_metadata
@photos.values.each {|p| p.collect_metadata }
end
end
end
41 changes: 41 additions & 0 deletions photo_index/lib/photo_index/options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'optparse'
require 'ostruct'

module PhotoIndex
class Options
attr_reader :config

# options is an array of symbols.
# Each symbol must be the name of an instance method for
# processing the option. (e.g. :index_in)
def initialize(*options)
@config = OpenStruct.new
opt_parser = OptionParser.new do |opts|
options.each {|opt|
method(opt).call(opts, @config)
}

# No argument, shows at tail. This will print an options summary.
# Try it and see!
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
end
opt_parser.parse!
end
def index_in(opts, config)
config.index_in = "photolib.xml" # default value
opts.on("--index-in FILENAME", "Input XML") {|filename|
config.index_in = filename
}
end
def index_out(opts, config)
config.index_out = "photolib.xml" # default value
opts.on("--index-out FILENAME", "Output XML") {|filename|
config.index_out = filename
}
end
end
end

36 changes: 36 additions & 0 deletions photo_index/lib/photo_index/photo.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

module PhotoIndex
class Photo
attr_reader :filename
def initialize(filename)
@filename = filename
@file_exists = true
@metadata = {}
end
def self.from_xml(node)
new(node['filename']).metadata_from_xml(node)
end
def metadata_from_xml(node)
filestat = node.at_css('filestat')
if filestat
@metadata[:filestat] = MetaData::FileStat.from_xml(filestat)
end
self
end

def to_xml(xml)
xml.photo(:filename => filename) {|xml|
if @metadata[:filestat]
xml.filestat {|xml| @metadata[:filestat].to_xml(xml)}
end
}
end
def collect_metadata
begin
@metadata[:filestat] = MetaData::FileStat.collect_metadata(filename)
rescue Errno::ENOENT
@file_exists = false
end
end
end
end
36 changes: 36 additions & 0 deletions photo_index/lib/photo_index/photo/metadata.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require 'time'
# There are various tests that can be done on image files:
module PhotoIndex
module MetaData
class FileStat
attr_reader :atime, :ctime, :mtime
def initialize(atime, ctime,mtime)
@atime = atime
@ctime = ctime
@mtime = mtime
end
def to_xml(xml)
xml.atime(atime.to_s)
xml.ctime(ctime.to_s)
xml.mtime(mtime.to_s)
end
def self.collect_metadata(fname)
stat = File::Stat.new(fname)
new(
stat.atime,
stat.ctime,
stat.mtime
)
end
def self.from_xml(filestat)
new(
filestat.at_css('atime').text,
filestat.at_css('ctime').text,
filestat.at_css('mtime').text
)
end
end
class Exif
end
end
end
22 changes: 22 additions & 0 deletions photo_index/lib/photo_index/show_possible_dups.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require 'pp'
module PhotoIndex
class Index
def show_possible_dups
ind = index_by_basename
dirs_to_photos = Hash.new{|h, k| h[k] = [] }
ind.values.find_all {|v| v.size > 1 }.each {|v|
dirs_to_photos[v.map{|p| File.dirname(p.filename)}].concat(v)
}
pp dirs_to_photos.keys

end
def index_by_basename
res = Hash.new {|h,k| h[k] = []}
@photos.values.each {|p|
res[File.basename(p.filename)] << p
}
res
end
end
end

1 change: 1 addition & 0 deletions photo_index/test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
photos
23 changes: 23 additions & 0 deletions photo_index/test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.PHONY: all

.PHONY: inout
.DEFAULT_GOAL := all
RUBY := ruby -I ../lib

XML1 := inout/a.xml
XML2 := inout/b.xml

inout:
find /home/matt/Pictures -iname '*.jpg' | $(RUBY) ../bin/add_files.rb --index-in basic-copy/input.xml --index-out $(XML1)
$(RUBY) ../bin/copy.rb --index-in $(XML1) --index-out $(XML2)
diff $(XML1) $(XML2)

all: inout
$(RUBY) all-tests.rb

# Run a single test (e.g. basic-network.rb) like this:
#
# make test=basic-network one
#
one:
$(RUBY) $(test).rb
4 changes: 4 additions & 0 deletions photo_index/test/all-tests.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
require 'test/unit'

# All test cases will be run automatically just by requiring them here:
require_relative 'basic-copy'
16 changes: 16 additions & 0 deletions photo_index/test/basic-copy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'test/unit'
require 'photo_index'

class BasicCopy < Test::Unit::TestCase
def setup
#puts "setup"
end
def teardown
#puts "teardown"
end
def test1
photolib = PhotoIndex::Index.new
photolib.load_from_xml('basic-copy/input.xml')
photolib.save_to_xml('basic-copy/output.xml')
end
end
1 change: 1 addition & 0 deletions photo_index/test/basic-copy/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
output.xml
Loading

0 comments on commit 0127f13

Please sign in to comment.