Class: Exercise

Direct Known Subclasses

Challenge, Reading

Constant Summary collapse

RANDOMIZED_FIELDS =
[:default_content, :description, :extra, :hint, :test]

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SiblingsNavigation

#navigable_name, #next_for, #restart

Methods included from ParentNavigation

#leave, #navigable_name, #navigable_parent, #navigation_end?

Methods included from Submittable

#find_assignment_and_submit!, #submit!

Methods included from Questionable

#submit_question!

Methods included from WithAssignments

#assignment_for, #find_assignment_for, #has_messages_for?, #messages_for, #status_for

Methods included from FriendlyName

#friendly_name, #to_param

Methods included from Assistable

#assist_with, #assistant

Methods included from WithRandomizations

#randomizer, #seed, #seed_with!

Methods included from WithDiscussions

#discuss!, #try_solve_discussions

Methods included from WithDescription

#description_teaser

Methods inherited from ApplicationRecord

aggregate_of, all_except, defaults, #delete, #destroy!, numbered, organic_on, resource_fields, #save, #save_and_notify!, #save_and_notify_changes!, serialize_symbolized_hash_array, #update_and_notify!, update_or_create!, whitelist_attributes

Class Method Details

.find_transparently!(params) ⇒ Object



201
202
203
# File 'app/models/exercise.rb', line 201

def self.find_transparently!(params)
  Guide.find_transparently!(params).locate_exercise! params[:bibliotheca_id]
end

.locate!(slug_and_bibliotheca_id) ⇒ Object



205
206
207
208
# File 'app/models/exercise.rb', line 205

def self.locate!(slug_and_bibliotheca_id)
  slug, bibliotheca_id = slug_and_bibliotheca_id
  Guide.locate!(slug).locate_exercise! bibliotheca_id
end

Instance Method Details

#choice_index_for(value) ⇒ Object



110
111
112
# File 'app/models/exercise.rb', line 110

def choice_index_for(value)
  choice_values.index(value)
end

#choice_valuesObject



106
107
108
# File 'app/models/exercise.rb', line 106

def choice_values
  self[:choice_values].presence || choices.map { |it| it.indifferent_get(:value) }
end

#console?Boolean

Returns:

  • (Boolean)


36
37
38
# File 'app/models/exercise.rb', line 36

def console?
  queriable?
end

#custom?Boolean

Returns:

  • (Boolean)


171
172
173
# File 'app/models/exercise.rb', line 171

def custom?
  false
end

#description_contextObject



159
160
161
# File 'app/models/exercise.rb', line 159

def description_context
  Mumukit::ContentType::Markdown.to_html splitted_description.first
end

#description_taskObject



167
168
169
# File 'app/models/exercise.rb', line 167

def description_task
  Mumukit::ContentType::Markdown.to_html splitted_description.drop(1).join("\n")
end

#ensure_type!(type) ⇒ Object



138
139
140
141
142
143
144
# File 'app/models/exercise.rb', line 138

def ensure_type!(type)
  if self.type != type
    reclassify! type
  else
    self
  end
end

#files_for(current_content) ⇒ Object



194
195
196
197
198
199
# File 'app/models/exercise.rb', line 194

def files_for(current_content)
  language
    .directives_sections
    .split_sections(current_content)
    .map { |name, content| Mumuki::Domain::File.new name, content }
end

#friendlyObject



77
78
79
# File 'app/models/exercise.rb', line 77

def friendly
  defaulting_name { "#{navigable_parent.friendly} - #{name}" }
end

#guide_done_for?(user) ⇒ Boolean

Returns:

  • (Boolean)


52
53
54
# File 'app/models/exercise.rb', line 52

def guide_done_for?(user)
  guide.done_for?(user)
end

#import_from_resource_h!(number, resource_h) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/models/exercise.rb', line 89

def import_from_resource_h!(number, resource_h)
  self.language = Language.for_name(resource_h.dig(:language, :name)) || guide.language
  self.locale = guide.locale

  reset!

  attrs = whitelist_attributes(resource_h, except: [:type, :id])
  attrs[:choices] = resource_h[:choices].to_a
  attrs[:bibliotheca_id] = resource_h[:id]
  attrs[:number] = number
  attrs[:manual_evaluation] ||= false
  attrs = attrs.except(:expectations) if type != 'Problem' || resource_h[:new_expectations]

  assign_attributes(attrs)
  save!
end

#inspection_keywordsObject



