openstax_kitchen 11.0.0 → 11.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/Gemfile.lock +1 -1
- data/lib/kitchen/composite_page_element.rb +8 -0
- data/lib/kitchen/directions/bake_chapter_introductions/bake_chapter_objectives.rb +46 -0
- data/lib/kitchen/directions/bake_chapter_introductions/bake_chapter_outline.rb +14 -0
- data/lib/kitchen/directions/bake_chapter_introductions/main.rb +43 -0
- data/lib/kitchen/directions/bake_chapter_introductions/v1.rb +56 -0
- data/lib/kitchen/directions/bake_chapter_introductions/v2.rb +91 -0
- data/lib/kitchen/directions/bake_custom_sections/main.rb +14 -0
- data/lib/kitchen/directions/bake_custom_sections/v1.rb +42 -0
- data/lib/kitchen/directions/bake_equations.rb +2 -3
- data/lib/kitchen/directions/bake_example.rb +4 -5
- data/lib/kitchen/directions/bake_figure.rb +5 -3
- data/lib/kitchen/directions/bake_iframes/main.rb +11 -0
- data/lib/kitchen/directions/bake_iframes/v1.rb +25 -0
- data/lib/kitchen/directions/bake_index/main.rb +2 -2
- data/lib/kitchen/directions/bake_index/v1.rb +39 -7
- data/lib/kitchen/directions/bake_index/v1.xhtml.erb +3 -3
- data/lib/kitchen/directions/bake_injected_exercise.rb +18 -0
- data/lib/kitchen/directions/bake_injected_exercise_question.rb +71 -0
- data/lib/kitchen/directions/bake_link_placeholders.rb +15 -2
- data/lib/kitchen/directions/bake_lists_with_para.rb +3 -3
- data/lib/kitchen/directions/bake_notes/bake_autotitled_notes.rb +5 -5
- data/lib/kitchen/directions/bake_notes/bake_note_subtitle.rb +6 -2
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/main.rb +13 -3
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v1.rb +29 -25
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v2.rb +22 -17
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v3.rb +27 -22
- data/lib/kitchen/directions/bake_numbered_exercise/main.rb +3 -2
- data/lib/kitchen/directions/bake_numbered_exercise/v1.rb +6 -24
- data/lib/kitchen/directions/bake_numbered_table/bake_table_body.rb +3 -3
- data/lib/kitchen/directions/bake_numbered_table/main.rb +4 -4
- data/lib/kitchen/directions/bake_numbered_table/v1.rb +3 -3
- data/lib/kitchen/directions/bake_numbered_table/v2.rb +3 -3
- data/lib/kitchen/directions/bake_toc.rb +1 -1
- data/lib/kitchen/directions/move_solutions_to_answer_key/move_solutions_from_exercise_section.rb +1 -7
- data/lib/kitchen/directions/move_solutions_to_answer_key/move_solutions_from_numbered_note.rb +1 -7
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/contemporary_math.rb +17 -0
- data/lib/kitchen/element_base.rb +38 -2
- data/lib/kitchen/element_enumerator_base.rb +35 -0
- data/lib/kitchen/injected_question_element.rb +77 -0
- data/lib/kitchen/injected_question_element_enumerator.rb +21 -0
- data/lib/kitchen/selectors/base.rb +6 -0
- data/lib/kitchen/selectors/standard_1.rb +3 -0
- data/lib/kitchen/solution_element_enumerator.rb +21 -0
- data/lib/kitchen/version.rb +1 -1
- data/lib/locales/en.yml +6 -4
- data/lib/locales/es.yml +5 -4
- data/lib/locales/pl.yml +52 -7
- metadata +17 -6
- data/lib/kitchen/directions/bake_chapter_introductions/chapter_introduction.xhtml.erb +0 -0
- data/lib/kitchen/directions/bake_chapter_introductions.rb +0 -65
- data/lib/kitchen/directions/bake_notes/bake_note_iframes.rb +0 -27
@@ -1,29 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Kitchen::Directions
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
classes
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
module Kitchen::Directions
|
4
|
+
module BakeNumberedNotes
|
5
|
+
class V3
|
6
|
+
# for the try it notes, must be called AFTER bake_exercises
|
7
|
+
def bake(book:, classes:, suppress_solution: true)
|
8
|
+
classes.each do |klass|
|
9
|
+
book.chapters.pages.notes("$.#{klass}").each do |note|
|
10
|
+
note.wrap_children(class: 'os-note-body')
|
11
|
+
previous_example = note.previous
|
12
|
+
os_number = previous_example&.first('.os-number')&.children&.to_s
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
)
|
21
|
-
|
22
|
-
note.title&.trash
|
23
|
-
note.exercises.each do |exercise|
|
24
|
-
Kitchen::Directions::BakeNumberedNotes.bake_note_exercise(
|
25
|
-
note: note, exercise: exercise, divider: '. ', suppress_solution: suppress_solution
|
14
|
+
note.prepend(child:
|
15
|
+
<<~HTML
|
16
|
+
<h3 class="os-title">
|
17
|
+
<span class="os-title-label">#{note.autogenerated_title}</span>
|
18
|
+
<span class="os-number">#{os_number}</span>
|
19
|
+
</h3>
|
20
|
+
HTML
|
26
21
|
)
|
22
|
+
|
23
|
+
note.title&.trash
|
24
|
+
note.exercises.each do |exercise|
|
25
|
+
BakeNumberedNotes.bake_note_exercise(
|
26
|
+
note: note, exercise: exercise, divider: '. ', suppress_solution: suppress_solution
|
27
|
+
)
|
28
|
+
end
|
29
|
+
note.injected_questions.each do |question|
|
30
|
+
BakeNumberedNotes.bake_note_injected_question(note: note, question: question)
|
31
|
+
end
|
27
32
|
end
|
28
33
|
end
|
29
34
|
end
|
@@ -3,9 +3,10 @@
|
|
3
3
|
module Kitchen
|
4
4
|
module Directions
|
5
5
|
module BakeNumberedExercise
|
6
|
-
def self.v1(exercise:, number:, suppress_solution_if: false,
|
6
|
+
def self.v1(exercise:, number:, suppress_solution_if: false,
|
7
|
+
note_suppressed_solutions: false, cases: false)
|
7
8
|
V1.new.bake(exercise: exercise, number: number, suppress_solution_if: suppress_solution_if,
|
8
|
-
note_suppressed_solutions: note_suppressed_solutions)
|
9
|
+
note_suppressed_solutions: note_suppressed_solutions, cases: cases)
|
9
10
|
end
|
10
11
|
|
11
12
|
def self.bake_solution_v1(exercise:, number:, divider: '. ')
|
@@ -2,14 +2,15 @@
|
|
2
2
|
|
3
3
|
module Kitchen::Directions::BakeNumberedExercise
|
4
4
|
class V1
|
5
|
-
def bake(exercise:, number:, suppress_solution_if: false,
|
5
|
+
def bake(exercise:, number:, suppress_solution_if: false,
|
6
|
+
note_suppressed_solutions: false, cases: false)
|
6
7
|
problem = exercise.problem
|
7
8
|
solution = exercise.solution
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
# Store label information
|
11
|
+
label_number = "#{exercise.ancestor(:chapter).count_in(:book)}.#{number}"
|
12
|
+
exercise.target_label(label_text: 'exercise', custom_content: label_number, cases: cases)
|
13
|
+
|
13
14
|
problem_number = "<span class='os-number'>#{number}</span>"
|
14
15
|
|
15
16
|
suppress_solution =
|
@@ -37,25 +38,6 @@ module Kitchen::Directions::BakeNumberedExercise
|
|
37
38
|
<div class="os-problem-container">#{problem.children}</div>
|
38
39
|
HTML
|
39
40
|
)
|
40
|
-
|
41
|
-
# Bake multipart questions
|
42
|
-
multipart_questions = problem.search('div.question-stem')
|
43
|
-
return unless multipart_questions.count > 1
|
44
|
-
|
45
|
-
multipart_clipboard = Kitchen::Clipboard.new
|
46
|
-
multipart_questions.each do |question|
|
47
|
-
question.wrap('<li>')
|
48
|
-
question = question.parent
|
49
|
-
question.cut(to: multipart_clipboard)
|
50
|
-
end
|
51
|
-
|
52
|
-
problem.first('div.question-stimulus, div.exercise-stimulus').append(sibling:
|
53
|
-
<<~HTML
|
54
|
-
<ol type="a">
|
55
|
-
#{multipart_clipboard.paste}
|
56
|
-
</ol>
|
57
|
-
HTML
|
58
|
-
)
|
59
41
|
end
|
60
42
|
|
61
43
|
def bake_solution(exercise:, number:, divider: '. ')
|
@@ -36,12 +36,12 @@ module Kitchen
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def bake(table:, number:)
|
39
|
+
def bake(table:, number:, cases: false)
|
40
40
|
table.remove_attribute('summary')
|
41
41
|
table.wrap(%(<div class="os-table">))
|
42
42
|
|
43
|
-
|
44
|
-
table.
|
43
|
+
# Store label information
|
44
|
+
table.target_label(label_text: 'table', custom_content: number, cases: cases)
|
45
45
|
|
46
46
|
if table.top_titled?
|
47
47
|
custom_table = CustomBody.new(table: table,
|
@@ -3,12 +3,12 @@
|
|
3
3
|
module Kitchen
|
4
4
|
module Directions
|
5
5
|
module BakeNumberedTable
|
6
|
-
def self.v1(table:, number:, always_caption: false)
|
7
|
-
V1.new.bake(table: table, number: number, always_caption: always_caption)
|
6
|
+
def self.v1(table:, number:, always_caption: false, cases: false)
|
7
|
+
V1.new.bake(table: table, number: number, always_caption: always_caption, cases: cases)
|
8
8
|
end
|
9
9
|
|
10
|
-
def self.v2(table:, number:)
|
11
|
-
V2.new.bake(table: table, number: number)
|
10
|
+
def self.v2(table:, number:, cases: false)
|
11
|
+
V2.new.bake(table: table, number: number, cases: cases)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -3,8 +3,8 @@
|
|
3
3
|
module Kitchen::Directions::BakeNumberedTable
|
4
4
|
class V1
|
5
5
|
|
6
|
-
def bake(table:, number:, always_caption: false)
|
7
|
-
Kitchen::Directions::BakeTableBody::V1.new.bake(table: table, number: number)
|
6
|
+
def bake(table:, number:, always_caption: false, cases: false)
|
7
|
+
Kitchen::Directions::BakeTableBody::V1.new.bake(table: table, number: number, cases: cases)
|
8
8
|
|
9
9
|
# TODO: extra spaces added here to match legacy implementation, but probably not meaningful?
|
10
10
|
new_caption = ''
|
@@ -31,7 +31,7 @@ module Kitchen::Directions::BakeNumberedTable
|
|
31
31
|
table.append(sibling:
|
32
32
|
<<~HTML
|
33
33
|
<div class="os-caption-container">
|
34
|
-
<span class="os-title-label">#{I18n.t(
|
34
|
+
<span class="os-title-label">#{I18n.t("table#{'.nominative' if cases}")} </span>
|
35
35
|
<span class="os-number">#{number}</span>
|
36
36
|
<span class="os-divider"> </span>#{caption_title}
|
37
37
|
<span class="os-divider"> </span>#{new_caption}
|
@@ -5,8 +5,8 @@ module Kitchen::Directions::BakeNumberedTable
|
|
5
5
|
# V2 caption titles are nested within an .os-caption span
|
6
6
|
class V2
|
7
7
|
|
8
|
-
def bake(table:, number:)
|
9
|
-
Kitchen::Directions::BakeTableBody::V1.new.bake(table: table, number: number)
|
8
|
+
def bake(table:, number:, cases: false)
|
9
|
+
Kitchen::Directions::BakeTableBody::V1.new.bake(table: table, number: number, cases: cases)
|
10
10
|
|
11
11
|
caption = ''
|
12
12
|
if table&.caption&.first("span[data-type='title']") && !table.top_captioned?
|
@@ -19,7 +19,7 @@ module Kitchen::Directions::BakeNumberedTable
|
|
19
19
|
table.append(sibling:
|
20
20
|
<<~HTML
|
21
21
|
<div class="os-caption-container">
|
22
|
-
<span class="os-title-label">#{I18n.t(
|
22
|
+
<span class="os-title-label">#{I18n.t("table#{'.nominative' if cases}")} </span>
|
23
23
|
<span class="os-number">#{number}</span>
|
24
24
|
<span class="os-divider"> </span>
|
25
25
|
#{caption}
|
@@ -103,7 +103,7 @@ module Kitchen
|
|
103
103
|
raise "do not know what TOC class to use for page with classes #{page.classes}"
|
104
104
|
end
|
105
105
|
when CompositePageElement
|
106
|
-
if page.is_index?
|
106
|
+
if page.is_index? || page.is_index_of_type?
|
107
107
|
'os-toc-index'
|
108
108
|
elsif page.is_citation_reference?
|
109
109
|
'os-toc-reference'
|
data/lib/kitchen/directions/move_solutions_to_answer_key/move_solutions_from_exercise_section.rb
CHANGED
@@ -8,13 +8,7 @@ module Kitchen::Directions::MoveSolutionsFromExerciseSection
|
|
8
8
|
|
9
9
|
class V1
|
10
10
|
def bake(chapter:, append_to:, section_class:, title_number:)
|
11
|
-
solutions_clipboard =
|
12
|
-
chapter.search("section.#{section_class}").exercises.each do |exercise|
|
13
|
-
solution = exercise.solution
|
14
|
-
next unless solution
|
15
|
-
|
16
|
-
solution.cut(to: solutions_clipboard)
|
17
|
-
end
|
11
|
+
solutions_clipboard = chapter.search("section.#{section_class}").solutions.cut
|
18
12
|
|
19
13
|
return if solutions_clipboard.items.empty?
|
20
14
|
|
data/lib/kitchen/directions/move_solutions_to_answer_key/move_solutions_from_numbered_note.rb
CHANGED
@@ -7,13 +7,7 @@ module Kitchen::Directions::MoveSolutionsFromNumberedNote
|
|
7
7
|
|
8
8
|
class V1
|
9
9
|
def bake(chapter:, append_to:, note_class:)
|
10
|
-
solutions_clipboard =
|
11
|
-
chapter.notes("$.#{note_class}").exercises.each do |exercise|
|
12
|
-
solution = exercise.solution
|
13
|
-
next unless solution
|
14
|
-
|
15
|
-
solution.cut(to: solutions_clipboard)
|
16
|
-
end
|
10
|
+
solutions_clipboard = chapter.notes("$.#{note_class}").solutions.cut
|
17
11
|
|
18
12
|
return if solutions_clipboard.items.empty?
|
19
13
|
|
@@ -17,6 +17,23 @@ module Kitchen::Directions::MoveSolutionsToAnswerKey
|
|
17
17
|
Kitchen::Directions::MoveSolutionsFromNumberedNote.v1(
|
18
18
|
chapter: chapter, append_to: append_to, note_class: 'your-turn'
|
19
19
|
)
|
20
|
+
|
21
|
+
# Bake section exercises
|
22
|
+
chapter.non_introduction_pages.each do |page|
|
23
|
+
number = "#{chapter.count_in(:book)}.#{page.count_in(:chapter)}"
|
24
|
+
Kitchen::Directions::MoveSolutionsFromExerciseSection.v1(
|
25
|
+
chapter: page, append_to: append_to, section_class: 'section-exercises',
|
26
|
+
title_number: number
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Bake other exercise sections
|
31
|
+
classes = %w[chapter-review chapter-test]
|
32
|
+
classes.each do |klass|
|
33
|
+
Kitchen::Directions::MoveSolutionsFromExerciseSection.v1(
|
34
|
+
chapter: chapter, append_to: append_to, section_class: klass
|
35
|
+
)
|
36
|
+
end
|
20
37
|
end
|
21
38
|
end
|
22
39
|
end
|
data/lib/kitchen/element_base.rb
CHANGED
@@ -67,6 +67,9 @@ module Kitchen
|
|
67
67
|
# @!method remove_attribute
|
68
68
|
# Removes an attribute from the element
|
69
69
|
# @see https://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node#remove_attribute-instance_method Nokogiri::XML::Node#remove_attribute
|
70
|
+
# @!method key?(attribute)
|
71
|
+
# Returns true if attribute is set
|
72
|
+
# @see https://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node#key%3F-instance_method Nokogiri::XML::Node#key?(attribute)
|
70
73
|
# @!method classes
|
71
74
|
# Gets the element's classes
|
72
75
|
# @see https://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node#classes-instance_method Nokogiri::XML::Node#classes
|
@@ -81,7 +84,7 @@ module Kitchen
|
|
81
84
|
# @return Object
|
82
85
|
def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
|
83
86
|
:text, :wrap, :children, :to_html, :remove_attribute,
|
84
|
-
:classes, :path, :inner_html=
|
87
|
+
:key?, :classes, :path, :inner_html=
|
85
88
|
|
86
89
|
# @!method config
|
87
90
|
# Get the config for this element's document
|
@@ -722,11 +725,44 @@ module Kitchen
|
|
722
725
|
end
|
723
726
|
end
|
724
727
|
|
728
|
+
# Creates labels for links to inside elements
|
729
|
+
# like Figures, Tables, Equations, Exercises, Notes.
|
730
|
+
#
|
731
|
+
# @param label_text [String] label of the element defined in yml file.
|
732
|
+
# (e.g. "Figure", "Table", "Equation")
|
733
|
+
# @param custom_content [String] might be numbering of the element or text
|
734
|
+
# copied from content (e.g. note title)
|
735
|
+
# @param cases [Boolean] true if labels should use grammatical cases
|
736
|
+
# (used in Polish books)
|
737
|
+
# @return [Pantry]
|
738
|
+
#
|
739
|
+
def target_label(label_text: nil, custom_content: nil, cases: false)
|
740
|
+
if cases
|
741
|
+
cases = %w[nominative genitive dative accusative instrumental locative vocative]
|
742
|
+
element_labels = {}
|
743
|
+
|
744
|
+
cases.each do |label_case|
|
745
|
+
element_labels[label_case] = "#{I18n.t("#{label_text}.#{label_case}")} #{custom_content}"
|
746
|
+
|
747
|
+
element_label_case = element_labels[label_case]
|
748
|
+
|
749
|
+
pantry(name: "#{label_case}_link_text").store element_label_case, label: id if id
|
750
|
+
end
|
751
|
+
else
|
752
|
+
element_label = if label_text
|
753
|
+
"#{I18n.t(label_text.to_s)} #{custom_content}"
|
754
|
+
else
|
755
|
+
custom_content
|
756
|
+
end
|
757
|
+
pantry(name: :link_text).store element_label, label: id if id
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
725
761
|
# @!method pages
|
726
762
|
# Returns a pages enumerator
|
727
763
|
def_delegators :as_enumerator, :pages, :chapters, :terms, :figures, :notes, :tables, :examples,
|
728
764
|
:metadatas, :non_introduction_pages, :units, :titles, :exercises, :references,
|
729
|
-
:composite_pages, :composite_chapters
|
765
|
+
:composite_pages, :composite_chapters, :solutions, :injected_questions
|
730
766
|
|
731
767
|
# Returns this element as an enumerator (over only one element, itself)
|
732
768
|
#
|
@@ -283,6 +283,41 @@ module Kitchen
|
|
283
283
|
chain_to(MetadataElementEnumerator, css_or_xpath: css_or_xpath, only: only, except: except)
|
284
284
|
end
|
285
285
|
|
286
|
+
# Returns an enumerator that iterates through solutions within the scope of this enumerator
|
287
|
+
#
|
288
|
+
# @param css_or_xpath [String] additional selectors to further narrow the element iterated over;
|
289
|
+
# a "$" in this argument will be replaced with the default selector for the element being
|
290
|
+
# iterated over.
|
291
|
+
# @param only [Symbol, Callable] the name of a method to call on an element or a
|
292
|
+
# lambda or proc that accepts an element; elements will only be included in the
|
293
|
+
# search results if the method or callable returns true
|
294
|
+
# @param except [Symbol, Callable] the name of a method to call on an element or a
|
295
|
+
# lambda or proc that accepts an element; elements will not be included in the
|
296
|
+
# search results if the method or callable returns false
|
297
|
+
#
|
298
|
+
def solutions(css_or_xpath=nil, only: nil, except: nil)
|
299
|
+
block_error_if(block_given?)
|
300
|
+
chain_to(SolutionElementEnumerator, css_or_xpath: css_or_xpath, only: only, except: except)
|
301
|
+
end
|
302
|
+
|
303
|
+
# Returns an enumerator that iterates through injected questions within the scope of this enumerator
|
304
|
+
#
|
305
|
+
# @param css_or_xpath [String] additional selectors to further narrow the element iterated over;
|
306
|
+
# a "$" in this argument will be replaced with the default selector for the element being
|
307
|
+
# iterated over.
|
308
|
+
# @param only [Symbol, Callable] the name of a method to call on an element or a
|
309
|
+
# lambda or proc that accepts an element; elements will only be included in the
|
310
|
+
# search results if the method or callable returns true
|
311
|
+
# @param except [Symbol, Callable] the name of a method to call on an element or a
|
312
|
+
# lambda or proc that accepts an element; elements will not be included in the
|
313
|
+
# search results if the method or callable returns false
|
314
|
+
#
|
315
|
+
def injected_questions(css_or_xpath=nil, only: nil, except: nil)
|
316
|
+
block_error_if(block_given?)
|
317
|
+
chain_to(InjectedQuestionElementEnumerator,
|
318
|
+
css_or_xpath: css_or_xpath, only: only, except: except)
|
319
|
+
end
|
320
|
+
|
286
321
|
# Returns an enumerator that iterates within the scope of this enumerator
|
287
322
|
#
|
288
323
|
# @param css_or_xpath [String] additional selectors to further narrow the element iterated over
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen
|
4
|
+
# An element for an example
|
5
|
+
#
|
6
|
+
class InjectedQuestionElement < ElementBase
|
7
|
+
|
8
|
+
# Creates a new +InjectedQuestionElement+
|
9
|
+
#
|
10
|
+
# @param node [Nokogiri::XML::Node] the node this element wraps
|
11
|
+
# @param document [Document] this element's document
|
12
|
+
#
|
13
|
+
def initialize(node:, document: nil)
|
14
|
+
super(node: node,
|
15
|
+
document: document,
|
16
|
+
enumerator_class: InjectedQuestionElementEnumerator)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the short type
|
20
|
+
# @return [Symbol]
|
21
|
+
#
|
22
|
+
def self.short_type
|
23
|
+
:injected_question
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the question stimulus as an element.
|
27
|
+
#
|
28
|
+
# @return [Element]
|
29
|
+
#
|
30
|
+
def stimulus
|
31
|
+
first('div[data-type="question-stimulus"]')
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the question stem as an element.
|
35
|
+
#
|
36
|
+
# @return [Element]
|
37
|
+
#
|
38
|
+
def stem
|
39
|
+
first('div[data-type="question-stem"]')
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the list of answers as an element.
|
43
|
+
#
|
44
|
+
# @return [Element]
|
45
|
+
#
|
46
|
+
def answers
|
47
|
+
first("ol[data-type='question-answers']")
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the solution element.
|
51
|
+
#
|
52
|
+
# @return [Element]
|
53
|
+
#
|
54
|
+
def solution
|
55
|
+
first("div[data-type='question-solution']")
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the answer correctness given an alphabet
|
59
|
+
#
|
60
|
+
# @return [Array]
|
61
|
+
#
|
62
|
+
def correct_answer_letters(alphabet)
|
63
|
+
answers.search('li[data-type="question-answer"]').each_with_index.map \
|
64
|
+
do |answer, index|
|
65
|
+
answer[:'data-correctness'] == '1.0' ? alphabet[index] : nil
|
66
|
+
end.compact
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns or creates the question's id
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
#
|
73
|
+
def id
|
74
|
+
self[:id] ||= "auto_#{ancestor(:page).id.gsub(/page_/, '')}_#{self[:'data-id']}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|