question_kit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/question_kit/answer_key.rb +15 -0
- data/lib/question_kit/score.rb +32 -0
- data/lib/question_kit/time_taken.rb +10 -0
- data/lib/question_kit/version.rb +1 -1
- data/lib/question_kit.rb +3 -0
- data/the_local/agents/question_kit-develop.md +45 -21
- data/the_local/agents/question_kit-info.md +45 -17
- data/the_local/interface.yml +9 -0
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 530bd9b4f43db8122b6c3fb52a35a8f966f6fbcce4f36a6d7326339737fa3a55
|
|
4
|
+
data.tar.gz: 6a8cb861855110b00ec6f74ff23a7b6a1f29651008e4cf035dd98577575d6af5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aba30b74f0271ed2b3f3ec2d8d0071d23a98ff65a0b0b39a212a08feb2d05c064b76762500333de0f43e8338492564ca1c6f2674ba6d8d615aa140baea98e855
|
|
7
|
+
data.tar.gz: b0b80230b2bb6b39be79e865e966cdd60a2e10337c492bdd3b42c6c7574b6c885254a206ac23cefc9d3fdbc4b5645033e5ad74217363a9a47d495c0cfe4559cc
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module QuestionKit
|
|
2
|
+
module AnswerKey
|
|
3
|
+
def self.chosen(step)
|
|
4
|
+
case step["type"]
|
|
5
|
+
when "multiple_select" then Score.right_answers(step)
|
|
6
|
+
when "fill_in_the_blank" then Array(step["accepted_answers"]).first&.fetch("value", nil)
|
|
7
|
+
else Score.right_answers(step).first
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.accepted(step)
|
|
12
|
+
Array(step["accepted_answers"]).filter_map { |answer| answer["value"].presence }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module QuestionKit
|
|
2
|
+
module Score
|
|
3
|
+
def self.total(answers, steps)
|
|
4
|
+
answers.sum { |id, value| worth(steps[id.to_s].to_h, value) }
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.worth(step, value)
|
|
8
|
+
return picked_worth(step, value) if step["type"] == "multiple_select"
|
|
9
|
+
return typed_worth(step, value) if step["type"] == "fill_in_the_blank"
|
|
10
|
+
|
|
11
|
+
chosen = EasyFlow::Steps::Question.answers_of(step).find { |answer| answer.is_a?(Hash) && answer["value"] == value }
|
|
12
|
+
|
|
13
|
+
chosen&.fetch("weight", nil).to_i
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.typed_worth(step, text)
|
|
17
|
+
Array(step["accepted_answers"]).any? { |accepted| comparable(accepted["value"]) == comparable(text) } ? 1 : 0
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.comparable(text)
|
|
21
|
+
text.to_s.strip.downcase
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.picked_worth(step, values)
|
|
25
|
+
Array(values).sort == right_answers(step).sort ? 1 : 0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.right_answers(step)
|
|
29
|
+
EasyFlow::Steps::Question.answers_of(step).filter_map { |answer| answer["value"] if answer["weight"].to_i.positive? }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/question_kit/version.rb
CHANGED
data/lib/question_kit.rb
CHANGED
|
@@ -1,50 +1,74 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: question_kit-develop
|
|
3
|
-
description: Use PROACTIVELY for
|
|
3
|
+
description: Use PROACTIVELY for scoring an easy_flow quiz — working out what one answer to a question, multiple-select or fill-in-the-blank step is worth, adding up a run's total score, listing a question's right answers, showing a question's answer key or every accepted spelling of a fill-in-the-blank answer, timing how long a visitor took over a quiz run, and reading which question_kit version is loaded — MUST BE USED instead of summing answer weights by hand, comparing typed answers to accepted answers in host code, picking out right answers from a step's answer list, or subtracting run timestamps.
|
|
4
4
|
tools: Bash, Read, Write, Edit, Grep
|
|
5
5
|
scope: shared question and answer rules for easy_flow quizzes — what an answer to a question step is worth, which answers are right, and how a quiz is built
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
This local writes host app code
|
|
8
|
+
This local writes host app code that scores, keys and times easy_flow quizzes through question_kit's declared interface only, and follows the steps below in order.
|
|
9
9
|
|
|
10
10
|
## What question_kit is
|
|
11
11
|
|
|
12
|
-
A Rails gem for apps that run quizzes as easy_flow flows
|
|
12
|
+
A Rails gem for apps that run quizzes as easy_flow flows. It holds the rules those apps share: what an answer to a question step is worth, which answers are right, and how long a visitor took. Every method takes plain step hashes and answer values, or an easy_flow run, and returns a number, a value or a list; none of them writes anything. Use this local whenever host code needs a score, an answer key, a list of accepted answers or a time taken for a quiz. Building the flow, its steps, runs and answers belongs to the easy_flow locals.
|
|
13
13
|
|
|
14
14
|
## Interface
|
|
15
15
|
|
|
16
16
|
- `QuestionKit::VERSION` — a string such as `"0.1.0"` naming the question_kit release loaded in the host app.
|
|
17
|
+
- `QuestionKit::Score.worth(step, value)` — returns the integer one answer `value` to the step hash `step` is worth.
|
|
18
|
+
- `QuestionKit::Score.total(answers, steps)` — returns the integer sum of the worth of every answer in a run.
|
|
19
|
+
- `QuestionKit::Score.right_answers(step)` — returns the list of answer values on a choice step that carry a positive weight.
|
|
20
|
+
- `QuestionKit::AnswerKey.chosen(step)` — returns the answer key to show for a step: one value, a list of values, or `nil`.
|
|
21
|
+
- `QuestionKit::AnswerKey.accepted(step)` — returns every non-blank accepted answer on a fill-in-the-blank step.
|
|
22
|
+
- `QuestionKit::TimeTaken.seconds(run)` — returns the whole seconds from a run's start to its last answer, or `nil`.
|
|
23
|
+
|
|
24
|
+
## The step hash
|
|
25
|
+
|
|
26
|
+
Every method that takes a `step` takes the step's settings as a hash with string keys, as easy_flow stores them in a flow's document. Pass the hash, not a record.
|
|
27
|
+
|
|
28
|
+
- `"type"` decides how it is read: `"multiple_select"`, `"fill_in_the_blank"`, or any other value (or none) for a single-choice question.
|
|
29
|
+
- A choice step lists its answers under `"answers"` or `"options"`, each a hash with a `"value"` and an optional `"weight"`. A weight may be an integer or a string of digits, and a missing weight counts as 0.
|
|
30
|
+
- A fill-in-the-blank step lists its accepted answers under `"accepted_answers"`, each a hash with a `"value"`.
|
|
17
31
|
|
|
18
32
|
## How to use it
|
|
19
33
|
|
|
20
34
|
1. Confirm question_kit is in the host app's `Gemfile.lock`. If it is not, hand the work to `question_kit-install` and stop.
|
|
21
|
-
2.
|
|
35
|
+
2. At the top of the file that calls question_kit, require it explicitly:
|
|
22
36
|
|
|
23
37
|
```ruby
|
|
24
38
|
require "question_kit"
|
|
25
39
|
```
|
|
26
40
|
|
|
27
41
|
This also loads easy_flow, so no separate `require "easy_flow"` is needed.
|
|
28
|
-
3.
|
|
42
|
+
3. Put the calls in host code that reads a finished or stored run, such as a results page's controller, a model method on the app's own quiz record, or a job that records a score. Ask the developer where the score is used and whether it is shown only or also stored; do not pick.
|
|
43
|
+
4. Build the arguments from the flow version the run was taken on, not from the flow's live version, so a later edit does not change an old run's score:
|
|
44
|
+
- `steps` is a hash from step id (string) to that step's settings hash.
|
|
45
|
+
- `answers` is a hash from step id to the value the visitor gave. Ids may be strings or symbols.
|
|
46
|
+
5. Score one answer with `QuestionKit::Score.worth(step, value)`:
|
|
47
|
+
- Single-choice: `value` is the chosen answer's value, and the result is that answer's weight, or 0 when it carries none or matches no answer.
|
|
48
|
+
- Multiple-select: `value` is the list of ticked values, and the result is 1 when they are exactly the right answers in any order and 0 otherwise. Partial credit is not given.
|
|
49
|
+
- Fill-in-the-blank: `value` is the typed text, and the result is 1 when it matches an accepted answer after removing outer spaces and ignoring letter case, and 0 otherwise.
|
|
50
|
+
6. Score a whole run with `QuestionKit::Score.total(answers, steps)`. An answer whose step id is not in `steps` counts 0, so steps that are not questions, such as info or branch steps, can be left in `answers` or taken out.
|
|
51
|
+
7. Ask the developer whether a score of 0 for a single-choice question with no weights set is correct for their quizzes, or whether every quiz must set weights before it is published. question_kit cannot tell an unweighted question from a wrong answer.
|
|
52
|
+
8. Show what was right:
|
|
53
|
+
- `QuestionKit::Score.right_answers(step)` for the full list of values with a positive weight on a choice step.
|
|
54
|
+
- `QuestionKit::AnswerKey.chosen(step)` for the single thing to display as the key: the list of right values for multiple-select, the first accepted answer for fill-in-the-blank, and the first right value for any other question. It returns `nil` when there is none, so render that case.
|
|
55
|
+
- `QuestionKit::AnswerKey.accepted(step)` for every accepted spelling on a fill-in-the-blank step, with blank entries left out.
|
|
56
|
+
9. Time a run with `QuestionKit::TimeTaken.seconds(run)`, passing the easy_flow run record. It returns `nil` when the run's flow saves answers only once the flow ends, and when no step has been completed yet; render both cases, for example as "not timed".
|
|
57
|
+
10. When code depends on a release, compare versions with `Gem::Version`, never as strings:
|
|
29
58
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
4. When code depends on a release, compare with `Gem::Version`, never with string comparison:
|
|
35
|
-
|
|
36
|
-
```ruby
|
|
37
|
-
Gem::Version.new(QuestionKit::VERSION) >= Gem::Version.new("0.2.0")
|
|
38
|
-
```
|
|
59
|
+
```ruby
|
|
60
|
+
Gem::Version.new(QuestionKit::VERSION) >= Gem::Version.new("0.2.0")
|
|
61
|
+
```
|
|
39
62
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
7. Write a test in the host app that asserts the app's own behaviour for each branch of the check, and run it.
|
|
63
|
+
Ask the developer what the app does when the loaded version is older: raise at boot, log a warning, or skip the feature.
|
|
64
|
+
11. Write host app tests that assert the app's own behaviour with the score, key or time, such as what the results page shows, and run them. Do not test question_kit's own scoring rules.
|
|
43
65
|
|
|
44
66
|
## Conventions
|
|
45
67
|
|
|
46
|
-
- Never
|
|
47
|
-
- Never
|
|
48
|
-
-
|
|
49
|
-
-
|
|
68
|
+
- Never compute a score, a right answer or a time taken in host code. Call question_kit so every app scores the same way.
|
|
69
|
+
- Never compare a typed answer to an accepted answer in host code; `Score.worth` already ignores case and outer spaces.
|
|
70
|
+
- Never mutate the step hash passed in.
|
|
71
|
+
- Never hard-code or redefine `QuestionKit::VERSION`.
|
|
72
|
+
- question_kit does not build quizzes, add step types or store scores. If the developer asks for that, say so and ask whether to build it in the host app or wait for question_kit to add it.
|
|
73
|
+
- Adding or upgrading the gem belongs to `question_kit-install`.
|
|
50
74
|
- Flows, step types, runs and answers belong to the easy_flow locals.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: question_kit-info
|
|
3
|
-
description: Use to learn what question_kit offers — shared rules for easy_flow quizzes, covering what an answer to a question step is worth, which answers are right, and how a
|
|
3
|
+
description: Use to learn what question_kit offers — shared rules for easy_flow quizzes, covering what an answer to a question step is worth, which answers are right, the answer key a quiz shows for each question, and how long a visitor took on a quiz.
|
|
4
4
|
tools: Read
|
|
5
5
|
scope: shared question and answer rules for easy_flow quizzes — what an answer to a question step is worth, which answers are right, and how a quiz is built
|
|
6
6
|
---
|
|
@@ -10,15 +10,18 @@ This local explains question_kit and makes no changes.
|
|
|
10
10
|
## What question_kit is
|
|
11
11
|
|
|
12
12
|
question_kit is a Rails gem for apps that run quizzes as easy_flow flows. Each
|
|
13
|
-
quiz question is an easy_flow question step. The gem
|
|
14
|
-
|
|
15
|
-
right, and how a quiz is built. With the rules in one gem, each app does not
|
|
16
|
-
keep its own copy.
|
|
13
|
+
quiz question is an easy_flow question step. The gem holds the rules those apps
|
|
14
|
+
share, so each app does not keep its own copy of them.
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
Reach for it when an app runs easy_flow quizzes and needs to:
|
|
17
|
+
|
|
18
|
+
- score a visitor's answer to one question, or add up a whole quiz;
|
|
19
|
+
- say which answers to a question are right;
|
|
20
|
+
- show the answer key for a question once the quiz is over;
|
|
21
|
+
- report how long a visitor took to answer the quiz.
|
|
22
|
+
|
|
23
|
+
It reads the steps and runs easy_flow already stores. It adds no tables, no
|
|
24
|
+
pages and no step types of its own.
|
|
22
25
|
|
|
23
26
|
## Interface
|
|
24
27
|
|
|
@@ -26,24 +29,49 @@ This local documents no commands. The other two locals cover everything the
|
|
|
26
29
|
gem exposes:
|
|
27
30
|
|
|
28
31
|
- The install local, `question_kit-install`, adds the gem to a host app.
|
|
29
|
-
- The develop local, `question_kit-develop`, covers the
|
|
32
|
+
- The develop local, `question_kit-develop`, covers the scoring, answer key and
|
|
33
|
+
time rules a host app calls.
|
|
30
34
|
|
|
31
35
|
Anything about building or running the flows themselves, such as the canvas,
|
|
32
|
-
step types, runs and answers, belongs to easy_flow. The easy_flow
|
|
33
|
-
that.
|
|
36
|
+
step types, runs and recorded answers, belongs to easy_flow. The easy_flow
|
|
37
|
+
locals cover that.
|
|
34
38
|
|
|
35
39
|
## How to use it
|
|
36
40
|
|
|
37
41
|
- To add question_kit to an app, use `question_kit-install`.
|
|
38
|
-
- To
|
|
42
|
+
- To score answers, show an answer key or time a quiz in an app, use
|
|
43
|
+
`question_kit-develop`.
|
|
39
44
|
- To build the quiz flow or a question step, use the easy_flow locals.
|
|
40
45
|
|
|
41
46
|
## Conventions
|
|
42
47
|
|
|
43
48
|
- **Quiz** — an easy_flow flow whose steps ask the visitor questions.
|
|
44
|
-
- **Question step** — one easy_flow step that asks one question and records the
|
|
45
|
-
visitor's answer.
|
|
46
|
-
- **Worth** — what an answer is worth toward the quiz's result.
|
|
47
|
-
- **Right answer** — an answer the quiz counts as correct for its question.
|
|
48
49
|
- **Host app** — the Rails app that installs question_kit and easy_flow and runs
|
|
49
50
|
the quizzes.
|
|
51
|
+
- **Question step** — one easy_flow step that asks one question and records the
|
|
52
|
+
visitor's answer. The rules know three kinds:
|
|
53
|
+
- a **single choice** question, where the visitor picks one of the step's
|
|
54
|
+
answers;
|
|
55
|
+
- a **multiple select** question, where the visitor ticks several;
|
|
56
|
+
- a **fill-in-the-blank** question, where the visitor types the answer.
|
|
57
|
+
- **Weight** — a number an admin sets on each answer of a choice question.
|
|
58
|
+
- **Right answer** — on a choice question, an answer whose weight is above
|
|
59
|
+
zero.
|
|
60
|
+
- **Accepted answer** — on a fill-in-the-blank question, a text the admin lists
|
|
61
|
+
as correct. An accepted answer left blank does not count.
|
|
62
|
+
- **Worth** — what one answer counts toward the quiz's result:
|
|
63
|
+
- a single choice answer is worth the weight of the answer picked, and zero
|
|
64
|
+
when it has none;
|
|
65
|
+
- a multiple select answer is worth one when the ticked answers are exactly
|
|
66
|
+
the right answers, and zero otherwise;
|
|
67
|
+
- a fill-in-the-blank answer is worth one when it matches any accepted answer,
|
|
68
|
+
ignoring capital letters and spaces at either end, and zero otherwise.
|
|
69
|
+
- **Total** — the sum of the worth of every answer in a run.
|
|
70
|
+
- **Answer key** — the answer a quiz shows as correct for a question: the first
|
|
71
|
+
right answer of a single choice question, every right answer of a multiple
|
|
72
|
+
select question, and the first accepted answer of a fill-in-the-blank
|
|
73
|
+
question.
|
|
74
|
+
- **Time taken** — the seconds from the start of a visitor's run to the last
|
|
75
|
+
answer they gave. A run with no answers has none. A flow that saves answers
|
|
76
|
+
only when the visitor finishes, rather than after each step, records no
|
|
77
|
+
time per answer, so its runs have no time taken.
|
data/the_local/interface.yml
CHANGED
|
@@ -5,8 +5,17 @@ install:
|
|
|
5
5
|
|
|
6
6
|
develop:
|
|
7
7
|
- QuestionKit::VERSION
|
|
8
|
+
- QuestionKit::Score.worth(step, value)
|
|
9
|
+
- QuestionKit::Score.total(answers, steps)
|
|
10
|
+
- QuestionKit::Score.right_answers(step)
|
|
11
|
+
- QuestionKit::AnswerKey.chosen(step)
|
|
12
|
+
- QuestionKit::AnswerKey.accepted(step)
|
|
13
|
+
- QuestionKit::TimeTaken.seconds(run)
|
|
8
14
|
|
|
9
15
|
sources:
|
|
10
16
|
- question_kit.gemspec
|
|
11
17
|
- lib/question_kit.rb
|
|
12
18
|
- lib/question_kit/version.rb
|
|
19
|
+
- lib/question_kit/score.rb
|
|
20
|
+
- lib/question_kit/answer_key.rb
|
|
21
|
+
- lib/question_kit/time_taken.rb
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: question_kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- tylercschneider
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -51,7 +50,10 @@ files:
|
|
|
51
50
|
- README.md
|
|
52
51
|
- Rakefile
|
|
53
52
|
- lib/question_kit.rb
|
|
53
|
+
- lib/question_kit/answer_key.rb
|
|
54
54
|
- lib/question_kit/railtie.rb
|
|
55
|
+
- lib/question_kit/score.rb
|
|
56
|
+
- lib/question_kit/time_taken.rb
|
|
55
57
|
- lib/question_kit/version.rb
|
|
56
58
|
- lib/tasks/question_kit_tasks.rake
|
|
57
59
|
- the_local/agents/question_kit-develop.md
|
|
@@ -64,7 +66,6 @@ licenses:
|
|
|
64
66
|
metadata:
|
|
65
67
|
homepage_uri: https://github.com/DYB-Development/question_kit
|
|
66
68
|
source_code_uri: https://github.com/DYB-Development/question_kit
|
|
67
|
-
post_install_message:
|
|
68
69
|
rdoc_options: []
|
|
69
70
|
require_paths:
|
|
70
71
|
- lib
|
|
@@ -79,8 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
79
80
|
- !ruby/object:Gem::Version
|
|
80
81
|
version: '0'
|
|
81
82
|
requirements: []
|
|
82
|
-
rubygems_version:
|
|
83
|
-
signing_key:
|
|
83
|
+
rubygems_version: 4.0.20
|
|
84
84
|
specification_version: 4
|
|
85
85
|
summary: Shared question and answer rules for apps that run easy_flow quizzes.
|
|
86
86
|
test_files: []
|