flickr_collager 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 723b3acc6258a9217c868acbc4117f68547e95d21983b5b4626662c0a9374656
4
+ data.tar.gz: 62383cf3bbe4e0c023e3d4a5c4b2e638b9a21bb6cad6ca82009ef47378a78995
5
+ SHA512:
6
+ metadata.gz: b2b24e7f2e8fcebe702e20830baa7b16b33335bb5164fb20c7c6435387c8c5fc70131b9917b1591006f8580b213e2191b3ee944a70c2b9faaa45ebd94729541e
7
+ data.tar.gz: ecbdb075230314daa83599e7b5b22d0f521119ac45c5d41c4475c60f82167341cf2694748df9b0081e562d5a723daa600a037b36bc749d1f53325da76c1e1dea
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,21 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ flickr_collager (0.0.1)
5
+ minitest (= 5.9.0)
6
+ rake (>= 12.3.3)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ minitest (5.9.0)
12
+ rake (13.0.1)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ flickr_collager!
19
+
20
+ BUNDLED WITH
21
+ 2.1.4
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
14
+
@@ -0,0 +1,32 @@
1
+ #Flickr Collager
2
+
3
+ ## Flickr image search and Collage Creator.
4
+
5
+
6
+ ## To build
7
+ There is a rakefile for all the tests, the list of all Rake tasks available can be
8
+ seen by :
9
+
10
+ ```rake -T```
11
+
12
+ Also the project can be made into a gem
13
+
14
+ by doing a ```bundle``` and then building the gem by executing ```gem build flickr_collager.gemspec```
15
+
16
+ Once the gem in built it can be installed by :
17
+
18
+ ```gem install flickr_collager-0.0.1.gem```
19
+
20
+ ## Prerequisites :
21
+
22
+ Before Installing the gem make sure Imagemagick is installed.
23
+
24
+ ```brew install imagemagick```
25
+
26
+ Once the gem is installed into installs a executable called ```collage``` which can used as follows :
27
+
28
+ ## Usage :
29
+
30
+ ```collage -w pen,pencil,rose,women,brizil,everest,sunshine --out-file collage.png```
31
+
32
+ The project compensates for missing search terms if less than 10 from a dictionary.
@@ -0,0 +1,45 @@
1
+ namespace :app do
2
+ namespace :test do
3
+ task :collage do
4
+ desc 'Tests collage'
5
+ system 'ruby spec/test_collage.rb'
6
+ end
7
+
8
+ task :flickr do
9
+ desc 'Test Flickr Search API'
10
+ system 'ruby spec/test_flickr_search.rb'
11
+ end
12
+
13
+ task :magick do
14
+ desc 'Test ImageMagick interface API'
15
+ system 'ruby spec/test_magick_collage.rb'
16
+ end
17
+ end
18
+
19
+ namespace :gem do
20
+ task :build do
21
+ desc "Build The Flickr Collager 'gem'."
22
+ system 'gem build flickr_collager.gemspec'
23
+ end
24
+ end
25
+
26
+ task all: [
27
+ 'app:test:collage', 'app:test:flickr',
28
+ 'app:test:magick', 'app:gem:build'
29
+ ]
30
+ end
31
+
32
+ desc 'All Tasks'
33
+ task default: 'app:all'
34
+
35
+ desc 'Test Collage'
36
+ task 'test:collage' => 'app:test:collage'
37
+
38
+ desc 'Test Flickr Search API'
39
+ task 'test:flickr' => 'app:test:flickr'
40
+
41
+ desc 'Test ImageMagick inetrface API'
42
+ task 'test:magick' => 'app:test:magick'
43
+
44
+ desc "Build the Flickr Collager 'gem'"
45
+ task 'gem:build' => 'app:gem:build'
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require_relative '../loader'
4
+
5
+ class CollageParser
6
+ def self.parse(params)
7
+ args = {}
8
+ OptionParser.new do |opts|
9
+ opts.banner = 'Usage: collage -w word1,word2..wordN -o outfile.png'
10
+ opts.on('-w', '--words ', Array, 'Flickr Search Words') do |w|
11
+ args[:words] = w
12
+ end
13
+
14
+ opts.on('-o', '--out-file ', String, 'Output File') do |f|
15
+ args[:outfile] = f
16
+ end
17
+
18
+ opts.on_tail('-h', '--help', 'Show this message') do
19
+ puts opts
20
+ exit
21
+ end
22
+ end.parse! params
23
+ args
24
+ end
25
+ end
26
+
27
+ args = if ARGV.empty?
28
+ %w(--help)
29
+ else
30
+ ARGV
31
+ end
32
+
33
+ options = CollageParser.parse args
34
+ collage = Collage.new options[:words]
35
+ collage.download_images.build options[:outfile]
36
+
37
+ if RUBY_PLATFORM.downcase =~ /darwin/
38
+ fork { system "open #{options[:outfile]}" }
39
+ elsif RUBY_PLATFORM.downcase =~ /linux/
40
+ fork { system "eog #{options[:outfile]}" }
41
+ elsif RUBY_PLATFORM.downcase =~ /mswin/
42
+ puts 'Dude Seriously, Windows?'
43
+ end
@@ -0,0 +1,22 @@
1
+ #Collage Search app config options.
2
+
3
+ options:
4
+ MIN_WORD_COUNT: 10
5
+ DICT_PATH: '/usr/share/dict/words'
6
+
7
+ flickr:
8
+ API_KEY: '2ee12fb262a51e63f4e3c04f092bec55'
9
+ API_SECRET: '6002a6a1481c6fe0'
10
+ API_URL: 'https://api.flickr.com/services/rest/?'
11
+ API_FORMAT: 'json'
12
+ API_METHOD: 'flickr.photos.search'
13
+ API_SORT: 'relevance'
14
+ TEMP_DIR: '/tmp/collage'
15
+
16
+ imagemagick:
17
+ ROWS: 2
18
+ COLUMNS: 5
19
+ BG_COLOR: 'white'
20
+ BORDR_SIZE: '+5+5'
21
+ IMG_SIZE: 1000
22
+
@@ -0,0 +1,22 @@
1
+ require_relative 'lib/string_refinements'
2
+ require_relative 'lib/app_config'
3
+ require 'fileutils'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'flickr_collager'
7
+ gem.version = AppConfig::Version::STRING
8
+ gem.authors = ['Pankaj Doharey']
9
+ gem.email = ['pankajdoharey@gmail.com']
10
+ gem.licenses = ['WTFPL']
11
+ gem.description = 'Collage Builder'
12
+ gem.summary = 'Flickr based Collage builder'
13
+ gem.homepage = 'http://selfdotsend.com'
14
+
15
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+ gem.add_dependency 'rake', '>= 12.3.3'
20
+ gem.add_dependency 'minitest', '5.9.0'
21
+ gem.post_install_message = AppConfig::PostInstall.message
22
+ end
@@ -0,0 +1,28 @@
1
+ # Main Container module to namespace the application and its various parts
2
+ # are submodules inside this module.
3
+ module AppConfig
4
+ using Camelizer
5
+
6
+ def self.included(klass)
7
+ config = YAML.load_file(path_finder)
8
+
9
+ Dir.glob File.expand_path('../app_config/*.rb', __FILE__) do |file|
10
+ require file
11
+
12
+ # Set app configration from config.yaml
13
+ file_basename = File.basename(file, '.rb')
14
+ (klass.const_get file_basename.camelize).options config
15
+ end
16
+ end
17
+
18
+ def self.path_finder
19
+ if File.exist?('config.yaml')
20
+ 'config.yaml'
21
+ else
22
+ File.expand_path('~/.collage/config.yaml', __FILE__)
23
+ end
24
+ end
25
+ end
26
+
27
+ require_relative 'version'
28
+ require_relative 'post_install'
@@ -0,0 +1,74 @@
1
+ module AppConfig
2
+ module FlickrSearch
3
+ def self.options(config)
4
+ config['flickr'].each do |key, val|
5
+ const_set(key, val)
6
+ end
7
+ end
8
+
9
+ def search_and_save_flickr_image(text)
10
+ uri = URI(search_flickr_for(text))
11
+ download_and_write_to_file uri
12
+ end
13
+
14
+ def search_flickr_for(search_term)
15
+ response_body = build_query_for(search_term).read_body
16
+ response_data = response_body.to_s.match(/\[([^\}]+)\}/)[0]
17
+ .sub(/^\[/, '')
18
+ json_response = JSON.parse response_data
19
+ construct_image_url_from json_response
20
+ rescue JSON::ParserError
21
+ puts "\nJSON Parser Error Detected. Retrying ..."
22
+ search_flickr_for(new_word)
23
+ end
24
+
25
+ def image_list
26
+ Dir[TEMP_DIR + '/*']
27
+ end
28
+
29
+ private
30
+
31
+ def download_and_write_to_file(uri)
32
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
33
+ filepath = File.join(TEMP_DIR, File.basename(uri.path))
34
+ open(filepath, 'wb') do |file|
35
+ file.write http.get(uri).read_body
36
+ file.close
37
+ end
38
+ end
39
+ rescue URI::InvalidURIError
40
+ puts 'URI::InvalidURIError Detected'
41
+ end
42
+
43
+ def build_query_for(word)
44
+ uri = uri_for word
45
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
46
+ request = Net::HTTP::Get.new uri
47
+ http.request request
48
+ end
49
+ end
50
+
51
+ def uri_for(search_text)
52
+ URI(
53
+ uri_builder(
54
+ url: API_URL, format: API_FORMAT, sort: API_SORT,
55
+ method: API_METHOD, text: search_text,
56
+ tag_mode: :all, api_key: API_KEY
57
+ )
58
+ )
59
+ end
60
+
61
+ def uri_builder(params = {})
62
+ url = params[:url]
63
+ params.reject! { |k, _| k == :url }
64
+ url + params.each_pair.map { |k, v| "#{k}=#{v}" }.join('&')
65
+ end
66
+
67
+ def construct_image_url_from(json)
68
+ <<-URL.gsub(/\s+/, '').strip
69
+ https://farm#{json['farm']}.staticflickr.com/
70
+ #{json['server']}/#{json['id']}_#{json['secret']}.jpg
71
+ URL
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,30 @@
1
+ module AppConfig
2
+ module MagickCollage
3
+ def self.options(config)
4
+ config['imagemagick'].each do |key, val|
5
+ const_set(key, val)
6
+ end
7
+ end
8
+
9
+ def build(file = 'collage.jpg')
10
+ crop_images
11
+ system <<-COMMAND.gsub(/\s+|\n/, ' ')
12
+ montage #{::AppConfig::FlickrSearch::TEMP_DIR}/cropped*.png
13
+ -geometry #{IMG_SIZE / COLUMNS}x#{IMG_SIZE / COLUMNS}#{BORDR_SIZE}
14
+ background #{BG_COLOR}
15
+ -tile #{COLUMNS}x#{ROWS} #{file}
16
+ 2>/dev/null
17
+ COMMAND
18
+ end
19
+
20
+ def crop_images
21
+ system <<-COMMAND.gsub(/\s+|\n/, ' ')
22
+ convert
23
+ #{::AppConfig::FlickrSearch::TEMP_DIR}/*.jpg -gamma .45455
24
+ -crop #{IMG_SIZE / COLUMNS}x#{IMG_SIZE / COLUMNS}+0+0 +repage
25
+ -gamma 2.2 #{::AppConfig::FlickrSearch::TEMP_DIR}/cropped.png
26
+ 2>/dev/null
27
+ COMMAND
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ module AppConfig
2
+ module Options
3
+ def self.options(config)
4
+ config['options'].each do |key, val|
5
+ const_set(key, val)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,60 @@
1
+ # Collage is the main class that kicks off various code for searching,
2
+ # downloading and stitching together images to produce collage
3
+ class Collage
4
+ attr_reader :search_words
5
+
6
+ def initialize(words)
7
+ create_and_clean_temp_directory
8
+ @search_words = fill_missing words
9
+ end
10
+
11
+ def download_images
12
+ Thread.new { animate_cursor }.run
13
+ @search_words.each_slice(COLUMNS) do |slice|
14
+ slice.map do |word|
15
+ Thread.new do
16
+ search_and_save_flickr_image word
17
+ print "**"
18
+ end.run
19
+ end.each(&:join)
20
+ end
21
+ self
22
+ end
23
+
24
+ private
25
+
26
+ def create_and_clean_temp_directory
27
+ FileUtils.remove_dir(TEMP_DIR) if File.directory?(TEMP_DIR)
28
+ FileUtils.mkdir(TEMP_DIR)
29
+ end
30
+
31
+ def fill_missing(words)
32
+ if words.count < MIN_WORD_COUNT
33
+ (MIN_WORD_COUNT - words.count).times { words << new_word }
34
+ end
35
+ words
36
+ end
37
+
38
+ def new_word
39
+ word = DICTIONARY.sample
40
+ if ascii?(word)
41
+ word
42
+ else
43
+ puts "\nNon-Ascii Word Detected : #{word}. Retrying ..."
44
+ new_word
45
+ end
46
+ end
47
+
48
+ def ascii?(word)
49
+ word.force_encoding('UTF-8').ascii_only?
50
+ end
51
+
52
+ def animate_cursor
53
+ char = %w(| / - \\).cycle
54
+ loop do
55
+ print "\b"
56
+ print "#{char.next}"
57
+ sleep 0.05
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ module AppConfig
2
+ module Dictionary
3
+ def self.included(klass)
4
+ klass.const_set(:DICTIONARY, File.read(klass::DICT_PATH).split("\n"))
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ module AppConfig
2
+ module PostInstall
3
+ def self.message
4
+ unless File.directory?(File.expand_path('~/.collage', __FILE__))
5
+ FileUtils.mkdir(File.expand_path('~/.collage/'))
6
+ FileUtils.cp('config.yaml', File.expand_path('~/.collage/config.yaml'))
7
+ puts <<-MESSAGE
8
+ A basic Yaml Configuration file has been copied to ~/.collage/config.yaml
9
+ you modify that to change some setting.
10
+ MESSAGE
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ module Camelizer
2
+ refine String do
3
+ def camelize
4
+ split('_').map(&:capitalize).join
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module AppConfig
2
+ module Version
3
+ STRING = '0.0.1'.freeze
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ ['../lib', '../lib/app_config'].each do |dir|
2
+ $LOAD_PATH << File.expand_path(dir, __FILE__)
3
+ end
4
+
5
+ require 'yaml'
6
+ require 'net/http'
7
+ require 'json'
8
+ require 'fileutils'
9
+ require 'string_refinements'
10
+ require 'app_config'
11
+ require 'dictionary'
12
+ require 'collage'
13
+
14
+ class Collage
15
+ include AppConfig
16
+ include Options
17
+ include Dictionary
18
+ include FlickrSearch
19
+ include MagickCollage
20
+ end
@@ -0,0 +1,22 @@
1
+ require_relative '../loader'
2
+ require 'minitest/autorun'
3
+ require 'minitest/spec'
4
+ require 'minitest/pride' # Colorful tests
5
+
6
+ def sample_10_words
7
+ ARGV[0..9] = 'cat', 'dog', 'crow', 'cow', 'elephant',
8
+ 'tree_snake', 'women', 'red', 'tower', 'beach'
9
+ end
10
+
11
+ def sample_8_words
12
+ ARGV[0..7] = 'cat', 'dog', 'crow', 'cow', 'elephant',
13
+ 'tree_snake', 'women', 'red'
14
+ end
15
+
16
+ def sample_5_words
17
+ ARGV[0..5] = 'cat', 'dog', 'crow', 'cow', 'elephant'
18
+ end
19
+
20
+ def sample_2_words
21
+ ARGV[0..1] = 'cat', 'dog'
22
+ end
@@ -0,0 +1,30 @@
1
+ require_relative '../loader'
2
+ require_relative 'spec_helper'
3
+
4
+ describe Collage do
5
+ before do
6
+ @collage = Collage.new(sample_10_words)
7
+ end
8
+ it 'should be able to read the words as commandline options and store them' do
9
+ @collage.search_words.count.must_equal 10
10
+ end
11
+
12
+ it 'should have 10 exact words in ARGV' do
13
+ @collage.search_words.must_equal ARGV[0..9]
14
+ end
15
+
16
+ it 'should verify the search wordcount and add 2 more words if required' do
17
+ @collage = Collage.new(sample_8_words)
18
+ @collage.search_words.count.must_equal 10
19
+ end
20
+
21
+ it 'should verify the search wordcount and add 5 more words if required' do
22
+ @collage = Collage.new(sample_5_words)
23
+ @collage.search_words.count.must_equal 10
24
+ end
25
+
26
+ it 'should verify the search wordcount and add 8 more words if required' do
27
+ @collage = Collage.new(sample_2_words)
28
+ @collage.search_words.count.must_equal 10
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../loader'
2
+ require_relative 'spec_helper'
3
+
4
+ describe ::AppConfig::FlickrSearch do
5
+ before do
6
+ @collage = Collage.new(sample_10_words)
7
+ @dict = File.read('/usr/share/dict/words').split("\n")
8
+ end
9
+
10
+ it 'should be able to search flickr api for a given search term' do
11
+ @collage.search_and_save_flickr_image(@dict.sample)
12
+ @collage.get_image_list.count.must_equal 1
13
+ end
14
+
15
+ it 'should search flickr api with sample words and store all images' do
16
+ @collage.download_images.get_image_list.count.must_equal 10
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../loader'
2
+ require_relative 'spec_helper'
3
+
4
+ describe ::AppConfig::MagickCollage do
5
+ before do
6
+ @collage = Collage.new(sample_5_words)
7
+ end
8
+
9
+ it 'should crop the images' do
10
+ @collage.download_images
11
+ @collage.crop_images
12
+ @collage.get_image_list.select { |i| i =~ /cropped/ }.count.must_equal 10
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flickr_collager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pankaj Doharey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 12.3.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 12.3.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 5.9.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 5.9.0
41
+ description: Collage Builder
42
+ email:
43
+ - pankajdoharey@gmail.com
44
+ executables:
45
+ - collage
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - Gemfile
50
+ - Gemfile.lock
51
+ - LICENSE
52
+ - README.md
53
+ - Rakefile
54
+ - bin/collage
55
+ - config.yaml
56
+ - flickr_collager.gemspec
57
+ - lib/app_config.rb
58
+ - lib/app_config/flickr_search.rb
59
+ - lib/app_config/magick_collage.rb
60
+ - lib/app_config/options.rb
61
+ - lib/collage.rb
62
+ - lib/dictionary.rb
63
+ - lib/post_install.rb
64
+ - lib/string_refinements.rb
65
+ - lib/version.rb
66
+ - loader.rb
67
+ - spec/spec_helper.rb
68
+ - spec/test_collage.rb
69
+ - spec/test_flickr_search.rb
70
+ - spec/test_magick_collage.rb
71
+ homepage: http://selfdotsend.com
72
+ licenses:
73
+ - WTFPL
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.7.6
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Flickr based Collage builder
95
+ test_files:
96
+ - spec/spec_helper.rb
97
+ - spec/test_collage.rb
98
+ - spec/test_flickr_search.rb
99
+ - spec/test_magick_collage.rb