mumuki-laboratory 5.5.0 → 5.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/application/discussions.js +43 -0
  3. data/app/assets/stylesheets/application/_modules.scss +2 -0
  4. data/app/assets/stylesheets/application/modules/_checkboxes.scss +21 -8
  5. data/app/assets/stylesheets/application/modules/_discussion.scss +339 -0
  6. data/app/assets/stylesheets/application/modules/_pagination.scss +10 -0
  7. data/app/controllers/application_controller.rb +4 -9
  8. data/app/controllers/book_discussions_controller.rb +7 -0
  9. data/app/controllers/chapters_controller.rb +2 -0
  10. data/app/controllers/discussions_controller.rb +65 -0
  11. data/app/controllers/discussions_messages_controller.rb +25 -0
  12. data/app/controllers/exercises_controller.rb +1 -0
  13. data/app/controllers/guide_container_controller.rb +1 -0
  14. data/app/controllers/messages_controller.rb +1 -1
  15. data/app/controllers/users_controller.rb +1 -0
  16. data/app/helpers/application_helper.rb +7 -0
  17. data/app/helpers/assignment_result_helper.rb +10 -30
  18. data/app/helpers/breadcrumbs_helper.rb +7 -3
  19. data/app/helpers/contextualization_result_helper.rb +31 -0
  20. data/app/helpers/discussions_helper.rb +163 -0
  21. data/app/helpers/exercise_input_helper.rb +33 -5
  22. data/app/helpers/icons_helper.rb +20 -3
  23. data/app/helpers/messages_helper.rb +4 -0
  24. data/app/helpers/status_helper.rb +2 -2
  25. data/app/models/assignment.rb +31 -47
  26. data/app/models/book.rb +3 -0
  27. data/app/models/chapter.rb +2 -0
  28. data/app/models/concerns/contextualization.rb +85 -0
  29. data/app/models/concerns/submittable/solvable.rb +1 -0
  30. data/app/models/concerns/with_assignments.rb +1 -1
  31. data/app/models/concerns/with_discussion_creation.rb +9 -0
  32. data/app/models/concerns/with_discussion_creation/subscription.rb +33 -0
  33. data/app/models/concerns/with_discussion_creation/upvote.rb +28 -0
  34. data/app/models/concerns/with_discussion_status.rb +11 -0
  35. data/app/models/concerns/with_discussions.rb +23 -0
  36. data/app/models/concerns/with_randomizations.rb +34 -0
  37. data/app/models/concerns/with_scoped_queries.rb +47 -0
  38. data/app/models/concerns/with_scoped_queries/filter.rb +15 -0
  39. data/app/models/concerns/with_scoped_queries/page.rb +10 -0
  40. data/app/models/concerns/with_scoped_queries/sort.rb +47 -0
  41. data/app/models/discussion.rb +128 -0
  42. data/app/models/exercise.rb +10 -1
  43. data/app/models/guide.rb +1 -1
  44. data/app/models/message.rb +24 -3
  45. data/app/models/organization.rb +4 -0
  46. data/app/models/submission/confirmation.rb +1 -1
  47. data/app/models/submission/console_submission.rb +1 -1
  48. data/app/models/submission/submission.rb +13 -0
  49. data/app/models/subscription.rb +12 -0
  50. data/app/models/topic.rb +1 -1
  51. data/app/models/upvote.rb +4 -0
  52. data/app/models/user.rb +3 -2
  53. data/app/views/book_discussions/index.html.erb +23 -0
  54. data/app/views/chapters/show.html.erb +0 -1
  55. data/app/views/discussions/_message.html.erb +22 -0
  56. data/app/views/discussions/_message_container.html.erb +8 -0
  57. data/app/views/discussions/_new_message.html.erb +19 -0
  58. data/app/views/discussions/index.html.erb +30 -0
  59. data/app/views/discussions/show.html.erb +76 -0
  60. data/app/views/exercise_solutions/_contextualization_results.html.erb +21 -0
  61. data/app/views/exercise_solutions/_contextualization_results_container.html.erb +4 -0
  62. data/app/views/exercise_solutions/_expectations.html.erb +9 -3
  63. data/app/views/exercise_solutions/_kids_results.html.erb +1 -1
  64. data/app/views/exercise_solutions/_results.html.erb +21 -55
  65. data/app/views/exercise_solutions/_results_title.html.erb +2 -2
  66. data/app/views/exercises/_exercise_assignment.html.erb +20 -0
  67. data/app/views/exercises/_read_only.html.erb +104 -0
  68. data/app/views/exercises/show.html.erb +4 -20
  69. data/app/views/layouts/_discussions.html.erb +68 -0
  70. data/app/views/layouts/_kids.html.erb +9 -6
  71. data/app/views/layouts/_messages.html.erb +1 -1
  72. data/app/views/layouts/_result.html.erb +2 -2
  73. data/app/views/layouts/_test_results.html.erb +6 -6
  74. data/app/views/layouts/application.html.erb +10 -9
  75. data/app/views/layouts/exercise_inputs/forms/_form.html.erb +5 -6
  76. data/app/views/layouts/exercise_inputs/forms/_interactive_form.html.erb +5 -5
  77. data/app/views/layouts/exercise_inputs/forms/_kids_form.html.erb +9 -17
  78. data/app/views/layouts/exercise_inputs/forms/_playground_form.html.erb +5 -5
  79. data/app/views/layouts/exercise_inputs/forms/_problem_form.html.erb +11 -11
  80. data/app/views/layouts/exercise_inputs/forms/_reading_form.html.erb +1 -1
  81. data/app/views/layouts/exercise_inputs/layouts/_input_bottom.html.erb +1 -1
  82. data/app/views/layouts/exercise_inputs/layouts/_input_kids.html.erb +7 -7
  83. data/app/views/layouts/exercise_inputs/layouts/_input_right.html.erb +1 -1
  84. data/app/views/layouts/exercise_inputs/read_only_editors/_code.html.erb +3 -0
  85. data/app/views/layouts/exercise_inputs/read_only_editors/_custom.html.erb +6 -0
  86. data/app/views/layouts/exercise_inputs/read_only_editors/_multiple_choice.html.erb +8 -0
  87. data/app/views/layouts/exercise_inputs/read_only_editors/_single_choice.html.erb +8 -0
  88. data/app/views/layouts/modals/_kids_context.html.erb +2 -2
  89. data/app/views/layouts/modals/_new_discussion.html.erb +27 -0
  90. data/app/views/users/show.html.erb +23 -3
  91. data/config/routes.rb +21 -1
  92. data/db/migrate/20180504173548_create_discussions.rb +12 -0
  93. data/db/migrate/20180504185845_add_discussion_id_to_message.rb +5 -0
  94. data/db/migrate/20180605143727_add_submission_to_discussion.rb +16 -0
  95. data/db/migrate/20180619182555_create_subscriptions.rb +9 -0
  96. data/db/migrate/20180702153442_create_upvotes.rb +8 -0
  97. data/db/migrate/20180702175220_add_upvotes_count_to_discussions.rb +5 -0
  98. data/db/migrate/20180704150839_rename_assignment_status_to_submission_status.rb +5 -0
  99. data/lib/mumuki/laboratory.rb +2 -0
  100. data/lib/mumuki/laboratory/controllers.rb +2 -1
  101. data/lib/mumuki/laboratory/controllers/content.rb +12 -0
  102. data/lib/mumuki/laboratory/controllers/notifications.rb +31 -0
  103. data/lib/mumuki/laboratory/controllers/results_rendering.rb +1 -1
  104. data/lib/mumuki/laboratory/engine.rb +0 -2
  105. data/lib/mumuki/laboratory/evaluation/manual.rb +1 -1
  106. data/lib/mumuki/laboratory/locales/en.yml +37 -1
  107. data/lib/mumuki/laboratory/locales/es.yml +42 -1
  108. data/lib/mumuki/laboratory/locales/pt.yml +33 -1
  109. data/lib/mumuki/laboratory/status.rb +51 -44
  110. data/lib/mumuki/laboratory/status/discussion/closed.rb +15 -0
  111. data/lib/mumuki/laboratory/status/discussion/discussion.rb +56 -0
  112. data/lib/mumuki/laboratory/status/discussion/opened.rb +27 -0
  113. data/lib/mumuki/laboratory/status/discussion/pending_review.rb +15 -0
  114. data/lib/mumuki/laboratory/status/discussion/solved.rb +19 -0
  115. data/lib/mumuki/laboratory/status/submission/aborted.rb +11 -0
  116. data/lib/mumuki/laboratory/status/submission/errored.rb +15 -0
  117. data/lib/mumuki/laboratory/status/{failed.rb → submission/failed.rb} +2 -2
  118. data/lib/mumuki/laboratory/status/submission/manual_evaluation_pending.rb +15 -0
  119. data/lib/mumuki/laboratory/status/{passed.rb → submission/passed.rb} +2 -2
  120. data/lib/mumuki/laboratory/status/{passed_with_warnings.rb → submission/passed_with_warnings.rb} +2 -2
  121. data/lib/mumuki/laboratory/status/submission/pending.rb +11 -0
  122. data/lib/mumuki/laboratory/status/submission/running.rb +11 -0
  123. data/lib/mumuki/laboratory/status/submission/submission.rb +49 -0
  124. data/lib/mumuki/laboratory/status/{unknown.rb → submission/unknown.rb} +2 -2
  125. data/lib/mumuki/laboratory/version.rb +1 -1
  126. data/spec/controllers/chapters_controller_spec.rb +17 -0
  127. data/spec/controllers/discussions_controller_spec.rb +19 -0
  128. data/spec/dummy/config/environments/development.rb +0 -2
  129. data/spec/dummy/config/environments/test.rb +0 -2
  130. data/spec/dummy/db/schema.rb +42 -2
  131. data/spec/evaluation_helper.rb +1 -1
  132. data/spec/factories/discussion_factory.rb +8 -0
  133. data/spec/features/dynamic_exam_spec.rb +1 -1
  134. data/spec/helpers/exercise_input_helper_spec.rb +25 -0
  135. data/spec/helpers/test_results_rendering_spec.rb +7 -7
  136. data/spec/models/assignment_spec.rb +17 -2
  137. data/spec/models/discussion_spec.rb +153 -0
  138. metadata +108 -27
  139. data/app/models/concerns/with_status.rb +0 -43
  140. data/app/models/status_rendering_verbosity.rb +0 -40
  141. data/lib/mumuki/laboratory/controllers/messages.rb +0 -9
  142. data/lib/mumuki/laboratory/status/aborted.rb +0 -7
  143. data/lib/mumuki/laboratory/status/base.rb +0 -47
  144. data/lib/mumuki/laboratory/status/errored.rb +0 -15
  145. data/lib/mumuki/laboratory/status/manual_evaluation_pending.rb +0 -15
  146. data/lib/mumuki/laboratory/status/pending.rb +0 -11
  147. data/lib/mumuki/laboratory/status/running.rb +0 -11
  148. data/spec/models/randomizer_spec.rb +0 -18
  149. data/spec/models/verbosity_spec.rb +0 -24
