Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

First unsolved problem link #113

Open
wants to merge 1 commit into from

2 participants

@koriroys

@mkelley33

First attempt at: #103

It works, but I'm not sure this is the functionality you were looking for.

It gets the first unsolved problem, not the next unsolved problem.

Pushing this up to start the discussion.

@mkelley33

Thank you for your contribution. I'd like to keep the discussion going. I think there are some other issues in the existing codebase unrelated to your pull-request. We're trying to get then next unsolved problem rather than the first unsolved problem so that the user stays within the difficulty. Why did you choose to implement the first unsolved problem?

@koriroys

Mostly because it was easier. Partially because it seemed like a step in the right direction, and I don't think it should be a huge stretch to build off this to get to the next unsolved problem.

What unrelated issues?

@mkelley33

I totally agree that it won't be a huge step to build off of this to get to the next unsolved problem. I like the way you think! The next step involves some minor tweaks to how problems are ordered (the somewhat-unrelated issues) both in the UI as well as in the seed file.

I'd like to take the branch you created in your fork, and use it as the basis for a new feature branch on origin with a similar name: "feature/next-problem-link"; its only slightly different, but standards for branch names in our organization is to use dashes and no abbreviations. Your commits will still be on the branch and you'll be added to the list of contributors.

I think the next step will be to continue working and/or collaborating on the feature until we can all agree on the implementation. I think its an important feature to get right, and I'd really enjoy discussing this further. Do you have any other thoughts or suggestions?

Thanks again for moving this forward!

@koriroys

Yeah I had to play with the seeds file to get the problem ordering sorted out. Didn't realize until later that I had basically reimplemented your rake task:

https://github.com/SciMed/rubeque/blob/develop/lib/tasks/problems.rake

as:

koriroys@962f506#L5R592

By all means, use the branch as a basis. My cucumber skills are non-existent, so I did it all in Rspec. :smile:

What else do you need?

@mkelley33

I just created a new branch: https://github.com/SciMed/rubeque/tree/feature/next-problem-link.

I merged your code in there. Any further contributions you make to this feature should go there.
When you're ready to submit another pull request, I'll merge it into the next-problem-link branch.

As for the tests, Rspec tests are great (and greatly appreciated!)

Thanks for telling me about the rake task. I didn't know we had that either! I'm relatively new to maintaining the project so feel free to share anything else you notice :)

So, I think the next step is to make the first unsolved problem link go to the next task of difficulty greater than or equal to that of the task that the user has just finished solving, or if there isn't at that level or above, then go to a problem in the next level of difficulty below the current one until all problems have been solved.

Use Case Scenario: experienced user solves problems one after the other.

Consider this scenario: a rubyist with a non-trivial amount of experience visits the site. So she goes on to solve more difficult problems. She solves a problem, and having an insatiable appetite for coding challenges, she clicks the "next problem" link. If she's redirected to a novice problem to solve, then she'll likely be disappointed or bored. However, if the next unsolved problem she's shown is at least as hard as the last one she recently solved, then the application will continue to challenge her. I believe this is the desired goal.

If you feel up to it, then by all means go ahead and implement the feature described above. When you've gotten to a good point (complete or not), then submit a pull request, and I'll take a look to see if we can merge it in yet.

Again, thank you for contributing here. I'll be sure to put your name in the list of contributors (or feel free to do so on the next-problem-link branch). Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 8, 2012
  1. @koriroys

    First unsolved problem link

    koriroys authored
