Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature incognito mode #112

Merged
merged 7 commits into from
Aug 13, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/models/concerns/navigation/siblings_navigation.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
module SiblingsNavigation

def next_for(user)
pending_siblings_for(user).select { |it| it.number > number }.sort_by(&:number).first
user.pending_siblings_at(self).select { |it| it.number > number }.sort_by(&:number).first
end

def restart(user)
pending_siblings_for(user).sort_by(&:number).first
user.pending_siblings_at(self).sort_by(&:number).first
end

def siblings
Expand Down
2 changes: 1 addition & 1 deletion app/models/concerns/submittable/submittable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def submit!(user, submission)

def find_assignment_and_submit!(user, submission)
assignment = assignment_for user
results = submission.run! assignment, evaluation_class.new
results = user.run_submission! submission, assignment, evaluation_class.new
[assignment, results]
end
end
2 changes: 1 addition & 1 deletion app/models/concerns/with_assignments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ def status_for(user)
end

def assignment_for(user, organization=Organization.current)
find_assignment_for(user, organization) || user.assignments.build(exercise: self, organization: organization)
find_assignment_for(user, organization) || user.build_assignment(self, organization)
end
end
2 changes: 1 addition & 1 deletion app/models/concerns/with_progress.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module WithProgress
def progress_for(user, organization)
Indicator.find_or_initialize_by(user: user, organization: organization, content: self)
user.progress_at(self, organization)
end

def completion_percentage_for(user, organization=Organization.current)
Expand Down
5 changes: 3 additions & 2 deletions app/models/guide.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def exercises_count
end

def next_exercise(user)
pending_exercises(user).order('public.exercises.number asc').first
user.next_exercise_at(self)
end

# TODO: Make use of pending_siblings logic
Expand All @@ -52,7 +52,8 @@ def pending_exercises(user)
on assignments.exercise_id = exercises.id
and assignments.submitter_id = #{user.id}
and assignments.submission_status = #{Mumuki::Domain::Status::Submission::Passed.to_i}").
where('assignments.id is null')
where('assignments.id is null').
order('public.exercises.number asc')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ordering logic it's related to next_exercise, not to pending_exercises.
It's an important distinction because we'll use indicators to easily get pending_items but ordering logic is not included there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, I had that doubt 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review it again!

end

def first_exercise
Expand Down
24 changes: 24 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,30 @@ def name_initials
name.split.map(&:first).map(&:capitalize).join(' ')
end

def progress_at(content, organization)
Indicator.find_or_initialize_by(user: self, organization: organization, content: content)
end

def build_assignment(exercise, organization)
assignments.build(exercise: exercise, organization: organization)
end

def pending_siblings_at(content)
content.pending_siblings_for(self)
end

def next_exercise_at(guide)
guide.pending_exercises(self).first
end

def run_submission!(submission, assignment, evaluation)
submission.run! assignment, evaluation
end

def incognito?
false
end

private

def set_uid!
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddIncognitoModeEnabledToOrganization < ActiveRecord::Migration[5.1]
def change
add_column :organizations, :incognito_mode_enabled, :boolean
end
end
1 change: 1 addition & 0 deletions lib/mumuki/domain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module Domain
end

require_relative './domain/area'
require_relative './domain/incognito'
require_relative './domain/evaluation'
require_relative './domain/submission'
require_relative './domain/status'
Expand Down
108 changes: 108 additions & 0 deletions lib/mumuki/domain/incognito.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
module Mumuki::Domain
class IncognitoClass

def incognito?
true
end

# ============
# Permissions
# ============

def ensure_enabled!
end

def has_student_granted_organizations?
false
end

def teacher_here?
false
end

def teacher_of?(*)
false
end

def profile_completed?
true
end

def writer?
false
end

def moderator_here?
false
end

# ========
# Visiting
# ========

def visit!(*)
end

# ========
# Progress
# ========

def next_exercise_at(guide)
guide.exercises.first
end

# def completed_containers_with_lookahead(*)
# raise 'Unsupported operation. Userless mode and progressive display modes are incompatible'
# end