@@ -0,0 +1,11 @@
1
+ module WithDiscussionStatus
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ serialize :status, Mumuki::Laboratory::Status::Discussion
6
+ validates_presence_of :status
7
+ scope :by_status, -> (status) { where(status: status) }
8
+ end
9
+
10
+ delegate :closed?, :opened?, :solved?, :pending_review?, :reachable_statuses, to: :status
11
+ end
@@ -0,0 +1,23 @@
1
+ module WithDiscussions
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :discussions, as: :item
6
+ end
7
+
8
+ def discuss!(user, discussion)
9
+ discussion.merge!(initiator_id: user.id)
10
+ discussion.merge!(submission: submission_for(user)) if submission_for(user).present?
11
+ created_discussion = discussions.create discussion
12
+ user.subscribe_to! created_discussion
13
+ created_discussion
14
+ end
15
+
16
+ def submission_for(_)
17
+ nil
18
+ end
19
+
20
+ def try_solve_discussions(user)
21
+ discussions.where(initiator: user).map(&:try_solve!)
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module WithRandomizations
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ serialize :randomizations, Hash
6
+ end
7
+
8
+ def seed
9
+ @seed || 0
10
+ end
11
+
12
+ def seed_with!(seed)
13
+ @seed = seed
14
+ end
15
+
16
+ def randomizer
17
+ @randomizer ||= Mumukit::Randomizer.parse(randomizations)
18
+ end
19
+
20
+ module ClassMethods
21
+ def randomize(*selectors)
22
+ selectors.each { |selector| randomize_field selector }
23
+ end
24
+
25
+ private
26
+
27
+ def randomize_field(selector)
28
+ define_method(selector) do |*args|
29
+ return unless super(*args)
30
+ randomizer.randomize!(super(*args), seed)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ module WithScopedQueries
2
+ extend ActiveSupport::Concern
3
+
4
+ SCOPING_METHODS = [Filter, Sort, Page]
5
+
6
+ included do
7
+ class_attribute :queriable_attributes, instance_writer: false
8
+ self.queriable_attributes = {}
9
+
10
+ SCOPING_METHODS.each do |mod|
11
+ define_singleton_method "#{mod.name.demodulize.underscore}able" do |*attributes|
12
+ include mod
13
+ mod.add_queriable_attributes_to(self, attributes)
14
+ end
15
+ end
16
+ end
17
+
18
+ class_methods do
19
+ def query_methods
20
+ queriable_attributes.keys
21
+ end
22
+
23
+ def scoped_query_module(method)
24
+ "WithScopedQueries::#{method.to_s.camelcase}".constantize
25
+ end
26
+
27
+ def permitted_query_params
28
+ queriable_attributes.values.flatten
29
+ end
30
+
31
+ def actual_params(params, excluded_param)
32
+ params.reject { |it| it == excluded_param.to_s }
33
+ end
34
+
35
+ def scoped_query_by(params, excluded_param=nil)
36
+ query_methods.inject(all) do |scope, method|
37
+ valid_params = valid_params_for(method, params, excluded_param)
38
+ scoped_query_module(method).query_by valid_params, scope, self
39
+ end
40
+ end
41
+
42
+ def valid_params_for(method, params, excluded_param)
43
+ actual_params = actual_params(params, excluded_param)
44
+ actual_params.permit queriable_attributes[method]
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ module WithScopedQueries::Filter
2
+ def self.query_by(params, current_scope, _)
3
+ params.to_h.inject(current_scope) do |scope, (field, value)|
4
+ if value.present?
5
+ scope.public_send("by_#{field}", value)
6
+ else
7
+ scope
8
+ end
9
+ end
10
+ end
11
+
12
+ def self.add_queriable_attributes_to(klass, attributes)
13
+ klass.queriable_attributes.merge!(filter: attributes)
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ module WithScopedQueries::Page
2
+ def self.query_by(params, current_scope, _)
3
+ page_param = params[:page] || 1
4
+ current_scope.page(page_param)
5
+ end
6
+
7
+ def self.add_queriable_attributes_to(klass, _)
8
+ klass.queriable_attributes.merge!(page: :page)
9
+ end
10
+ end
@@ -0,0 +1,47 @@
1
+ module WithScopedQueries::Sort
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ class_attribute :sorting_fields, :default_sorting_field, instance_writer: false
6
+ end
7
+
8
+ def self.query_by(params, scope, klass)
9
+ sort_param = params[:sort] || klass.default_sorting_field.to_s
10
+ normalized_params = normalize_params(sort_param)
11
+ if sort_param.present? && klass.sorting_params_allowed?(*normalized_params)
12
+ sort_method_for(klass, scope, *normalized_params)
13
+ else
14
+ scope
15
+ end
16
+ end
17
+
18
+ def self.normalize_params(param)
19
+ param&.split(/_(?!.*_)/)
20
+ end
21
+
22
+ def self.add_queriable_attributes_to(klass, attributes)
23
+ klass.default_sorting_field = attributes.extract_options![:default]
24
+ klass.sorting_fields = attributes
25
+ klass.queriable_attributes.merge!(sort: :sort)
26
+ end
27
+
28
+ def self.sort_method_for(klass, scope, field, direction)
29
+ if klass.column_names.include? field
30
+ scope.public_send(:order, "#{klass.table_name}.#{field} #{direction}")
31
+ else
32
+ scope.public_send("order_by_#{field}", direction)
33
+ end
34
+ end
35
+
36
+ class_methods do
37
+ def sorting_filters
38
+ sorting_fields.product([:asc, :desc]).map do |it|
39
+ "#{it.first}_#{it.second}"
40
+ end
41
+ end
42
+
43
+ def sorting_params_allowed?(sort_param, direction_param=nil)
44
+ sorting_fields.include?(sort_param.to_sym) && [:asc, :desc].include?(direction_param&.to_sym)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,128 @@
1
+ class Discussion < ApplicationRecord
2
+ include WithDiscussionStatus, ParentNavigation, WithScopedQueries, Contextualization
3
+
4
+ belongs_to :item, polymorphic: true
5
+ has_many :messages
6
+ belongs_to :initiator, class_name: 'User'
7
+ belongs_to :exercise, foreign_type: :exercise, foreign_key: 'item_id'
8
+ has_many :subscriptions
9
+ has_many :upvotes
10
+
11
+ scope :by_language, -> (language) { includes(:exercise).joins(exercise: :language).where(languages: {name: language}) }
12
+
13
+ before_save :capitalize_title
14
+ validates_presence_of :title
15
+
16
+ sortable :created_at, :upvotes_count, default: :created_at_desc
17
+ filterable :status, :language
18
+ pageable
19
+
20
+ delegate :language, to: :item
21
+ delegate :to_discussion_status, to: :status
22
+
23
+ scope :for_user, -> (user) do
24
+ if user.try(:moderator?)
25
+ all
26
+ else
27
+ where.not(status: :closed).where.not(status: :pending_review).or(where(initiator: user))
28
+ end
29
+ end
30
+
31
+ def try_solve!
32
+ if opened?
33
+ update! status: reachable_statuses_for(initiator).first
34
+ end
35
+ end
36
+
37
+ def capitalize_title
38
+ title.capitalize!
39
+ end
40
+
41
+ def used_in?(organization)
42
+ item.used_in?(organization)
43
+ end
44
+
45
+ def commentable_by?(user)
46
+ opened? && user.present?
47
+ end
48
+
49
+ def subscribable?
50
+ opened? || solved?
51
+ end
52
+
53
+ def has_submission?
54
+ submission.solution.present?
55
+ end
56
+
57
+ def read_by?(user)
58
+ subscription_for(user).read
59
+ end
60
+
61
+ def last_message_date
62
+ messages.last&.created_at || created_at
63
+ end
64
+
65
+ def friendly
66
+ title
67
+ end
68
+
69
+ def subscription_for(user)
70
+ subscriptions.find_by(user: user)
71
+ end
72
+
73
+ def upvote_for(user)
74
+ upvotes.find_by(user: user)
75
+ end
76
+
77
+ def unread_subscriptions(user)
78
+ subscriptions.where.not(user: user).map(&:unread!)
79
+ end
80
+
81
+ def submit_message!(message, user)
82
+ message.merge!(sender: user.uid)
83
+ messages.create(message)
84
+ unread_subscriptions(user)
85
+ end
86
+
87
+ def authorized?(user)
88
+ initiator?(user) || user.try(:moderator?)
89
+ end
90
+
91
+ def initiator?(user)
92
+ user.try(:uid) == initiator.uid
93
+ end
94
+
95
+ def reachable_statuses_for(user)
96
+ return [] unless authorized?(user)
97
+ status.reachable_statuses_for(user, self)
98
+ end
99
+
100
+ def reachable_status_for?(user, status)
101
+ reachable_statuses_for(user).include? status
102
+ end
103
+
104
+ def allowed_statuses_for(user)
105
+ status.allowed_statuses_for(user, self)
106
+ end
107
+
108
+ def update_status!(status, user)
109
+ update!(status: status) if reachable_status_for?(user, status)
110
+ end
111
+
112
+ def has_messages?
113
+ messages.exists?
114
+ end
115
+
116
+ def responses_count
117
+ messages.where.not(sender: initiator.uid).count
118
+ end
119
+
120
+ def has_responses?
121
+ responses_count > 0
122
+ end
123
+
124
+ def self.debatable_for(klazz, params)
125
+ debatable_id = params[:"#{klazz.underscore}_id"]
126
+ klazz.constantize.find(debatable_id)
127
+ end
128
+ end
@@ -6,7 +6,8 @@ class Exercise < ApplicationRecord
6
6
  FriendlyName,