175
176
177
# File 'app/models/exercise.rb', line 175

def inspection_keywords
  {}
end

#limited?Boolean

Returns:

  • (Boolean)


190
191
192
# File 'app/models/exercise.rb', line 190

def limited?
  navigable_parent.limited_for?(self)
end

#messages_path_for(user) ⇒ Object



151
152
153
# File 'app/models/exercise.rb', line 151

def messages_path_for(user)
  "api/guides/#{guide.slug}/#{bibliotheca_id}/student/#{URI.escape user.uid}/messages?language=#{language}"
end

#messages_url_for(user) ⇒ Object



155
156
157
# File 'app/models/exercise.rb', line 155

def messages_url_for(user)
  Mumukit::Platform.classroom_api.organic_url_for(Organization.current, messages_path_for(user))
end

#new_solutionObject



85
86
87
# File 'app/models/exercise.rb', line 85

def new_solution
  Mumuki::Domain::Submission::Solution.new(content: default_content)
end

#pending_siblings_for(user) ⇒ Object



44
45
46
# File 'app/models/exercise.rb', line 44

def pending_siblings_for(user)
  guide.pending_exercises(user)
end

#previousObject



56
57
58
# File 'app/models/exercise.rb', line 56

def previous
  sibling_at number.pred
end

#reclassify!(type) ⇒ Object



146
147
148
149
# File 'app/models/exercise.rb', line 146

def reclassify!(type)
  update!(type: type)
  Exercise.find(id)
end

#reset!Object



129
130
131
132
133
134
135
136
# File 'app/models/exercise.rb', line 129

def reset!
  self.name = nil
  self.description = nil
  self.corollary = nil
  self.hint = nil
  self.extra = nil
  self.tag_list = []
end

#search_tagsObject



65
66
67
# File 'app/models/exercise.rb', line 65

def search_tags
  [language&.name, *tag_list].compact
end

#settingsObject



210
211
212
# File 'app/models/exercise.rb', line 210

def settings
  guide.settings.deep_merge super
end

#sibling_at(index) ⇒ Object



60
61
62
63
# File 'app/models/exercise.rb', line 60

def sibling_at(index)
  index = number + index unless index.positive?
  guide.exercises.find_by(number: index)
end

#splitted_descriptionObject



163
164
165
# File 'app/models/exercise.rb', line 163

def splitted_description
  description.split('> ')
end

#structural_parentObject



48
49
50
# File 'app/models/exercise.rb', line 48

def structural_parent
  guide
end

#submission_for(user) ⇒ Object



81
82
83
# File 'app/models/exercise.rb', line 81

def submission_for(user)
  assignment_for(user).submission
end

#to_resource_hObject

Keep this list up to date with Mumukit::Sync::Store::Github::Schema::Exercise



116
117
118
119
120
121
122
123
124
125
126
127
# File 'app/models/exercise.rb', line 116

def to_resource_h
  language_resource_h = language.to_embedded_resource_h if language != guide.language
  as_json(only: i(name layout editor corollary teacher_info manual_evaluation locale
                   choices assistance_rules randomizations tag_list extra_visible goal
                   free_form_editor_source initial_state final_state))
    .merge(id: bibliotheca_id, language: language_resource_h, type: type.underscore)
    .merge(expectations: self[:expectations])
    .merge(settings: self[:settings])
    .merge(RANDOMIZED_FIELDS.map { |it| [it, self[it]] }.to_h)
    .symbolize_keys
    .compact
end

#transparent_idObject



69
70
71
# File 'app/models/exercise.rb', line 69

def transparent_id
  "#{guide.transparent_id}/#{bibliotheca_id}"
end

#transparent_paramsObject



73
74
75
# File 'app/models/exercise.rb', line 73

def transparent_params
  guide.transparent_params.merge(bibliotheca_id: bibliotheca_id)
end

#try_submit_solution!(user, solution = {}) ⇒ Object

Submits the user solution only if the corresponding assignment has attemps left



181
182
183
184
185
186
187
188
# File 'app/models/exercise.rb', line 181

def try_submit_solution!(user, solution={})
  assignment = assignment_for(user)
  if assignment.attempts_left?
    submit_solution!(user, solution)
  else
    assignment
  end
end

#used_in?(organization = Organization.current) ⇒ Boolean

Returns:

  • (Boolean)


40
41
42
# File 'app/models/exercise.rb', line 40

def used_in?(organization=Organization.current)
  guide.usage_in_organization(organization).present?
end