Class: Exercise

Direct Known Subclasses

Challenge, Reading

Instance Attribute Summary

Attributes included from WithEditionMode

#edition_mode

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 WithEditionMode

#edit!

Methods included from WithDescription

#description_teaser

Methods inherited from ApplicationRecord

aggregate_of, all_except, defaults, numbered, #save, #save_and_notify!, #save_and_notify_changes!, serialize_symbolized_hash_array, #update_and_notify!, update_or_create!, whitelist_attributes

Instance Method Details

#choice_index_for(value) ⇒ Object



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

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

#choice_valuesObject



102
103
104
# File 'app/models/exercise.rb', line 102

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

#console?Boolean

Returns:

  • (Boolean)


32
33
34
# File 'app/models/exercise.rb', line 32

def console?
  queriable?
end

#custom?Boolean

Returns:

  • (Boolean)


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

def custom?
  false
end

#default_contentObject



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

def default_content
  self[:default_content] || ''
end

#description_contextObject



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

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

#description_taskObject



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

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

#ensure_type!(type) ⇒ Object



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

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

#files_for(current_content) ⇒ Object



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

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

#friendlyObject



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

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

#guide_done_for?(user) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#import_from_resource_h!(number, resource_h) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'app/models/exercise.rb', line 85

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



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

def inspection_keywords
  {}
end

#limited?Boolean

Returns:

  • (Boolean)


185
186
187
# File 'app/models/exercise.rb', line 185

def limited?
  navigable_parent.limited_for?(self)
end

#messages_path_for(user) ⇒ Object



142
143
144
# File 'app/models/exercise.rb', line 142

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



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

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

#new_solutionObject



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

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

#pending_siblings_for(user) ⇒ Object



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

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

#previousObject



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

def previous
  sibling_at number.pred
end

#reclassify!(type) ⇒ Object



137
138
139
140
# File 'app/models/exercise.rb', line 137

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

#reset!Object



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

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

#search_tagsObject



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

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

#sibling_at(index) ⇒ Object



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

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

#slugObject



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

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

#slug_partsObject



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

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

#splitted_descriptionObject



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

def splitted_description
  description.split('> ')
end

#structural_parentObject



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

def structural_parent
  guide
end

#submission_for(user) ⇒ Object



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

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

#to_resource_hObject



110
111
112
113
114
115
116
117
118
# File 'app/models/exercise.rb', line 110

def to_resource_h
  language_resource_h = language.to_embedded_resource_h if language != guide.language
  as_json(only: %i(name layout editor description corollary teacher_info hint test manual_evaluation locale extra
                   choices expectations assistance_rules randomizations tag_list extra_visible goal default_content
                   free_form_editor_source initial_state final_state))
    .merge(id: bibliotheca_id, language: language_resource_h, type: type.underscore)
    .symbolize_keys
    .compact
end

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

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



176
177
178
179
180
181
182
183
# File 'app/models/exercise.rb', line 176

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)


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

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