7
7
  WithLanguage,
8
8
  Assistable,
9
- WithRandomizations
9
+ WithRandomizations,
10
+ WithDiscussions
10
11
 
11
12
  include Submittable,
12
13
  Questionable
@@ -67,6 +68,10 @@ class Exercise < ApplicationRecord
67
68
  defaulting_name { "#{navigable_parent.friendly} - #{name}" }
68
69
  end
69
70
 
71
+ def submission_for(user)
72
+ assignment_for(user)&.submission
73
+ end
74
+
70
75
  def new_solution
71
76
  Solution.new(content: default_content)
72
77
  end
@@ -133,6 +138,10 @@ class Exercise < ApplicationRecord
133
138
  false
134
139
  end
135
140
 
141
+ def inspection_keywords
142
+ {}
143
+ end
144
+
136
145
  private
137
146
 
138
147
  def evaluation_class
data/app/models/guide.rb CHANGED
@@ -37,7 +37,7 @@ class Guide < Content
37
37
  joins("left join public.assignments assignments
38
38
  on assignments.exercise_id = exercises.id
39
39
  and assignments.submitter_id = #{user.id}
40
- and assignments.status = #{Mumuki::Laboratory::Status::Passed.to_i}").
40
+ and assignments.submission_status = #{Mumuki::Laboratory::Status::Submission::Passed.to_i}").
41
41
  where('assignments.id is null')
42
42
  end
43
43
 
@@ -1,18 +1,39 @@
1
1
  class Message < ApplicationRecord
2
2
 
3
+ belongs_to :discussion, optional: true
3
4
  belongs_to :assignment, foreign_key: :submission_id, primary_key: :submission_id, optional: true
4
5
  has_one :exercise, through: :assignment
5
6
 
6
- validates_presence_of :submission_id, :content, :sender
7
-
7
+ validates_presence_of :content, :sender
8
+ validates_presence_of :submission_id, :unless => :discussion_id?
8
9
  markdown_on :content
9
10
 
10
11
  def notify!
11
12
  Mumukit::Nuntius.notify! 'student-messages', as_platform_json
12
13
  end
13
14
 
15
+ def from_initiator?
16
+ sender_user == discussion&.initiator
17
+ end
18
+
19
+ def from_user?(user)
20
+ sender_user == user
21
+ end
22
+
23
+ def sender_user
24
+ User.find_by(uid: sender)
25
+ end
26
+
27
+ def authorized?(user)
28
+ from_user?(user) || user&.moderator?
29
+ end
30
+
31
+ def authorize!
32
+ raise Mumukit::Auth::UnauthorizedAccessError unless authorized?(current_user)
33
+ end
34
+
14
35
  def as_platform_json
15
- as_json(except: [:id, :type],
36
+ as_json(except: [:id, :type, :discussion_id],
16
37
  include: {exercise: {only: [:bibliotheca_id]}})
17
38
  .merge(organization: Organization.current.name)
18
39
  end
@@ -92,6 +92,10 @@ class Organization < ApplicationRecord
92
92
  central? ? 'mumuki' : name
93
93
  end
94
94
 
95
+ def ask_for_help_enabled?
96
+ report_issue_enabled? || community_link.present? || forum_enabled?
97
+ end
98
+
95
99
  private
96
100
 
97
101
  def ensure_consistent_public_login
@@ -4,6 +4,6 @@ class Confirmation < PersistentSubmission
4
4
  end
5
5
 
6
6
  def try_evaluate!(*)
7
- {status: Mumuki::Laboratory::Status::Passed, result: ''}
7
+ {status: Mumuki::Laboratory::Status::Submission::Passed, result: ''}
8
8
  end
9
9
  end
@@ -7,7 +7,7 @@ class ConsoleSubmission < Submission
7
7
 
8
8
  def format_query_result!(results)
9
9
  results[:result] = I18n.t(:try_again) if results[:status] == :aborted
10
- results[:status] = results[:status].to_mumuki_status
10
+ results[:status] = results[:status].to_submission_status
11
11
  results
12
12
  end
13
13
  end
@@ -5,6 +5,19 @@ class Submission
5
5
 
6
6
  required :try_evaluate!
7
7
 
8
+ ATTRIBUTES = [:solution, :status, :result, :expectation_results, :feedback, :test_results,
9
+ :submission_id, :queries, :query_results, :manual_evaluation_comment]
10
+
11
+ attr_accessor *ATTRIBUTES
12
+
13
+ def self.from_attributes(*args)
14
+ new ATTRIBUTES.zip(args).to_h
15
+ end
16
+
17
+ def self.mapping_attributes
18
+ ATTRIBUTES
19
+ end
20
+
8
21
  def run!(assignment, evaluation)
9
22
  save_submission! assignment
10
23
  results = evaluation.evaluate! assignment, self