diff --git a/Gemfile b/Gemfile
index 6680c06..3cd9f5f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -73,3 +73,4 @@ end
gem 'jquery-rails'
gem "turbolinks"
+gem 'algorithmia'
diff --git a/Gemfile.lock b/Gemfile.lock
index 3d9eb18..c9f56dd 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -64,6 +64,9 @@ GEM
tzinfo (~> 1.1)
addressable (2.5.1)
public_suffix (~> 2.0, >= 2.0.2)
+ algorithmia (1.0.1)
+ httparty (~> 0.13)
+ json (~> 1.8)
annotate (2.7.2)
activerecord (>= 3.2, < 6.0)
rake (>= 10.4, < 13.0)
@@ -114,6 +117,8 @@ GEM
globalid (0.4.0)
activesupport (>= 4.2.0)
hashdiff (0.3.4)
+ httparty (0.15.6)
+ multi_xml (>= 0.5.2)
i18n (0.8.6)
jbuilder (2.7.0)
activesupport (>= 4.2.0)
@@ -122,6 +127,7 @@ GEM
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
+ json (1.8.6)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.4.1)
@@ -145,6 +151,7 @@ GEM
mini_portile2 (2.3.0)
minitest (5.10.2)
multi_json (1.12.1)
+ multi_xml (0.6.0)
nio4r (2.1.0)
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
@@ -267,6 +274,7 @@ PLATFORMS
ruby
DEPENDENCIES
+ algorithmia
annotate
awesome_print
better_errors
diff --git a/README.md b/README.md
index 6fdced5..39e1464 100644
--- a/README.md
+++ b/README.md
@@ -120,6 +120,86 @@ The way it should work is:
- [Word Count](http://omnicalc-target.herokuapp.com/word_count/new)
- [Descriptive Statistics](http://omnicalc-target.herokuapp.com/descriptive_statistics/new)
-## Stretch Goals
+## Part IV: Homework
- - [Bootstrap it](http://getbootstrap.com/components/#panels) to make it look like [the real Omnicalc](http://omnicalc-target.herokuapp.com/). We've already connected `bootstrap.css` and [Font Awesome](http://fontawesome.io/icons/) for you, so you can just start using them.
+### Setup
+
+For our homework, we're going to be exploring a machine learning API marketplace called [Algorithmia](https://algorithmia.com/).
+
+First, visit [Algorithmia](https://algorithmia.com/) and sign up for an account. You'll be able to find your API keys by visiting 'https://algorithmia.com/users/[your username]' or by clicking on the profile icon at the top right and clicking the 'My API Keys' link. You'll need this key to complete the homework exercises below.
+
+We'll also need to make sure your API key stays hidden, in case your project ever gets pushed to Github or another public repository. Unsavory types like to scrape Github for sensitive information like API keys and run up huge bills for compromised users. In this specific case, you didn't have to tie your API key to a credit card, but protecting your API keys is generally good practice.
+
+We've already got the infrastructure for this in place. Our class project apps come bundled with a gem called `dotenv` which lets us store sensitive information just in our local development environment and hide that info from Git so it doesn't get pushed anywhere with our code. The info is stored in a file called `.env` that exists in the root folder of your application. Create a new file at the root directory of your application and call it `.env`. In the file place, the following code:
+
+```
+ALGORITHMIA_KEY="replace_me_with_your_key"
+```
+
+This is just a key-value pair that we can access anywhere in our Rails environment using the ENV hash. For example, to access this 'sensitive' info, we can open up Rails console and type in:
+
+```
+ENV['ALGORITHMIA_KEY']
+```
+
+and we should see output of
+
+```
+"replace_me_with_your_key"
+```
+
+You can use this pattern throughout your Rails app to pull up any sensitive info. Practice by using the `.env` file to store your actual Algorithmia API key.
+
+### Problem 1 - Auto-tag Text
+
+The first service we'll use auto-tags blocks of text.
+
+Here's how it should work:
+
+- If I visit `/text-tag`, I should see a form that has a single textarea element which lets me enter text for tagging. If you'd like an example, you can use this excerpt from the Paul Graham essay, [Do Things That Don't Scale](http://www.paulgraham.com/ds.html):
+
+```
+One of the most common types of advice we give at Y Combinator is to do things that don't scale. A lot of would-be founders believe that startups either take off or don't. You build something, make it available, and if you've made a better mousetrap, people beat a path to your door as promised. Or they don't, in which case the market must not exist.
+```
+- The textarea should have a label of `Text` and the button you click to submit the form should be called `Generate Tags`.
+- When the form is submitted, I should see an unordered list of tags corresponding to the submitted text. If you used the example text, you should see the following tags:
+```
+beat
+case
+door
+exist
+market
+path
+people
+promise
+```
+
+Visit the [AutoTag page](https://algorithmia.com/algorithms/nlp/AutoTag), and follow the instructions at the bottom of the page to integrate the API in your controller. ** You do not need to include the `require 'algorithmia'` statement from the instructions**
+
+### Problem 2 - Colorize Images
+
+The next service we'll use colorizes black and white images.
+
+Here's how it should work:
+
+- If I visit `/colorize`, I should see a form that has a single input which lets me enter the URL of a black and white image. You can use [https://cdn.vox-cdn.com/uploads/chorus_asset/file/4863353/grantpark-1.0.jpg](https://cdn.vox-cdn.com/uploads/chorus_asset/file/4863353/grantpark-1.0.jpg) as an example. The smaller the image, the better. Try not to go beyond 800x800px.
+- The input should have a label of `Image URL` and the button you click to submit the form should be called `Colorize`.
+- When the form is submitted, I should see a colorized version of the original black and white picture
+
+The API needs a bit of time to do it's work, so expect it to take about 30 seconds or so for the request to complete.
+
+Visit the [Image Colorization page](https://algorithmia.com/algorithms/deeplearning/ColorfulImageColorization), and follow the instructions at the bottom of the page to integrate the API in your controller.
+
+### Problem 3 - Auto-tag Images
+
+The next service we'll use tags images.
+
+Here's how it should work:
+
+- If I visit `/image-tag`, I should see a form that has a single input which lets me enter the URL of an image. You can use [http://www.pjproductions.co.uk/blog_images/Chicago-Booth-Group-photo-Pete-Jones.jpg](http://www.pjproductions.co.uk/blog_images/Chicago-Booth-Group-photo-Pete-Jones.jpg) as an example.
+- The input should have a label of `Image URL` and the button you click to submit the form should be called `Colorize`.
+- When the form is submitted, I should see a set of tags inside an unordered list.
+
+The API needs a bit of time to do it's work, so expect it to take about 30 seconds or so for the request to complete.
+
+Visit the [Illustration Tagger page](https://algorithmia.com/algorithms/deeplearning/IllustrationTagger), and follow the instructions at the bottom of the page to integrate the API in your controller.
diff --git a/app/controllers/algorithmia_controller.rb b/app/controllers/algorithmia_controller.rb
new file mode 100644
index 0000000..02bd2d3
--- /dev/null
+++ b/app/controllers/algorithmia_controller.rb
@@ -0,0 +1,45 @@
+class AlgorithmiaController < ApplicationController
+ def text_tag_form
+ #code
+ end
+
+ def process_text_tag
+ @text = params[:text]
+ client = Algorithmia.client(ENV['ALGORITHMIA_KEY'])
+ algo = client.algo('nlp/AutoTag/1.0.1')
+ @tags = algo.pipe(@text).result
+ end
+
+ def image_tag_form
+ #code
+ end
+
+ def process_image_tag
+ input = {
+ image: params[:image_url]
+ }
+ client = Algorithmia.client('simVbAuHzAns0iC7jNJJxE9sbDW1')
+ algo = client.algo('deeplearning/IllustrationTagger/0.4.0')
+ @original_image_url = params[:image_url]
+ @tag_hashes = algo.pipe(input).result['general']
+ end
+
+ def colorize_form
+ #code
+ end
+
+ def process_colorize
+ input = {
+ image: params[:image_url]
+ }
+ client = Algorithmia.client(ENV['ALGORITHMIA_KEY'])
+ algo = client.algo('deeplearning/ColorfulImageColorization/1.1.7')
+ result_hash = algo.pipe(input).result
+ result_output = result_hash['output']
+ algorithmia_path = result_output.split("data://")[1]
+ @original_image_url = params[:image_url]
+ @colorized_image_url = "https://algorithmia.com/v1/data/#{algorithmia_path}"
+ end
+
+
+end
diff --git a/app/views/algorithmia/colorize_form.html.erb b/app/views/algorithmia/colorize_form.html.erb
new file mode 100644
index 0000000..610cb74
--- /dev/null
+++ b/app/views/algorithmia/colorize_form.html.erb
@@ -0,0 +1,18 @@
+
+ Colorize
+
+
+
diff --git a/app/views/algorithmia/image_tag_form.html.erb b/app/views/algorithmia/image_tag_form.html.erb
new file mode 100644
index 0000000..ef7ad73
--- /dev/null
+++ b/app/views/algorithmia/image_tag_form.html.erb
@@ -0,0 +1,18 @@
+
+ Image Tagger
+
+
+
diff --git a/app/views/algorithmia/process_colorize.html.erb b/app/views/algorithmia/process_colorize.html.erb
new file mode 100644
index 0000000..38f286a
--- /dev/null
+++ b/app/views/algorithmia/process_colorize.html.erb
@@ -0,0 +1,7 @@
+Colorizer
+
+Your original image
+
+
+Colorized image
+
diff --git a/app/views/algorithmia/process_image_tag.html.erb b/app/views/algorithmia/process_image_tag.html.erb
new file mode 100644
index 0000000..dd0a563
--- /dev/null
+++ b/app/views/algorithmia/process_image_tag.html.erb
@@ -0,0 +1,11 @@
+Colorizer
+
+Your original image
+
+
+Generated tags for your image
+
+<% @tag_hashes.each do |tag_hash| %>
+ - <%= tag_hash.keys[0] %>
+<% end %>
+
diff --git a/app/views/algorithmia/process_text_tag.html.erb b/app/views/algorithmia/process_text_tag.html.erb
new file mode 100644
index 0000000..e49e288
--- /dev/null
+++ b/app/views/algorithmia/process_text_tag.html.erb
@@ -0,0 +1,11 @@
+Auto-tagger
+
+Your text
+<%= @text %>
+
+Generated tags for your text
+
+<% @tags.each do |tag| %>
+ - <%= tag %>
+<% end %>
+
diff --git a/app/views/algorithmia/text_tag_form.html.erb b/app/views/algorithmia/text_tag_form.html.erb
new file mode 100644
index 0000000..ae099f0
--- /dev/null
+++ b/app/views/algorithmia/text_tag_form.html.erb
@@ -0,0 +1,18 @@
+
+ Autotag
+
+
+
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index e13ef3d..4e28a7c 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -20,7 +20,9 @@
- <%= yield %>
+
+ <%= yield %>
+
diff --git a/config/routes.rb b/config/routes.rb
index 787824f..679d88e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,3 +1,11 @@
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
+
+
+ get "/image-tag" => "algorithmia#image_tag_form"
+ get "/image-tag/results" => "algorithmia#process_image_tag"
+ get "/colorize" => "algorithmia#colorize_form"
+ get "/colorize/results" => "algorithmia#process_colorize"
+ get "/text-tag" => "algorithmia#text_tag_form"
+ get "/text-tag/results" => "algorithmia#process_text_tag"
end
diff --git a/examples.txt b/examples.txt
new file mode 100644
index 0000000..d2c8f9f
--- /dev/null
+++ b/examples.txt
@@ -0,0 +1,63 @@
+example_id | status | run_time |
+------------------------------------------ | ------ | --------------- |
+./spec/features/algorithmia_spec.rb[1:1] | passed | 0.42592 seconds |
+./spec/features/algorithmia_spec.rb[2:1] | passed | 0.02373 seconds |
+./spec/features/algorithmia_spec.rb[3:1] | passed | 0.10707 seconds |
+./spec/features/algorithmia_spec.rb[4:1] | passed | 0.06595 seconds |
+./spec/features/algorithmia_spec.rb[5:1] | passed | 0.04462 seconds |
+./spec/features/algorithmia_spec.rb[6:1] | passed | 0.02967 seconds |
+./spec/features/algorithmia_spec.rb[7:1] | passed | 0.05147 seconds |
+./spec/features/algorithmia_spec.rb[8:1] | passed | 0.03795 seconds |
+./spec/features/algorithmia_spec.rb[9:1] | passed | 0.02798 seconds |
+./spec/features/algorithmia_spec.rb[10:1] | passed | 0.02208 seconds |
+./spec/features/algorithmia_spec.rb[11:1] | passed | 0.01762 seconds |
+./spec/features/algorithmia_spec.rb[12:1] | passed | 0.05064 seconds |
+./spec/features/calculations_spec.rb[1:1] | failed | 0.00177 seconds |
+./spec/features/calculations_spec.rb[2:1] | failed | 0.01092 seconds |
+./spec/features/calculations_spec.rb[3:1] | failed | 0.01252 seconds |
+./spec/features/calculations_spec.rb[4:1] | failed | 0.00236 seconds |
+./spec/features/calculations_spec.rb[5:1] | failed | 0.00266 seconds |
+./spec/features/calculations_spec.rb[6:1] | failed | 0.00329 seconds |
+./spec/features/calculations_spec.rb[7:1] | failed | 0.00248 seconds |
+./spec/features/calculations_spec.rb[8:1] | failed | 0.00186 seconds |
+./spec/features/calculations_spec.rb[9:1] | failed | 0.00305 seconds |
+./spec/features/calculations_spec.rb[10:1] | failed | 0.0018 seconds |
+./spec/features/calculations_spec.rb[11:1] | failed | 0.00996 seconds |
+./spec/features/calculations_spec.rb[12:1] | failed | 0.00451 seconds |
+./spec/features/calculations_spec.rb[13:1] | failed | 0.00237 seconds |
+./spec/features/calculations_spec.rb[14:1] | failed | 0.00178 seconds |
+./spec/features/calculations_spec.rb[15:1] | failed | 0.00204 seconds |
+./spec/features/calculations_spec.rb[16:1] | failed | 0.00338 seconds |
+./spec/features/calculations_spec.rb[17:1] | failed | 0.00251 seconds |
+./spec/features/calculations_spec.rb[18:1] | failed | 0.00189 seconds |
+./spec/features/calculations_spec.rb[19:1] | failed | 0.00151 seconds |
+./spec/features/calculations_spec.rb[20:1] | failed | 0.00165 seconds |
+./spec/features/calculations_spec.rb[21:1] | failed | 0.00172 seconds |
+./spec/features/calculations_spec.rb[22:1] | failed | 0.00156 seconds |
+./spec/features/calculations_spec.rb[23:1] | failed | 0.00217 seconds |
+./spec/features/calculations_spec.rb[24:1] | failed | 0.00172 seconds |
+./spec/features/calculations_spec.rb[25:1] | failed | 0.00246 seconds |
+./spec/features/calculations_spec.rb[26:1] | failed | 0.00374 seconds |
+./spec/features/calculations_spec.rb[27:1] | failed | 0.01445 seconds |
+./spec/features/calculations_spec.rb[28:1] | failed | 0.00421 seconds |
+./spec/features/calculations_spec.rb[29:1] | failed | 0.00275 seconds |
+./spec/features/calculations_spec.rb[30:1] | failed | 0.00284 seconds |
+./spec/features/calculations_spec.rb[31:1] | failed | 0.00713 seconds |
+./spec/features/calculations_spec.rb[32:1] | failed | 0.0046 seconds |
+./spec/features/calculations_spec.rb[33:1] | failed | 0.00726 seconds |
+./spec/features/calculations_spec.rb[34:1] | failed | 0.00275 seconds |
+./spec/features/calculations_spec.rb[35:1] | failed | 0.00208 seconds |
+./spec/features/calculations_spec.rb[36:1] | failed | 0.00158 seconds |
+./spec/features/calculations_spec.rb[37:1] | failed | 0.00161 seconds |
+./spec/features/calculations_spec.rb[38:1] | failed | 0.00151 seconds |
+./spec/features/calculations_spec.rb[39:1] | failed | 0.00172 seconds |
+./spec/features/calculations_spec.rb[40:1] | failed | 0.00175 seconds |
+./spec/features/calculations_spec.rb[41:1] | failed | 0.00183 seconds |
+./spec/features/calculations_spec.rb[42:1] | failed | 0.00247 seconds |
+./spec/features/calculations_spec.rb[43:1] | failed | 0.00183 seconds |
+./spec/features/calculations_spec.rb[44:1] | failed | 0.00168 seconds |
+./spec/features/calculations_spec.rb[45:1] | failed | 0.00279 seconds |
+./spec/features/calculations_spec.rb[46:1] | failed | 0.00196 seconds |
+./spec/features/calculations_spec.rb[47:1] | failed | 0.00396 seconds |
+./spec/features/calculations_spec.rb[48:1] | failed | 0.0039 seconds |
+./spec/features/calculations_spec.rb[49:1] | failed | 0.00252 seconds |
diff --git a/spec/features/algorithmia_spec.rb b/spec/features/algorithmia_spec.rb
new file mode 100644
index 0000000..74d74cc
--- /dev/null
+++ b/spec/features/algorithmia_spec.rb
@@ -0,0 +1,146 @@
+require "rails_helper"
+
+describe "/text-tag" do
+ it "has a label named 'Text'", points: 1, hint: h("copy_must_match label_for_input") do
+ visit "/text-tag"
+
+ expect(page).to have_css("label", text: 'Text')
+ end
+end
+
+describe "/text-tag" do
+ it "has a textarea", points: 1, hint: h("copy_must_match label_for_input") do
+ visit "/text-tag"
+
+ expect(page).to have_css("textarea", count: 1)
+ end
+end
+
+describe "/text-tag" do
+ it "has a button named 'Generate Tags'", points: 1, hint: h("copy_must_match label_for_input") do
+ visit "/text-tag"
+
+ expect(page).to have_css("button", text: 'Generate Tags')
+ end
+end
+
+describe "/text-tag" do
+ it "correctly generates tags", points: 5 do
+ Algorithmia = class_double("Algorithmia")
+ text_tag_setup
+
+ visit "/text-tag"
+ fill_in "Text", with: "One of the most common types of advice we give at Y Combinator is to do things that don't scale. A lot of would-be founders believe that startups either take off or don't. You build something, make it available, and if you've made a better mousetrap, people beat a path to your door as promised. Or they don't, in which case the market must not exist."
+ click_button "Generate Tags"
+ expect(page).to have_css("li", text: "beat")
+ expect(page).to have_css("li", text: "case")
+ end
+end
+
+describe "/colorize" do
+ it "has a label named 'Image URL'", points: 1 do
+ visit "/colorize"
+
+ expect(page).to have_css("label", text: 'Image URL')
+ end
+end
+
+describe "/colorize" do
+ it "has an input", points: 1 do
+ visit "/colorize"
+
+ expect(page).to have_css("input", count: 1)
+ end
+end
+
+describe "/colorize" do
+ it "has a button named 'Colorize'", points: 1 do
+ visit "/colorize"
+
+ expect(page).to have_css("button", text: 'Colorize')
+ end
+end
+
+describe "/colorize" do
+ it "displays the colorized image", points: 5 do
+ Algorithmia = class_double("Algorithmia2")
+ colorize_setup
+
+ visit "/colorize"
+ fill_in "Image URL", with: "https://cdn.vox-cdn.com/uploads/chorus_asset/file/4863353/grantpark-1.0.jpg"
+ click_button "Colorize"
+ expect(page).to have_css("img[src*='.algo/deeplearning/ColorfulImageColorization/temp/grantpark-1.0.png']")
+ end
+end
+
+describe "/image-tag" do
+ it "has a label named 'Image URL'", points: 1 do
+ visit "/image-tag"
+
+ expect(page).to have_css("label", text: 'Image URL')
+ end
+end
+
+describe "/image-tag" do
+ it "has an input", points: 1 do
+ visit "/image-tag"
+
+ expect(page).to have_css("input", count: 1)
+ end
+end
+
+describe "/image-tag" do
+ it "has a button named 'Process'", points: 1 do
+ visit "/image-tag"
+
+ expect(page).to have_css("button", text: 'Process')
+ end
+end
+
+describe "/image-tag" do
+ it "correctly generates tags", points: 5 do
+ Algorithmia = class_double("Algorithmia3")
+ image_tag_setup
+
+ visit "/image-tag"
+ fill_in "Image URL", with: "http://www.pjproductions.co.uk/blog_images/Chicago-Booth-Group-photo-Pete-Jones.jpg"
+ click_button "Process"
+
+ expect(page).to have_css("li", text: "formal")
+ expect(page).to have_css("li", text: "everyone")
+ expect(page).to have_css("li", text: "suit")
+ end
+end
+
+def text_tag_setup
+ client_obj = double(:client)
+ algo_obj = double(:algo)
+ pipe_obj = double(:pipe)
+ allow(Algorithmia).to receive(:client).and_return(client_obj)
+ allow(client_obj).to receive(:algo).and_return(algo_obj)
+ allow(algo_obj).to receive(:pipe).and_return(pipe_obj)
+ allow(pipe_obj).to receive(:result).and_return([
+ "beat",
+ "case"
+ ])
+end
+
+def colorize_setup
+ client_obj = double(:client)
+ algo_obj = double(:algo)
+ pipe_obj = double(:pipe)
+ allow(Algorithmia).to receive(:client).and_return(client_obj)
+ allow(client_obj).to receive(:algo).and_return(algo_obj)
+ allow(algo_obj).to receive(:pipe).and_return(pipe_obj)
+ allow(pipe_obj).to receive(:result).and_return({'output' => 'data://.algo/deeplearning/ColorfulImageColorization/temp/grantpark-1.0.png'})
+end
+
+def image_tag_setup
+ client_obj = double(:client)
+ algo_obj = double(:algo)
+ pipe_obj = double(:pipe)
+ allow(Algorithmia).to receive(:client).and_return(client_obj)
+ allow(client_obj).to receive(:algo).and_return(algo_obj)
+ allow(algo_obj).to receive(:pipe).and_return(pipe_obj)
+ allow(pipe_obj).to receive(:result).and_return({'general' => [{'formal': 0.7}, {'everyone': 0.8}, {'suit': 0.6}]})
+end
diff --git a/spec/features/calculations_spec.rb b/spec/features/calculations_spec.rb
index 7242ada..fbb9c78 100644
--- a/spec/features/calculations_spec.rb
+++ b/spec/features/calculations_spec.rb
@@ -64,6 +64,16 @@
end
end
+describe "/flexible/random/[MINIMUM]/[MAXIMUM]" do
+ it "contains the correct baseline copy", points: 1 do
+ visit "/flexible/random/1/10"
+
+ pattern = /A random number between 1 and 10 is/
+ matched_groups = pattern.match(page.text)
+ expect(matched_groups).to_not be_nil
+ end
+end
+
describe "/flexible/random/[MINIMUM]/[MAXIMUM]" do
it "creates a constrained random number", points: 1 do
visit "/flexible/random/1/10"