This page is out of date. Refresh to see the latest.
View
2  Gemfile.lock
@@ -95,7 +95,7 @@ GEM
faraday (0.8.4)
multipart-post (~> 1.1)
ffi (1.1.4)
- gherkin (2.11.1)
+ gherkin (2.11.2)
json (>= 1.4.6)
hashie (1.2.0)
heroku (2.30.1)
View
8 app/controllers/problems_controller.rb
@@ -1,10 +1,10 @@
class ProblemsController < ApplicationController
# GET /problems
# GET /problems.json
-
+
before_filter :restrict_to_admin, only: [:edit,:update,:destroy,:unapproved]
before_filter :authenticate_user!, only: [:new]
-
+
def index
@problems = Problem.approved.all
@@ -76,7 +76,7 @@ def create
end
end
end
-
+
def approve
@problem = Problem.find(params[:id])
@problem.approved = true
@@ -129,7 +129,7 @@ def rss
def sort_column
Problem.fields.keys.include?(params[:sort]) ? params[:sort] : :difficulty
end
-
+
def sort_direction
%w(asc desc).include?(params[:direction]) ? params[:direction] : :asc
end
View
12 app/controllers/solutions_controller.rb
@@ -46,7 +46,7 @@ def create
if run_and_save_solution(@solution)
notice = 'Your solution passed!'
notice += ' Please sign in or register to earn points.' if current_user.blank?
- notice += " #{share_link} #{next_problem_link}"
+ notice += "#{share_link}#{next_problem_link}#{first_unsolved_problem_link}"
format.html { redirect_to problem_path(@problem.id, solution_code: @solution.code), notice: notice }
format.json { render json: @solution, status: :created, location: @solution }
else
@@ -70,7 +70,7 @@ def update
respond_to do |format|
if update_solution(@solution)
message = current_user_admin? && params[:bypass_run_code] ? "Solution successfully updated." : "Solution passed and was updated." +
- " #{share_link} #{next_problem_link}"
+ "#{share_link}#{next_problem_link}#{first_unsolved_problem_link}"
format.html { redirect_to @problem, notice: message }
format.json { head :ok }
else
@@ -134,11 +134,15 @@ def run_and_save_solution(solution)
end
def share_link
- "<a href='#{share_problem_solutions_path(@problem, solution_code: @solution.code)}'>Share your solution</a>!"
+ " <a href='#{share_problem_solutions_path(@problem, solution_code: @solution.code)}'>Share your solution</a>!"
end
def next_problem_link
- @problem.next_problem ? "Or go to <a href='#{problem_path(@problem.next_problem)}'>the next problem</a>." : ""
+ @problem.next_problem ? " Or go to <a href='#{problem_path(@problem.next_problem)}'>the next problem</a>." : ""
+ end
+
+ def first_unsolved_problem_link
+ current_user.try(:first_unsolved_problem) ? " Or go to <a href='#{problem_path(current_user.first_unsolved_problem)}'>your first unsolved problem</a>." : ""
end
def update_solution(solution)
View
4 app/controllers/users_controller.rb
@@ -1,10 +1,10 @@
class UsersController < ApplicationController
before_filter :restrict_to_admin, only: [:edit,:update,:destroy]
-
+
def index
@users = User.all.desc(:score).desc(:solution_count).page(params[:page] || 1)
end
-
+
def show
@user = User.where(:id => params[:id])
end
View
8 app/models/user.rb
@@ -63,11 +63,17 @@ class User
def solved_problems
@solved_problems ||= Hash[solutions.map{|solution| [solution.problem_id,solution.score]}]
end
-
+
def solved_problem_scores
solved_problems
end
+ def first_unsolved_problem
+ problems = Problem.asc(:order_number).uniq
+ solutions = self.solutions.map(&:problem)
+ (problems - solutions).first
+ end
+
def to_s
username
end
View
140 db/seeds.rb
@@ -7,8 +7,7 @@
######################################################################################################################
-Problem.create!(#_id: 1,
- difficulty: 0,
+Problem.create!(difficulty: 0,
title: "The Truth",
tag_list: "booleans",
instructions: "Here's a hint: true equals true.",
@@ -18,8 +17,7 @@
######################################################################################################################
-Problem.create!(#_id: 1,
- difficulty: 0,
+Problem.create!(difficulty: 0,
title: "Hello World",
instructions: "",
tag_list: "strings",
@@ -29,8 +27,7 @@
######################################################################################################################
-Problem.create!(#_id: 2,
- difficulty: 0,
+Problem.create!(difficulty: 0,
title: "Nil Values",
instructions: "Enter in a boolean value for what #nil? will return.",
tag_list: "nil, booleans",
@@ -40,8 +37,7 @@
######################################################################################################################
-Problem.create!(#_id: 3,
- difficulty: 0,
+Problem.create!(difficulty: 0,
title: "Reverse",
instructions: "What happens when you apply reverse? Hint: don't forget to quote your strings!",
tag_list: "strings",
@@ -51,8 +47,7 @@
######################################################################################################################
-Problem.create!(#_id: 3,
- difficulty: 0,
+Problem.create!(difficulty: 0,
title: "Maximum",
instructions: "Find the max in a given set of numbers",
tag_list: "numbers",
@@ -67,19 +62,18 @@ def maximum(arr)
assert_equal maximum([-2, 0, 33, 304, 2, -2]), 304
assert_equal maximum([1]), 1
code_block
- )
+ )
######################################################################################################################
-Problem.create!(#_id: 4,
- difficulty: 0,
+Problem.create!(difficulty: 0,
title: "FizzBuzz",
instructions: "If a number is divisible by 3, return \"Fizz\". If a number is divisible by 5, return \"Buzz\". If a number
is divisible by 3 and 5, return \"FizzBuzz\"",
tag_list: "arithmetic, strings",
approved: true,
order_number: (order_number+=1),
- code: <<-eos
+ code: <<-code_block
def fizzbuzz(x)
___
end
@@ -88,13 +82,12 @@ def fizzbuzz(x)
assert_equal fizzbuzz(50), "Buzz"
assert_equal fizzbuzz(15), "FizzBuzz"
assert_equal fizzbuzz(5175), "FizzBuzz"
-eos
+code_block
)
######################################################################################################################
-Problem.create!(#_id: 5,
- difficulty: 0,
+Problem.create!(difficulty: 0,
title: "Map",
instructions: "How would you create an array that contains the squares of all elements in a range?",
tag_list: "enumerables",
@@ -340,8 +333,7 @@ def method_missing(m, *args)
######################################################################################################################
-Problem.create!(#_id: 6,
- difficulty: 1,
+Problem.create!(difficulty: 1,
title: "Reverse Each Word",
instructions: "Write a method that takes a sentence and returns it with each word reversed in place.",
tag_list: "strings, enumerables",
@@ -358,8 +350,7 @@ def reverse_each_word(sentence)
######################################################################################################################
-Problem.create!(#_id: 7,
- difficulty: 1,
+Problem.create!(difficulty: 1,
title: "Your Favorite and Mine, Fibonacci!",
instructions: "Write a method that handles Fibonacci sequences. Have it return the nth item in the Fibonacci sequence.
Hint: The first item in the sequence is 0.",
@@ -457,15 +448,14 @@ def chunky_bacon
######################################################################################################################
-Problem.create!(#_id: 8,
- difficulty: 2,
- title: "Hello? Yes, This Is Dog",
- instructions: 'Write a method to validate some strings that could potentially represent phone numbers. See if you can do it with a
- single regular expression.',
- approved: true,
- order_number: (order_number+=1),
- tag_list: "strings, regular expressions",
- code: <<-eos
+Problem.create!(difficulty: 2,
+ title: "Hello? Yes, This Is Dog",
+ instructions: 'Write a method to validate some strings that could potentially represent phone numbers. See if you can do it with a
+ single regular expression.',
+ approved: true,
+ order_number: (order_number+=1),
+ tag_list: "strings, regular expressions",
+ code: <<-code_block
def phone_number?(num)
___
end
@@ -477,19 +467,18 @@ def phone_number?(num)
assert_equal phone_number?("(555) 555-555"), false
assert_equal phone_number?("555-555-555"), true
assert_equal phone_number?("(555-555-555"), false
-eos
+code_block
)
######################################################################################################################
-Problem.create!(#_id: 8,
- difficulty: 2,
- title: "Prime Factors",
- instructions: 'Find all of the prime factors for a given number',
- approved: true,
- order_number: (order_number+=1),
- tag_list: "arithmetic",
- code: <<-eos
+Problem.create!(difficulty: 2,
+ title: "Prime Factors",
+ instructions: 'Find all of the prime factors for a given number',
+ approved: true,
+ order_number: (order_number+=1),
+ tag_list: "arithmetic",
+ code: <<-code_block
def prime_factors(num)
___
end
@@ -497,38 +486,36 @@ def prime_factors(num)
assert_equal prime_factors(102), [2, 3, 17]
assert_equal prime_factors(896680), [2, 5, 29, 773]
assert_equal prime_factors(42), [2, 3, 7]
-eos
+code_block
)
######################################################################################################################
-Problem.create!(#_id: 8,
- difficulty: 2,
- title: "Pigs Fly; Wait, No They Don't.",
- instructions: 'Can you make a method return a differect value after a "return" is called?',
- approved: true,
- order_number: (order_number+=1),
- tag_list: "exceptions",
- code: <<-eos
+Problem.create!(difficulty: 2,
+ title: "Pigs Fly; Wait, No They Don't.",
+ instructions: 'Can you make a method return a differect value after a "return" is called?',
+ approved: true,
+ order_number: (order_number+=1),
+ tag_list: "exceptions",
+ code: <<-code_block
def do_pigs_fly?
return true
___
end
assert_equal do_pigs_fly?, false
- eos
+code_block
)
######################################################################################################################
-Problem.create!(#_id: 8,
- difficulty: 2,
- title: "Regular Expressions Revisted",
- instructions: 'Parse this list into a two dimensional array where the inner array has a number and name.',
- approved: true,
- order_number: (order_number+=1),
- tag_list: "regular expressions, strings, arrays",
- code: <<-eos
+Problem.create!(difficulty: 2,
+ title: "Regular Expressions Revisted",
+ instructions: 'Parse this list into a two dimensional array where the inner array has a number and name.',
+ approved: true,
+ order_number: (order_number+=1),
+ tag_list: "regular expressions, strings, arrays",
+ code: <<-code_block
doctor_map = "One: William Hartnell
Two:Patrick Troughton
Three:: Jon Pertwee
@@ -550,22 +537,21 @@ def do_pigs_fly?
assert_equal doctors[7][0], "Eight"
assert_equal doctors[9][1], "David Tennant"
assert_equal doctors[10][1], "Matthew Robert Smith"
-eos
+code_block
)
######################################################################################################################
-Problem.create!(#_id: 9,
- difficulty: 2,
- title: "Happy Numbers",
- instructions: "Happy numbers are positive integers that follow a particular formula: take each individual digit,
- square it, and then sum the squares to get a new number. Repeat with the new number and eventually,
- you might get to a number whose squared sum is 1. This is a happy number. An unhappy number (or
- sad number) is one that loops endlessly. Write a function that determines if a number is happy or not.",
- approved: true,
- order_number: (order_number+=1),
- tag_list: "arithmetic",
- code: <<-code_block
+Problem.create!(difficulty: 2,
+ title: "Happy Numbers",
+ instructions: "Happy numbers are positive integers that follow a particular formula: take each individual digit,
+ square it, and then sum the squares to get a new number. Repeat with the new number and eventually,
+ you might get to a number whose squared sum is 1. This is a happy number. An unhappy number (or
+ sad number) is one that loops endlessly. Write a function that determines if a number is happy or not.",
+ approved: true,
+ order_number: (order_number+=1),
+ tag_list: "arithmetic",
+ code: <<-code_block
___
assert_equal happy_number?(7), true
@@ -573,7 +559,7 @@ def do_pigs_fly?
assert_equal happy_number?(2), false
assert_equal happy_number?(3), false
code_block
- )
+ )
######################################################################################################################
@@ -589,10 +575,10 @@ def do_pigs_fly?
a given card. Cards will be supplied in the format "2H" where two is the numeric value and H stands for hearts. Face value cards
will be of the format "AC" for ace of clubs, or "KS" for king of spades, etc. Results must contain two significant digits
after the decimal.},
- tag_list: "probability, arithmetic",
- approved: true,
- order_number: (order_number+=1),
- code: <<-code_block
+ tag_list: "probability, arithmetic",
+ approved: true,
+ order_number: (order_number+=1),
+ code: <<-code_block
___
assert_equal first_trick.winning_probability("2C"), 0.00
@@ -602,4 +588,8 @@ def do_pigs_fly?
assert_equal first_trick.winning_probability("2S"), 0.00
assert_equal first_trick.winning_probability("AH"), 0.00
code_block
-)
+ )
+
+Problem.asc(:order_number).all.each_cons(2) do |problem, next_problem|
+ problem.update_attribute :next_problem_id, next_problem.id
+end
View
76 spec/models/user_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe User do
+ describe "first unsolved problem" do
+
+ before do
+ @user = User.create!(name: 'Juan', email: 'juan@gmail.mx', password: 'mexico', username: 'juantwo')
+ @first_problem = Problem.create!(difficulty: 0,
+ title: "The Truth",
+ tag_list: "booleans",
+ instructions: "Here's a hint: true equals true.",
+ code: "assert_equal true, ___",
+ approved: true,
+ order_number: 0
+ )
+ @second_problem = Problem.create!(difficulty: 0,
+ title: "Hello World",
+ instructions: "",
+ tag_list: "strings",
+ code: "assert_equal 'HELLO WORLD', 'hello world'.___",
+ approved: true,
+ order_number: 1
+ )
+ @third_problem = Problem.create!(difficulty: 0,
+ title: "Nil Values",
+ instructions: "Enter in a boolean value for what #nil? will return.",
+ tag_list: "nil, booleans",
+ code: "[0, '', 'chunky_bacon'].each { |v| assert_equal v.nil?, ___ }",
+ approved: true,
+ order_number: 2
+ )
+ end
+
+ it "is the first problem if no problems have been solved" do
+ expect(@user.first_unsolved_problem).to eq(@first_problem)
+ end
+
+ it "is the second problem if the first problem has been solved" do
+ Solution.any_instance.stub(run_problem: true)
+ solution = Solution.new
+ solution.problem = @first_problem
+ solution.user = @user
+ solution.save!
+
+ expect(@user.first_unsolved_problem).to eq(@second_problem)
+ end
+
+ it "is the first problem if the second and third problem have been solved" do
+ Solution.any_instance.stub(run_problem: true)
+ solution = Solution.new
+ solution.problem = @second_problem
+ solution.user = @user
+ solution.save!
+ solution2 = Solution.new
+ solution2.problem = @third_problem
+ solution2.user = @user
+ solution2.save!
+
+ expect(@user.first_unsolved_problem).to eq(@first_problem)
+ end
+
+ it "is the third problem if the first and second problem have been solved" do
+ Solution.any_instance.stub(run_problem: true)
+ solution = Solution.new
+ solution.problem = @first_problem
+ solution.user = @user
+ solution.save!
+ solution2 = Solution.new
+ solution2.problem = @second_problem
+ solution2.user = @user
+ solution2.save!
+
+ expect(@user.first_unsolved_problem).to eq(@third_problem)
+ end
+ end
+end
View
12 spec/spec_helper.rb
@@ -25,6 +25,18 @@
# instead of true.
#config.use_transactional_fixtures = true
+ config.before(:suite) do
+ DatabaseCleaner.clean_with(:truncation)
+ end
+
+ config.before(:each) do
+ DatabaseCleaner.start
+ end
+
+ config.after(:each) do
+ DatabaseCleaner.clean
+ end
+
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
Something went wrong with that request. Please try again.