def progress_at(content, organization)
Indicator.new content: content, organization: organization
end

def build_assignment(exercise, organization)
Assignment.new exercise: exercise, organization: organization, submitter: self
end

def pending_siblings_at(content)
[]
end

# ============
# ActiveRecord
# ============

def id
'<incognito>'
end

def is_a?(other)
other.is_a?(Class) && other.name == 'User' || super
end

def _read_attribute(key)
return id if key == 'id'
raise "unknown attribute #{key}"
end

def self.primary_key
'id'
end

# ==========
# Evaluation
# ==========

def interpolations
[]
end

def run_submission!(submission, assignment, evaluation)
results = submission.dry_run! assignment, evaluation
assignment.assign_attributes results
results
end

end

Incognito = IncognitoClass.new
end
6 changes: 5 additions & 1 deletion lib/mumuki/domain/submission/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ def self.mapping_attributes

def run!(assignment, evaluation)
save_submission! assignment
results = evaluation.evaluate! assignment, self
results = dry_run! assignment, evaluation
save_results! results, assignment
notify_results! results, assignment
results
end

def dry_run!(assignment, evaluation)
evaluation.evaluate! assignment, self
end

def with_client_result(result)
self.client_result = result if result.present?
self
Expand Down
3 changes: 2 additions & 1 deletion spec/dummy/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20200731081757) do
ActiveRecord::Schema.define(version: 20200804191643) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -309,6 +309,7 @@
t.text "theme", default: "{}", null: false
t.text "profile", default: "{}", null: false
t.integer "progressive_display_lookahead"
t.boolean "incognito_mode_enabled"
t.index ["book_id"], name: "index_organizations_on_book_id"
t.index ["name"], name: "index_organizations_on_name", unique: true
end
Expand Down
71 changes: 71 additions & 0 deletions spec/models/incognito_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
require 'spec_helper'

describe Mumuki::Domain::Incognito do
let(:user) { Mumuki::Domain::Incognito }
let(:organization) { create(:organization) }

it 'can mock an AR relation' do
Assignment.new submitter: user
end

describe 'ensure_enabled!' do
it { expect { user.ensure_enabled! }.to_not raise_error }
end

describe 'assignment fooling' do
let(:assignment) { user.build_assignment(exercise, organization) }

context 'when exercise has no default content' do
let(:exercise) { create(:exercise) }

it { expect(assignment).to be_an Assignment }
it { expect(assignment.submitter).to be user }
it { expect(assignment.current_content).to eq '' }
end

context 'when exercise has default content' do
let(:exercise) { create(:exercise, default_content: '...') }

it { expect(assignment.current_content).to eq '...' }
end
end

describe 'guide fooling' do
let(:guide) { create(:guide, exercises: [ create(:exercise), create(:exercise) ]) }

describe 'completion_percentage_for', organization_workspace: :test do
it { expect(guide.completion_percentage_for(user)).to eq 0 }
end

describe 'next_exercise', organization_workspace: :test do
# TODO this looks weird. Why do we need a non-polymorphic
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we aren't using indicators for this one as progress is shared between organizations.
This change was made and had to be rolled back because of that.

# next_exercise? Aren't indicators not enought?
it { expect(guide.next_exercise(user)).to eq guide.exercises.first }
end
end

describe 'exercise fooling' do
let(:problem) { create(:problem) }

describe 'next_for' do
it { expect(problem.next_for(user)).to be nil }
end

describe 'try_submit_solution!', organization_workspace: :test do
let!(:chapter) do
create(:chapter, name: 'Functional Programming', lessons: [
create(:lesson, exercises: [problem])
])
end
let(:bridge_response) { {result: '0 failures', status: :passed} }

before { reindex_current_organization! }
before { expect_any_instance_of(Language).to receive(:run_tests!).and_return(bridge_response) }

let!(:assignment) { problem.try_submit_solution!(user) }

it { expect(assignment).to_not be nil }
it { expect(assignment.status).to be_like :passed }
end
end
end