├── .gitignore
├── LICENSE
├── README.md
├── assets
├── icons
│ ├── correct.png
│ └── incorrect.png
└── images
│ ├── Animals.jpeg
│ ├── Presidents.jpeg
│ └── us_map.png
├── capsule.bxb
├── code
├── ExtractAnswerFromOption.js
├── FindQuiz.js
├── StartQuiz.js
├── UpdateQuiz.js
├── data
│ └── quizzes.js
└── lib
│ └── util.js
├── contentUtility
└── CreateQuizContent.xlsx
├── models
├── actions
│ ├── FindQuiz.model.bxb
│ ├── StartQuiz.model.bxb
│ ├── UpdateQuiz.model.bxb
│ └── utils
│ │ └── ExtractAnswerFromOption.model.bxb
└── concepts
│ ├── primitive
│ ├── Answer.model.bxb
│ └── SearchTerm.model.bxb
│ └── structures
│ ├── CorrectAnswer.model.bxb
│ ├── Option.model.bxb
│ ├── Question.model.bxb
│ └── Quiz.model.bxb
└── resources
├── base
└── views
│ ├── FindQuiz.view.bxb
│ ├── QuizCompleted.view.bxb
│ ├── UpdateQuiz.view.bxb
│ └── macros
│ ├── QuizCompleted.layout.bxb
│ └── QuizDetails.layout.bxb
└── en
├── capsule-info.bxb
├── dialogs
├── NoQuiz.dialog.bxb
└── macros
│ ├── FindQuiz.dialog.bxb
│ ├── LayoutMacros.dialog.bxb
│ └── UpdateQuiz.dialog.bxb
├── example-quiz.hints.bxb
├── quiz.endpoints.bxb
└── training
├── t-4.training.bxb
├── t-5.training.bxb
├── t-7.training.bxb
├── t-8.training.bxb
├── t-a.training.bxb
├── t-h.training.bxb
├── t-i.training.bxb
├── t-p.training.bxb
├── t-q.training.bxb
├── t-r.training.bxb
└── t-x.training.bxb
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Bixby Quiz Sample Capsule
5 |
6 |
7 | ## Overview
8 |
9 | Do you have a great idea for a quiz that will entertain and delight Bixby users? Do you want to create a fun quiz to help you learn something new? A quiz to stump your friends? The quiz sample capsule is the perfect place to start building a Bixby quiz capsule!
10 |
11 | Both the fundamentals of a Bixby Capsule and some more intermediate/advanced concepts are used. This is a great capsule to learn even more about how to develop for Bixby.
12 |
13 | You can easily customize this capsule with your own quiz without any development! Just use the spreadsheet in the `contentUtility` folder and cut and paste from there into the `quizzes.js` file, update the `capsule.id` and `training` as necessary and you have made this capsule your own!
14 |
15 | ## How to get started
16 |
17 | * Download and install the Bixby Studio IDE from the [Bixby Developer Center](http://bixbydevelopers.com)
18 | * Download this capsule (zip is the easiest way) from Github. Unzip in your directory of choice
19 | * Open the Capsule in Bixby Studio
20 | * Open the simulator and give it a try!
21 |
22 | ## How to try
23 | Ask Bixby to start a quiz, try these:
24 | ```
25 | Start a quiz (Bixby asks you which quiz you want to start)
26 |
27 | Start a states quiz (Bixby starts a quiz about US States)
28 |
29 | Start a presidents quiz (Bixby starts a quiz about US presidents)
30 |
31 | Start a funny quiz (Bixby starts a quiz that is funny)
32 |
33 | ```
34 |
35 | # How to customize
36 | * Change the capsule id to reflect your organization and your content. The capsule id is defined in `capsule.bxb` file.
37 | * Create your own quiz questions and answers by editing the `code/data/quizzes.js` file. You may also use the spreadsheet located in `contentUtility/createQuizContent` The `quizzes.js` file has comments that explain each of the fields and how they customize a quiz
38 | * You can customize the Quiz NoResult dialog by modifying the `resources/en/dialogs/NoQuiz.dialog.bxb`
39 | * You can customize the Quiz Found dialog by modifying the `resources/base/views/FindQuiz.view.bxb` view file
40 | * You can customize the Quiz Question/Answer prompts by directly modifying the `resources/base/views/UpdateQuiz.view.bxb` file and by modifying the dialog macros in `resources/en/dialog/macros/`.
41 | * The training in this sample capsule is basic. You will likely need to add additional training for your quiz
42 | * Have fun!
43 |
44 | ---
45 |
46 | ## Additional Resources
47 |
48 | ### Your Source for Everything Bixby
49 | * [Bixby Developer Center](http://bixbydevelopers.com) - Everything you need to get started with Bixby Development!
50 | * [Bixby News, Blogs and Tutorials](https://bixby.developer.samsung.com/) - Bixby News, Tutorials, Blogs and Events
51 |
52 | ### Guides & Best Practices
53 | * [Quick Start Guide](https://bixbydevelopers.com/dev/docs/get-started/quick-start) - Build your first capsule
54 | * [Design Guides](https://bixbydevelopers.com/dev/docs/dev-guide/design-guides) - Best practices for designing your capsules
55 | * [Developer Guides](https://bixbydevelopers.com/dev/docs/dev-guide/developers) - Guides that take you from design and modeling all the way through deployment of your capsules
56 |
57 | ### Bixby Videos
58 | * [Bixby Developers YouTube Channel](https://www.youtube.com/c/bixbydevelopers) - Tutorial videos, Presentations, Capsule Demos and more
59 |
60 | ### Bixby Podcast
61 | * [Bixby Developers Chat](http://bixbydev.buzzsprout.com/) - Voice, Conversational AI and Bixby discussions
62 |
63 | ### Bixby on Social Media
64 | * [@BixbyDevelopers](https://twitter.com/bixbydevelopers) - Twitter
65 | * [Facebook](https://facebook.com/BixbyDevelopers)
66 | * [Instagram](https://www.instagram.com/bixbydevelopers/)
67 |
68 | ### Need Support?
69 | * Have a feature request? Please suggest it in our [Support Community](https://support.bixbydevelopers.com/hc/en-us/community/topics/360000183273-Feature-Requests) to help us prioritize.
70 | * Have a technical question? Ask on [Stack Overflow](https://stackoverflow.com/questions/tagged/bixby) with tag “bixby”
71 |
--------------------------------------------------------------------------------
/assets/icons/correct.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bixbydevelopers/capsule-sample-quiz/51eb3d267edd3f9b866b55131f01f27a5bf74286/assets/icons/correct.png
--------------------------------------------------------------------------------
/assets/icons/incorrect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bixbydevelopers/capsule-sample-quiz/51eb3d267edd3f9b866b55131f01f27a5bf74286/assets/icons/incorrect.png
--------------------------------------------------------------------------------
/assets/images/Animals.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bixbydevelopers/capsule-sample-quiz/51eb3d267edd3f9b866b55131f01f27a5bf74286/assets/images/Animals.jpeg
--------------------------------------------------------------------------------
/assets/images/Presidents.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bixbydevelopers/capsule-sample-quiz/51eb3d267edd3f9b866b55131f01f27a5bf74286/assets/images/Presidents.jpeg
--------------------------------------------------------------------------------
/assets/images/us_map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bixbydevelopers/capsule-sample-quiz/51eb3d267edd3f9b866b55131f01f27a5bf74286/assets/images/us_map.png
--------------------------------------------------------------------------------
/capsule.bxb:
--------------------------------------------------------------------------------
1 | capsule {
2 | id (example.quiz)
3 | version (0.2.0) //JSRS migration VCAP-24311
4 | format (3)
5 | targets {
6 | target (bixby-mobile-en-US)
7 | }
8 | capsule-imports {
9 | import (viv.core) {
10 | as (core)
11 | }
12 | }
13 | runtime-version (2) {
14 | js-runtime-version (2)
15 | }
16 | store-countries {
17 | all
18 | }
19 | store-sections {
20 | section (GamesAndFun)
21 | }
22 | }
--------------------------------------------------------------------------------
/code/ExtractAnswerFromOption.js:
--------------------------------------------------------------------------------
1 | //Convert the option structure to a text answer.
2 | export default function extractAnswerFromOption({ option }) {
3 | return option.text;
4 | }
5 |
--------------------------------------------------------------------------------
/code/FindQuiz.js:
--------------------------------------------------------------------------------
1 | import console from 'console';
2 | import { buildQuizzes } from "./lib/util.js";
3 |
4 | export default function ({ searchTerm }) {
5 | return buildQuizzes(searchTerm);
6 | }
7 |
--------------------------------------------------------------------------------
/code/StartQuiz.js:
--------------------------------------------------------------------------------
1 | export default function startQuiz({ quiz }) {
2 | return quiz;
3 | }
4 |
--------------------------------------------------------------------------------
/code/UpdateQuiz.js:
--------------------------------------------------------------------------------
1 | import * as util from "./lib/util";
2 |
3 | // Will repromt if the user does not give a valid input. Set to false to not reprompt
4 | // Futue enhancement would be to reprompt only n times and then give up (suggest n = 1 or 2)
5 | const reprompt = true;
6 |
7 | import console from 'console';
8 |
9 | export default function updateQuiz({ quiz, answer }) {
10 | answer = answer.toLowerCase();
11 | const i = quiz.index;
12 | const options = quiz.questions[i].options;
13 | const hasOptions = !(options === undefined || options.length == 0);
14 | const correctAnswers = quiz.questions[i].correctAnswer.acceptedAnswers;
15 | const correctAlias = String(
16 | quiz.questions[i].correctAnswer.acceptedAlias
17 | ).toLowerCase();
18 | const currentQuestion = quiz.questions[i];
19 | quiz.questions[i].answer = answer;
20 | quiz.questions[quiz.index].invalidAnswer = false;
21 | var correct = false;
22 | var answeredOption = false;
23 |
24 | // Check for match with answer option e.g. "California" or "B Calfornia" assuming B was the option letter
25 | // Matches answer only and answer in sentence
26 | correct = util.checkAnswerMatch(correctAnswers, answer);
27 |
28 | var possOptionAlias = [];
29 | if (!correct && hasOptions) {
30 | // Check if user answered an incorrect option text, also contruct possible aliases
31 | var ret = util.checkIncorrectOption(options, answer);
32 | answeredOption = ret.answeredOption;
33 | possOptionAlias = ret.possOptionAlias;
34 |
35 | if (!answeredOption) {
36 | // Did user answer with an alias and was the alias correct
37 | ret = util.checkAliasMatch(answer, possOptionAlias, correctAlias);
38 | answeredOption = ret.answeredOption;
39 | correct = ret.correct;
40 | }
41 | }
42 |
43 | if (correct) {
44 | quiz.questions[quiz.index].correct = true;
45 | quiz.score++;
46 | }
47 |
48 | if (!correct && !answeredOption && hasOptions && reprompt) {
49 | // Answered with an invalid option. Reprompt
50 | quiz.questions[quiz.index].invalidAnswer = true;
51 | var validOptionsText = util.arrayToListForSpeech(possOptionAlias);
52 | quiz.textToSpeak =
53 | util.buildQuestionToSpeak(quiz.questions[quiz.index]) +
54 | '. Please answer with a letter: ' +
55 | validOptionsText || ' ';
56 | } else if (quiz.index < quiz.questions.length - 1) {
57 | quiz.index++;
58 | quiz.textToSpeak =
59 | util.buildQuestionToSpeak(quiz.questions[quiz.index]) || ' ';
60 | } else {
61 | quiz.completed = true;
62 | }
63 | return quiz;
64 | }
65 |
--------------------------------------------------------------------------------
/code/data/quizzes.js:
--------------------------------------------------------------------------------
1 | export default [
2 | // Start of funny quiz
3 | {
4 | title: 'Funny Quiz',
5 | tags: ['funny', 'animal'], //used to find this quiz
6 | image: {
7 | url: '/images/Animals.jpeg',
8 | },
9 | questions: [
10 | {
11 | question: 'What do cats like to eat on a hot day?',
12 | options: ['Mice cream', 'Ice Cream', 'Hot Cream'],
13 | answer: 0, // Corresponds to "Mice cream"
14 | },
15 | {
16 | question: 'What do you call a cold dog?',
17 | options: ['Frozen', 'Hot dog', 'Chilli Dog'],
18 | answer: 'Chilli Dog',
19 | explanation: "Because it's cold. Get it?",
20 | },
21 | ],
22 | },
23 | // End of funny quiz
24 |
25 | // Start of president quiz
26 | {
27 | title: 'President Quiz',
28 | tags: ['president', 'presidents'], //used to find this quiz
29 | image: {
30 | url: '/images/Presidents.jpeg',
31 | },
32 | questions: [
33 | {
34 | question:
35 | "Which president said 'Efforts and courage are not enough without purpose and direction.'?",
36 | options: ['Kennedy', 'Obama'],
37 | answer: 0,
38 | },
39 | {
40 | question:
41 | "Which president said 'Men are not prisoners of fate, but only prisoners of their own minds'?",
42 | options: ['Hoover', 'Roosevelt', 'Truman'],
43 | answer: [
44 | 'Roosevelt',
45 | 'FDR',
46 | 'Franklin Roosevelt',
47 | 'Franklin D Roosevelt',
48 | ],
49 | },
50 | {
51 | question: "Which president said 'Speak softly, and carry a big stick'?",
52 | answer: ['Teddy Roosevelt', 'Roosevelt', 'Theodore Roosevelt'],
53 | explanation:
54 | "That famous quote describes Teddy Roosevelt's foreign policy",
55 | },
56 | ],
57 | },
58 | // End of presidents quiz
59 |
60 | // Start of states quiz generated from the spreadsheet
61 | {
62 | title: 'States Quiz',
63 | tags: ['states', 'state', 'united states'],
64 | image: { url: '/images/us_map.png' },
65 | questions: [
66 | {
67 | question: "Which state has the nickname 'Spud State'?",
68 | options: ['Idaho', 'Wyoming', 'Florida'],
69 | answer: 'Idaho',
70 | explanation:
71 | 'Idaho is well known for growing potatoes and thus the nickname, Spud State',
72 | },
73 | {
74 | question: "Which state has the nickname 'Peach State'?",
75 | options: ['Hawaii', 'California', 'Georgia'],
76 | answer: 2,
77 | },
78 | {
79 | question: "Which state has the nickname 'Silver State'?",
80 | options: ['New York', 'Montana', 'Nevada'],
81 | answer: 'Nevada',
82 | explanation:
83 | "Nevada had a silver rush in the mid 1800's and thus has the nickname, Silver State",
84 | },
85 | {
86 | question: 'What state is known as the Land of 10,000 lakes?',
87 | options: ['Wisconsin', 'Louisiana', 'Minnesota'],
88 | answer: 'Minnesota',
89 | explanation:
90 | 'Minnesota has over 11,000 lakes, so the land of 10,000 lakes is under counting!',
91 | },
92 | {
93 | question: 'What state is the largest in area?',
94 | options: ['California', 'Alaska', 'Texas'],
95 | answer: 1,
96 | explanation:
97 | 'Alaska is the largest state by far with 665,384 square miles. Second place Texas has 268,596 square miles and third place California has 163,694 square miles',
98 | },
99 | {
100 | question: 'What state is the largest agricultural producer?',
101 | options: ['Iowa', 'Kansas', 'California'],
102 | answer: 'California',
103 | explanation:
104 | "Caifornia produces over 11% of the nation's agricultural output followed by #2 Iowa which produces over 8%.",
105 | },
106 | {
107 | question: 'Which of the following states does not have a panhandle?',
108 | options: ['Idaho', 'Florida', 'New Mexico'],
109 | answer: 'New Mexico',
110 | },
111 | {
112 | question: 'Which state does not border the Great Lakes?',
113 | options: ['Ohio', 'Michigan', 'Iowa'],
114 | answer: 'Iowa',
115 | },
116 | {
117 | question: 'What is the smallest US state?',
118 | options: ['Delaware', 'Rhode Island', 'Connecticut'],
119 | answer: 'Rhode Island',
120 | explanation:
121 | 'Rhode Island is the smallest US state at 1,045 square miles. The next smallest is Delaware which at 1,954 square miles is almost twice the size of Rhode Island',
122 | },
123 | {
124 | question: "How many states are prefixed with 'New'?",
125 | answer: ['four', '4'],
126 | explanation:
127 | "There are four states prefixed by 'New': New Hampshire, New Jersey, New Mexico and New York",
128 | },
129 | ],
130 | },
131 | // End of States quiz
132 | ];
133 |
--------------------------------------------------------------------------------
/code/lib/util.js:
--------------------------------------------------------------------------------
1 | import quizzes from '../data/quizzes';
2 | import console from 'console';
3 |
4 | function findItems(items, searchTerm) {
5 | var matches = [];
6 | for (var i = 0; i < items.length; i++) {
7 | if (matchTag(items[i].tags, searchTerm)) {
8 | matches.push(items[i]);
9 | }
10 | }
11 | return matches;
12 | }
13 |
14 | //returns true if there is an element in a list of tags that matches a specific tag
15 | function matchTag(tags, tag) {
16 | for (var i = 0; i < tags.length; i++) {
17 | if (tag.toLowerCase() == tags[i].toLowerCase()) {
18 | return true;
19 | }
20 | }
21 | return false;
22 | }
23 |
24 | function buildQuestionsFromJson(quizJson) {
25 | var questions = [];
26 | for (var i = 0; i < quizJson.questions.length; i++) {
27 | questions.push(buildQuestionFromJson(quizJson.questions[i], i));
28 | }
29 | return questions;
30 | }
31 |
32 | function buildCorrectAnswerText(question) {
33 | if (Array.isArray(question.answer)) {
34 | return question.answer[0];
35 | } else if (typeof question.answer === 'number') {
36 | return question.options[question.answer];
37 | } else if (typeof question.answer === 'string') {
38 | return question.answer;
39 | }
40 | }
41 |
42 | function buildQuestionFromJson(questionJson) {
43 | if (!questionJson) {
44 | return null;
45 | }
46 | var options = [];
47 | if (questionJson.options) {
48 | for (var i = 0; i < questionJson.options.length; i++) {
49 | options.push({
50 | text: questionJson.options[i],
51 | alias: String.fromCharCode('A'.charCodeAt(0) + i),
52 | });
53 | }
54 | }
55 |
56 | var acceptedAnswers = buildAcceptedAnswers(questionJson.answer, options);
57 |
58 | var question = {
59 | text: questionJson.question,
60 | options: options,
61 | correctAnswer: {
62 | acceptedAnswers: acceptedAnswers.answers,
63 | acceptedAlias: acceptedAnswers.alias,
64 | explanation: questionJson.explanation,
65 | text: buildCorrectAnswerText(questionJson),
66 | },
67 | };
68 | return question;
69 | }
70 |
71 | //
72 | export function buildQuestionToSpeak(question) {
73 | var options = question.options;
74 | var optionsString = '';
75 | for (var i = 0; i < options.length; i++) {
76 | optionsString +=
77 | options[i].alias +
78 | '... ' +
79 | options[i].text +
80 | (i + 1 < options.length ? ', ... ' : '');
81 | }
82 | return optionsString;
83 | }
84 |
85 | function buildAcceptedAnswers(answer, options) {
86 | var acceptedAnswers = [];
87 | var alias;
88 |
89 | if (Array.isArray(answer)) {
90 | //is answer an array?
91 | for (var i = 0; i < answer.length; i++) {
92 | let acceptedAnswer = buildAcceptedAnswer(answer[i], options);
93 | acceptedAnswers = acceptedAnswers.concat(acceptedAnswer.answer);
94 | if (acceptedAnswer.alias) {
95 | alias = acceptedAnswer.alias;
96 | }
97 | }
98 | } else {
99 | let acceptedAnswer = buildAcceptedAnswer(answer, options);
100 | acceptedAnswers = acceptedAnswer.answer;
101 | if (acceptedAnswer.alias) {
102 | alias = acceptedAnswer.alias;
103 | }
104 | }
105 | return {
106 | answers: acceptedAnswers,
107 | alias: alias,
108 | };
109 | }
110 |
111 | function buildAcceptedAnswer(answer, options) {
112 | var acceptedAnswer;
113 | var alias;
114 | if (typeof answer === 'number' && options && answer < options.length) {
115 | alias = options[answer].alias;
116 | acceptedAnswer = options[answer].text;
117 | } else if (answer) {
118 | acceptedAnswer = answer;
119 | //also find alias matching the answer
120 | if (options) {
121 | for (var i = 0; i < options.length; i++) {
122 | if (options[i].text == answer) {
123 | alias = options[i].alias;
124 | }
125 | }
126 | }
127 | }
128 |
129 | return {
130 | answer: acceptedAnswer,
131 | alias: alias,
132 | };
133 | }
134 |
135 | export function buildQuizzes(searchTerm) {
136 | var quizzValue = quizzes;
137 | if (searchTerm) {
138 | const foundQuiz = findItems(quizzes, searchTerm);
139 | if (foundQuiz.length > 0) {
140 | quizzValue = foundQuiz;
141 | }
142 | }
143 | var formattedQuizzes = [];
144 | //read the questions in the quiz and initialize the state
145 | for (var i = 0; i < quizzValue.length; i++) {
146 | var quiz = quizzValue[i];
147 | quiz.completed = false;
148 | quiz.score = 0;
149 | quiz.index = 0;
150 | var questions = buildQuestionsFromJson(quiz);
151 | quiz.textToSpeak = buildQuestionToSpeak(questions[0]);
152 | //cannot start a quiz without any questions
153 | if (!questions || !questions.length) {
154 | console.log('Chosen quiz has no questions!');
155 | } else {
156 | quiz.questions = questions;
157 | formattedQuizzes.push(quiz);
158 | }
159 | }
160 | return formattedQuizzes;
161 | }
162 |
163 | export function arrayToListForSpeech(input) {
164 | var output = input.join();
165 | output = output.replace(/,/g, '...');
166 | output =
167 | output.substring(0, output.lastIndexOf('...')) +
168 | '... or ' +
169 | output.substring(output.lastIndexOf('...') + 3, output.length) +
170 | '...';
171 | output = output.toUpperCase();
172 | return output;
173 | }
174 |
175 | // ASR Aliases to handle ASR variations for "A", "B" "C" or "D" etc
176 | // This is a temporary workaround for an in progress bug that will be fixed
177 | const inputAliases = {
178 | a: 'a',
179 | hey: 'a',
180 | b: 'b',
181 | be: 'b',
182 | bee: 'b',
183 | c: 'c',
184 | see: 'c',
185 | sea: 'c',
186 | si: 'c',
187 | d: 'd',
188 | t: 'd',
189 | de: 'd',
190 | };
191 | // Regular expresion to find ASR alias - creatd by ASRaliases const
192 | const aliasesRE =
193 | '^(' +
194 | String(Object.getOwnPropertyNames(inputAliases))
195 | .replace('/"/g', '')
196 | .replace(/,/g, '|') +
197 | ')';
198 |
199 | export function checkAnswerMatch(correctAnswers, answer) {
200 | var correct = false;
201 | correctAnswers.forEach((o) => {
202 | var lowerO = o.toLowerCase();
203 | var corrAnsRegExp = '^' + lowerO + '$|' + aliasesRE + ' ' + lowerO + '$';
204 | if (answer.match(corrAnsRegExp)) {
205 | correct = true;
206 | }
207 | });
208 | return correct;
209 | }
210 |
211 | export function checkIncorrectOption(options, answer) {
212 | var answeredOption = false;
213 | var possOptionAlias = [];
214 | options.forEach((o) => {
215 | var lowerO = o.text.toLowerCase();
216 | var optionsRegExp =
217 | lowerO + '| ' + lowerO + ' |^' + lowerO + ' | ' + lowerO + '$';
218 | if (answer.match(optionsRegExp)) {
219 | answeredOption = true;
220 | }
221 | var x = o.alias.toLowerCase();
222 | possOptionAlias.push(x);
223 | });
224 |
225 | return {
226 | answeredOption: answeredOption,
227 | possOptionAlias: possOptionAlias,
228 | };
229 | }
230 |
231 | export function checkAliasMatch(answer, possOptionAlias, correctAlias) {
232 | var answeredOption = false;
233 | var correct = false;
234 | var aliasMatch = inputAliases[answer];
235 | if (aliasMatch) {
236 | if (possOptionAlias.indexOf(aliasMatch) > -1) {
237 | answeredOption = true;
238 | if (aliasMatch == correctAlias) {
239 | correct = true;
240 | }
241 | }
242 | }
243 | return {
244 | answeredOption: answeredOption,
245 | correct: correct,
246 | };
247 | }
248 |
--------------------------------------------------------------------------------
/contentUtility/CreateQuizContent.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bixbydevelopers/capsule-sample-quiz/51eb3d267edd3f9b866b55131f01f27a5bf74286/contentUtility/CreateQuizContent.xlsx
--------------------------------------------------------------------------------
/models/actions/FindQuiz.model.bxb:
--------------------------------------------------------------------------------
1 | action (FindQuiz) {
2 | description (Finds the quiz the user specified. If no quiz specified, gives users all the quizzes so they can choose one.)
3 | type (Calculation)
4 | collect {
5 | input (searchTerm) {
6 | type (SearchTerm)
7 | min (Optional) max (One)
8 | description (Try to match a specific quiz)
9 | }
10 | }
11 | output (Quiz)
12 | }
13 |
--------------------------------------------------------------------------------
/models/actions/StartQuiz.model.bxb:
--------------------------------------------------------------------------------
1 | action (StartQuiz) {
2 | description (Keep prompting the user for answers until quiz is completed.)
3 | type (Calculation)
4 | collect {
5 | input (quiz) {
6 | type (Quiz)
7 | min (Required) max (One)
8 | default-init {
9 | intent {
10 | goal: FindQuiz
11 | }
12 | }
13 | validate {
14 | if (!quiz.completed) {
15 | replan {
16 | intent {
17 | goal: UpdateQuiz
18 | value { $expr(quiz) }
19 | }
20 | }
21 | }
22 | }
23 | }
24 | }
25 | output (Quiz)
26 | }
--------------------------------------------------------------------------------
/models/actions/UpdateQuiz.model.bxb:
--------------------------------------------------------------------------------
1 | action (UpdateQuiz) {
2 | type (Calculation)
3 | description (Compares the user's answer to the correct answer and updates your score and move to the next question)
4 | collect {
5 | input (answer) {
6 | type (Answer)
7 | min (Required) max (One)
8 | }
9 | input (quiz) {
10 | type (Quiz)
11 | min (Required) max (One)
12 | }
13 | }
14 | output (Quiz)
15 | }
16 |
--------------------------------------------------------------------------------
/models/actions/utils/ExtractAnswerFromOption.model.bxb:
--------------------------------------------------------------------------------
1 | action (ExtractAnswerFromOption) {
2 | description (Convert an option - the option user tapped on - to a (text) answer)
3 | type (Constructor)
4 | collect {
5 | input (option) {
6 | type (Option)
7 | min (Required) max (One)
8 | }
9 | }
10 | output (Answer)
11 | }
12 |
--------------------------------------------------------------------------------
/models/concepts/primitive/Answer.model.bxb:
--------------------------------------------------------------------------------
1 | text (Answer) {
2 | description (The user's answer)
3 | }
4 |
--------------------------------------------------------------------------------
/models/concepts/primitive/SearchTerm.model.bxb:
--------------------------------------------------------------------------------
1 | text (SearchTerm) {
2 | description (User searching for a quiz with this term)
3 | }
4 |
--------------------------------------------------------------------------------
/models/concepts/structures/CorrectAnswer.model.bxb:
--------------------------------------------------------------------------------
1 | structure (CorrectAnswer) {
2 | description (A structure to hold information on the correct answer)
3 | property (text) {
4 | description (The correct answer to display to the user.)
5 | type (core.Text)
6 | min (Required) max (One)
7 | visibility (Private)
8 | }
9 | property (acceptedAnswers) {
10 | description (additional acceptable answers. For example, Roosevelt, FDR, and Franklin Delano Roosevelt are all correct.)
11 | type (core.Text)
12 | min (Optional) max (Many)
13 | visibility (Private)
14 | }
15 | property (acceptedAlias) {
16 | description (Alias for correct answer)
17 | type (core.Text)
18 | min (Optional) max (Many)
19 | visibility (Private)
20 | }
21 | property (explanation) {
22 | description (a description of why this is the correct answer)
23 | type (core.Text)
24 | min (Optional) max (One)
25 | visibility (Private)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/models/concepts/structures/Option.model.bxb:
--------------------------------------------------------------------------------
1 | structure (Option) {
2 | description(An option presented to the user for a multi choice question)
3 | property (text) {
4 | description ("Text description of the option")
5 | type (Answer)
6 | min (Required) max (One)
7 | visibility (Private)
8 | }
9 | property (alias) {
10 | description("Alias used for the option (e.g. first option 'A', second option 'B', etc)")
11 | type (core.Text)
12 | min (Required) max (One)
13 | visibility (Private)
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/models/concepts/structures/Question.model.bxb:
--------------------------------------------------------------------------------
1 | structure (Question) {
2 | description (A question in a quiz)
3 | property (text) {
4 | description(Question to be displayed)
5 | type (core.Text)
6 | min (Required) max (One)
7 | visibility (Private)
8 | }
9 | property (options) {
10 | description(Options provided to the user)
11 | type (Option)
12 | min (Optional) max (Many)
13 | visibility (Private)
14 | }
15 | property (correctAnswer) {
16 | description(Information on the correct answer)
17 | type (CorrectAnswer)
18 | min (Required) max (One)
19 | visibility (Private)
20 | }
21 | property (correct) {
22 | description (Whether the user got this right or not)
23 | type (core.Boolean)
24 | min (Optional) max (One)
25 | visibility (Private)
26 | }
27 | property (invalidAnswer) {
28 | description (Whether the last answer to this question was invalid)
29 | type (core.Boolean)
30 | min (Optional) max (One)
31 | visibility (Private)
32 | }
33 | property (answer) {
34 | description (What answer the user provided)
35 | type (Answer)
36 | min (Optional) max (One)
37 | visibility (Private)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/models/concepts/structures/Quiz.model.bxb:
--------------------------------------------------------------------------------
1 | structure (Quiz) {
2 | description (A whole Quiz structure)
3 | property (title) {
4 | type (core.Text)
5 | min (Required) max (One)
6 | visibility (Private)
7 | }
8 | property (tags) {
9 | description (Labels attached to this quiz to enable searching for the quiz by keyword)
10 | type (core.Text)
11 | min (Optional) max (Many)
12 | visibility (Private)
13 | }
14 | property (questions) {
15 | description (Questions in this quiz)
16 | type (Question)
17 | min (Required) max (Many)
18 | visibility (Private)
19 | }
20 | property (image) {
21 | description (Image describing this quiz)
22 | type (core.BaseImage)
23 | min (Optional) max (One)
24 | visibility (Private)
25 | }
26 | property (textToSpeak) {
27 | description(Question formatted for speech channel)
28 | type (core.Text)
29 | min (Optional) max (One)
30 | visibility (Private)
31 | }
32 | property (index) {
33 | description (Index of the current question)
34 | type (core.Integer)
35 | min (Required) max (One)
36 | }
37 | property (score) {
38 | description (User's score)
39 | type (core.Integer)
40 | min (Required) max (One)
41 | visibility (Private)
42 | }
43 | property (completed) {
44 | description (Toggles true when the quiz is completed)
45 | type (core.Boolean)
46 | min (Required) max (One)
47 | visibility (Private)
48 | }
49 | features{
50 | transient
51 | }
52 | }
--------------------------------------------------------------------------------
/resources/base/views/FindQuiz.view.bxb:
--------------------------------------------------------------------------------
1 | // See the FindQuiz.dialog file for localized strings for UI
2 |
3 | input-view {
4 | match {
5 | Quiz(quizzes)
6 | }
7 |
8 | message {
9 | template ("#{macro('QUIZ_SELECTION_QUESTION', quizzes)}")
10 | }
11 |
12 | render {
13 | if ($handsFree) { // If hands free mode, read out the quiz options and provide navigation guidance to the user
14 | selection-of (quizzes) {
15 | navigation-mode {
16 | read-many {
17 | list-summary {
18 | template ("#{macro('WELCOME_TO_QUIZ', quizzes)}")
19 | }
20 | page-content (page) {
21 | item-selection-question {
22 | if (isLastNavPage(page)) {
23 | template ("#{macro('ITEM-SELECTION-QUESTION', quizzes)}")
24 | } else {
25 | template ("#{macro('ITEM-SELECTION-QUESTION', quizzes)} #{macro('RESULT_NEXT_QUESTION')}")
26 | }
27 | }
28 | overflow-statement {
29 | template-macro (OVERFLOW_STATEMENT)
30 | }
31 | underflow-statement {
32 | template-macro (UNDERFLOW_STATEMENT)
33 | }
34 | page-marker {
35 | if (isLastNavPage(page) && !isFirstNavPage(page)){
36 | if (size(page) == 1) {
37 | template ("#{macro('LAST_QUIZ')}")
38 | } else {
39 | template ("#{macro('LAST_QUIZZES', size(page))}")
40 | }
41 | } else-if (!isFirstNavPage(page)) {
42 | template ("#{macro('NEXT_QUIZZES', size(page))}")
43 | }
44 | }
45 | }
46 | page-size (3)
47 | }
48 | }
49 | where-each (quiz) {
50 | spoken-summary {
51 | template("#{macro('QUIZ_LIST_SUMMARY', quiz)}")
52 | }
53 | layout-macro (QuizDetails) {
54 | param (quiz) { expression (quiz) }
55 | }
56 | }
57 | }
58 | }
59 | else {
60 | selection-of (quizzes) {
61 | where-each (quiz) {
62 | layout-macro (QuizDetails) {
63 | param (quiz) { expression (quiz) }
64 | }
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/resources/base/views/QuizCompleted.view.bxb:
--------------------------------------------------------------------------------
1 | // See the UpdateQuiz.dialog for localized strings
2 |
3 | result-view{
4 | match: Quiz(quiz){
5 | from-output:StartQuiz(action)
6 | }
7 |
8 | message {
9 | if (action.quiz.questions[action.quiz.index].correct) {
10 | template ("#{macro('COMPLETED_LAST_CORRECT', action.quiz)}") {
11 | }
12 | } else {
13 | template ("#{macro('COMPLETED_LAST_INCORRECT', action.quiz)}") {
14 | }
15 | }
16 | }
17 |
18 | render{
19 | layout-macro (QuizCompleted){
20 | param (quiz){
21 | expression (quiz)
22 | }
23 | }
24 | }
25 | conversation-drivers {
26 | conversation-driver {
27 | template-macro (ANOTHER_QUIZ)
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/resources/base/views/UpdateQuiz.view.bxb:
--------------------------------------------------------------------------------
1 | // See the UpdatQuiz.dialog for localized strings
2 |
3 | input-view {
4 | match: Answer(this) {
5 | to-input: UpdateQuiz(action)
6 | }
7 |
8 | message {
9 | if (action.quiz.index > 0) { // if not on first question
10 | if (action.quiz.questions[action.quiz.index].invalidAnswer) { // if could not understand last question answer
11 | template ("#{macro('INVALID_QUESTION_ANSWER', action.quiz)}") {
12 | }
13 | }
14 | else-if (action.quiz.questions[action.quiz.index-1].correct) { // if last question correct
15 | template ("#{macro('CORRECT_QUESTION_ANSWER', action.quiz)}") {
16 | }
17 | } else {
18 | template ("#{macro('INCORRECT_QUESTION_ANSWER', action.quiz)}") {
19 | }
20 | }
21 | } else { // first question
22 | if (action.quiz.questions[action.quiz.index].invalidAnswer) { // if could not understand last question answer
23 | template ("#{macro('INVALID_QUESTION_ANSWER', action.quiz)}") {
24 | }
25 | } else {
26 | template ("#{macro('FIRST_QUESTION', action.quiz)}")
27 | }
28 |
29 |
30 | }
31 | }
32 |
33 | render {
34 | if (size(action.quiz.questions[action.quiz.index].options) > 0) {
35 | selection-of (action.quiz.questions[action.quiz.index].options) {
36 | where-each (option) {
37 | cell-card {
38 | slot2 {
39 | content {
40 | primary {
41 | template ("[#{value(option.alias)}. ]#{value(option.text)}")
42 | }
43 | }
44 | }
45 | }
46 | }
47 | }
48 | } else {
49 | form {
50 | elements {
51 | text-input {
52 | id (answer)
53 | type (Answer)
54 | }
55 | }
56 | on-submit {
57 | goal: Answer
58 | value: viv.core.FormElement(answer)
59 | }
60 | }
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/resources/base/views/macros/QuizCompleted.layout.bxb:
--------------------------------------------------------------------------------
1 | // See the LayoutMacros.dialog for localized strings
2 |
3 | layout-macro-def (QuizCompleted){
4 | params {
5 | param (quiz) {
6 | type (Quiz)
7 | min (Optional)
8 | max (Many)
9 | }
10 | }
11 | content{
12 | section{
13 | content{
14 | title-area {
15 | slot1 {
16 | text {
17 | style(Title_M)
18 | value("#{macro('QUESTION_REVIEW')}:")
19 | }
20 | }
21 | }
22 | for-each (quiz.questions){
23 | as (question) {
24 | paragraph {
25 | style (Title_S)
26 | value ("#{macro('QUESTION')}: #{value(question.text)}")
27 | }
28 | cell-area {
29 | slot1 {
30 | image {
31 | shape (Circle)
32 | url {
33 | if (exists(question.correct) && question.correct == true) {
34 | template ("/icons/correct.png")
35 | } else {
36 | template ("/icons/incorrect.png")
37 | }
38 | }
39 | }
40 | }
41 | slot2 {
42 | content {
43 | order (PrimarySecondary)
44 | primary {
45 | if (exists(question.answer)) {
46 | template ("'#{title(question.answer)}'")
47 | }
48 | }
49 | }
50 | }
51 | }
52 | if (exists(question.correct) && question.correct == false) {
53 | paragraph {
54 | style (Detail_L)
55 | value {
56 | template ("#{macro('CORRECT_ANSWER')}: '#{value(question.correctAnswer[0].text)}'[ - #{value(question.correctAnswer.explanation)}]")
57 | }
58 | }
59 | }
60 | paragraph {
61 | value {
62 | template ("")
63 | }
64 | }
65 | divider
66 | }
67 | }
68 | }
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/resources/base/views/macros/QuizDetails.layout.bxb:
--------------------------------------------------------------------------------
1 | //See the LayoutMacros.dialog for localized strings
2 |
3 | layout-macro-def(QuizDetails) {
4 | params {
5 | param (quiz) {
6 | type (Quiz)
7 | min (Required)
8 | max (One)
9 | }
10 | }
11 | content {
12 | cell-card {
13 | slot1 {
14 | if (exists(quiz.image.url)) {
15 | image {
16 | shape (Square)
17 | url {
18 | template ("#{value(quiz.image.url)}")
19 | }
20 | }
21 | }
22 | }
23 | slot2 {
24 | content {
25 | primary {
26 | template ("#{value(quiz.title)}")
27 | }
28 | secondary {
29 | template ("#{number(size(quiz.questions))} #{macro('QUESTIONS')}")
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/resources/en/capsule-info.bxb:
--------------------------------------------------------------------------------
1 | capsule-info {
2 | display-name (Example Quiz)
3 | developer-name (Viv Labs, Inc.)
4 | icon-asset (icons/correct.png)
5 | description ("An example capsule that demonstrates how to build a quiz")
6 | website-url (https://bixbydevelopers.com/)
7 | search-keywords{
8 | keyword (quiz)
9 | }
10 | dispatch-name (Example Quiz)
11 | }
--------------------------------------------------------------------------------
/resources/en/dialogs/NoQuiz.dialog.bxb:
--------------------------------------------------------------------------------
1 | dialog (NoResult) {
2 | match: Quiz {
3 | from-output: FindQuiz(action)
4 | }
5 | template("I didn't find any quiz[ matching #{value(action.searchTerm)}].")
6 | }
7 |
--------------------------------------------------------------------------------
/resources/en/dialogs/macros/FindQuiz.dialog.bxb:
--------------------------------------------------------------------------------
1 | // Localized strings used by FindQuiz view
2 |
3 | template-macro-def (WELCOME_TO_QUIZ) {
4 | params {
5 | param (quizzes) {
6 | type (Quiz)
7 | min (Required)
8 | max (One)
9 | }
10 | }
11 | content {
12 | template ("Welcome to Bixby Quiz. There are #{size(quizzes)} quizzes available")
13 | }
14 | }
15 |
16 | template-macro-def (QUIZ_SELECTION_QUESTION) {
17 | params {
18 | param (quizzes) {
19 | type (Quiz)
20 | min (Required)
21 | max (One)
22 | }
23 | }
24 | content {
25 | choose (Random) {
26 | template ("Welcome to Bixby Quiz. Which quiz would you like to take?")
27 | template ("Welcome to Bixby Quiz. Which of these #{size(quizzes)} quizzes would you like to take?")
28 | }
29 | }
30 | }
31 |
32 | template-macro-def (ITEM-SELECTION-QUESTION) {
33 | params {
34 | param (quizzes) {
35 | type (Quiz)
36 | min (Required)
37 | max (One)
38 | }
39 | }
40 | content {
41 | choose (Random) {
42 | template ("Which quiz would you like to take?")
43 | template ("Which quiz sounds good to you?")
44 | }
45 | }
46 | }
47 |
48 | template-macro-def (OVERFLOW_STATEMENT) {
49 | content {
50 | template ("That's all the quizzes I have.")
51 | }
52 | }
53 |
54 | template-macro-def (UNDERFLOW_STATEMENT) {
55 | content {
56 | template ("This is the first list of quizzes.")
57 | }
58 | }
59 |
60 | template-macro-def (RESULT_NEXT_QUESTION) {
61 | content {
62 | template ("Or say next for more quizzes.")
63 | }
64 | }
65 |
66 | template-macro-def (NEXT_QUIZZES) {
67 | params {
68 | param (pageSize) {
69 | type (core.Integer)
70 | min (Required)
71 | max (One)
72 | }
73 | }
74 | content {
75 | template ("The next #{pageSize} quizzes are")
76 | }
77 | }
78 |
79 | template-macro-def (LAST_QUIZZES) {
80 | params {
81 | param (pageSize) {
82 | type (core.Integer)
83 | min (Required)
84 | max (One)
85 | }
86 | }
87 | content {
88 | template ("The last #{pageSize} quizzes are")
89 | }
90 | }
91 |
92 | template-macro-def (LAST_QUIZ) {
93 | content {
94 | template ("The last quiz is")
95 | }
96 | }
97 |
98 |
99 | template-macro-def (QUIZ_LIST_SUMMARY) {
100 | params {
101 | param (quizzes) {
102 | type (Quiz)
103 | min (Required) max (Many)
104 | }
105 | }
106 | content {
107 | template("#{value(quizzes.title)} has #{size(quizzes.questions)} questions.")
108 | }
109 | }
--------------------------------------------------------------------------------
/resources/en/dialogs/macros/LayoutMacros.dialog.bxb:
--------------------------------------------------------------------------------
1 | // Misc localized dialogs used by layout macros
2 |
3 | template-macro-def (ANOTHER_QUIZ) {
4 | content: template ("Take another quiz")
5 | }
6 |
7 | template-macro-def (QUESTIONS) {
8 | content: template ("Questions")
9 | }
10 |
11 | template-macro-def (QUESTION) {
12 | content: template ("Question")
13 | }
14 |
15 | template-macro-def (QUESTION_REVIEW) {
16 | content: template ("Question Review")
17 | }
18 |
19 | template-macro-def (CORRECT_ANSWER) {
20 | content: template ("Correct answer")
21 | }
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/resources/en/dialogs/macros/UpdateQuiz.dialog.bxb:
--------------------------------------------------------------------------------
1 | // Localized strings used by UpdateQuiz view
2 |
3 | template-macro-def (FIRST_QUESTION) {
4 | params {
5 | param (quiz) {
6 | type (Quiz)
7 | min (Required)
8 | max (One)
9 | }
10 | }
11 | content {
12 | template ("#{value(quiz.questions[quiz.index].text)}") {
13 | speech ("#{value(quiz.questions[quiz.index].text)}... [#{value(quiz.textToSpeak)}]")
14 | }
15 | }
16 | }
17 |
18 | template-macro-def (CORRECT_QUESTION_ANSWER) {
19 | params {
20 | param (quiz) {
21 | type (Quiz)
22 | min (Required)
23 | max (One)
24 | }
25 | }
26 | content {
27 | template ("Correct. #{value(quiz.questions[quiz.index].text)}") {
28 | speech ("Correct... #{value(quiz.questions[quiz.index].text)}... [#{value(quiz.textToSpeak)}]")
29 | }
30 | }
31 | }
32 |
33 | template-macro-def (INCORRECT_QUESTION_ANSWER) {
34 | params {
35 | param (quiz) {
36 | type (Quiz)
37 | min (Required)
38 | max (One)
39 | }
40 | }
41 | content {
42 | template ("Incorrect. The correct answer is #{value(quiz.questions[quiz.index-1].correctAnswer.text)}. #{value(quiz.questions[quiz.index].text)}") {
43 | speech ("Incorrect... The correct answer is #{value(quiz.questions[quiz.index-1].correctAnswer.text)}... #{value(quiz.questions[quiz.index].text)}... [#{value(quiz.textToSpeak)}]")
44 | }
45 | }
46 | }
47 |
48 | template-macro-def (INVALID_QUESTION_ANSWER) {
49 | params {
50 | param (quiz) {
51 | type (Quiz)
52 | min (Required)
53 | max (One)
54 | }
55 | }
56 | content {
57 | template ("Sorry, I didn't understand. #{value(quiz.questions[quiz.index].text)}") {
58 | speech ("Sorry...I didn't understand... #{value(quiz.questions[quiz.index].text)}... [#{value(quiz.textToSpeak)}]")
59 | }
60 | }
61 | }
62 |
63 | template-macro-def (COMPLETED_LAST_CORRECT) {
64 | params {
65 | param (quiz) {
66 | type (Quiz)
67 | min (Required)
68 | max (One)
69 | }
70 | }
71 | content {
72 | template ("Correct. You got #{value(quiz.score)} out of #{size(quiz.questions)} right!") {
73 | speech ("Correct... You got #{value(quiz.score)} out of #{size(quiz.questions)} right!")
74 | }
75 | }
76 | }
77 |
78 | template-macro-def (COMPLETED_LAST_INCORRECT) {
79 | params {
80 | param (quiz) {
81 | type (Quiz)
82 | min (Required)
83 | max (One)
84 | }
85 | }
86 | content {
87 | template ("Incorrect. The correct answer is #{value(quiz.questions[quiz.index].correctAnswer.text)}. You got #{value(quiz.score)} out of #{size(quiz.questions)} right!") {
88 | speech ("Incorrect... The correct answer is #{value(quiz.questions[quiz.index].correctAnswer.text)}... You got #{value(quiz.score)} out of #{size(quiz.questions)} right!")
89 | }
90 | }
91 | }
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/resources/en/example-quiz.hints.bxb:
--------------------------------------------------------------------------------
1 | hints {
2 | uncategorized {
3 | hint (With Example Quiz, start a quiz)
4 | hint (In Example Quiz, start the president quiz)
5 | hint (In Example Quiz, start the funny quiz)
6 | }
7 | }
--------------------------------------------------------------------------------
/resources/en/quiz.endpoints.bxb:
--------------------------------------------------------------------------------
1 | endpoints {
2 | action-endpoints {
3 | action-endpoint (FindQuiz) {
4 | accepted-inputs (searchTerm)
5 | local-endpoint (FindQuiz.js)
6 | }
7 | action-endpoint (StartQuiz) {
8 | accepted-inputs (quiz)
9 | local-endpoint (StartQuiz.js)
10 | }
11 | action-endpoint (UpdateQuiz) {
12 | accepted-inputs (quiz, answer)
13 | local-endpoint (UpdateQuiz.js)
14 | }
15 | action-endpoint (ExtractAnswerFromOption) {
16 | accepted-inputs (option)
17 | local-endpoint (ExtractAnswerFromOption.js)
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/resources/en/training/t-4.training.bxb:
--------------------------------------------------------------------------------
1 | train (t-4vwgbhq5kwk84it2qngh0vwj9) {
2 | utterance ("[g:Quiz:prompt] the (funny)[v:SearchTerm] one")
3 | plan (Y2BmEEytSMwtyEnVKyzNrNILBBIMoihCbpl5KWBhcRTh4NTEouSMkNSiXAYmBkYgZgCSjAwgwATBAA==)
4 | last-modified (1569982429459)
5 | }
--------------------------------------------------------------------------------
/resources/en/training/t-5.training.bxb:
--------------------------------------------------------------------------------
1 | train (t-5as44ztbuhebtyy97sicyiun2) {
2 | utterance ("[g:StartQuiz] start the (president)[v:SearchTerm] quiz")
3 | plan (Y2BlEEytSMwtyEnVKyzNrNILBBIMYihCwSWJRSVgcSxKRVGE3DLzUsDC4qgmpCYWJWeEpBblMrAwMDIwMTADaQYoixHMAgEmIA0UBwA=)
4 | last-modified (1569982429784)
5 | }
--------------------------------------------------------------------------------
/resources/en/training/t-7.training.bxb:
--------------------------------------------------------------------------------
1 | train (t-74ylfgy64fg78k7y5z4ck27v6) {
2 | utterance ("[g:Quiz:prompt] (foo bar)[v:SearchTerm]")
3 | plan (Y2BmEEytSMwtyEnVKyzNrNILBBIMoihCbpl5KWBhcRTh4NTEouSMkNSiXAYmBkYgZgCSjAwgwATBAA==)
4 | last-modified (1569982429575)
5 | }
6 | train (t-7vj6xlz29ebf97j8a04ej4uxk) {
7 | utterance ([g:StartQuiz] quiz)
8 | plan (Y2BmEEytSMwtyEnVKyzNrNILBBIMYihCwSWJRSVgcSxKmRgYgZgBSDIyMMBoAA==)
9 | last-modified (1569982429700)
10 | }
--------------------------------------------------------------------------------
/resources/en/training/t-8.training.bxb:
--------------------------------------------------------------------------------
1 | train (t-815a9m07q9nskqvmnd80p8x4p) {
2 | utterance ("[g:StartQuiz] give me a (funny)[v:SearchTerm] quiz")
3 | plan (Y2BlEEytSMwtyEnVKyzNrNILBBIMYihCwSWJRSVgcSxKRVGE3DLzUsDC4qgmpCYWJWeEpBblMrAwMDIwMTADaQYoixHMAgEmIA0UBwA=)
4 | last-modified (1569982429865)
5 | }
--------------------------------------------------------------------------------
/resources/en/training/t-a.training.bxb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bixbydevelopers/capsule-sample-quiz/51eb3d267edd3f9b866b55131f01f27a5bf74286/resources/en/training/t-a.training.bxb
--------------------------------------------------------------------------------
/resources/en/training/t-h.training.bxb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bixbydevelopers/capsule-sample-quiz/51eb3d267edd3f9b866b55131f01f27a5bf74286/resources/en/training/t-h.training.bxb
--------------------------------------------------------------------------------
/resources/en/training/t-i.training.bxb:
--------------------------------------------------------------------------------
1 | train (t-izysb78007qgj4ijcyn57p94b) {
2 | utterance ("[g:Quiz:prompt] give me the (funny)[v:SearchTerm] quiz")
3 | plan (Y2BmEEytSMwtyEnVKyzNrNILBBIMoihCbpl5KWBhcRTh4NTEouSMkNSiXAYmBkYgZgCSjAwgwATBAA==)
4 | last-modified (1569982430132)
5 | }
--------------------------------------------------------------------------------
/resources/en/training/t-p.training.bxb:
--------------------------------------------------------------------------------
1 | train (t-pz8x5ohru6pbxz2ujn1khw440) {
2 | utterance ("[g:Quiz:prompt] (funny)[v:SearchTerm] quiz")
3 | plan (Y2BmEEytSMwtyEnVKyzNrNILBBIMoihCbpl5KWBhcRTh4NTEouSMkNSiXAYmBkYgZgCSjAwgwATBAA==)
4 | last-modified (1569982429973)
5 | }
--------------------------------------------------------------------------------
/resources/en/training/t-q.training.bxb:
--------------------------------------------------------------------------------
1 | train (t-qcwihhq2mqxtic76jjlue9wfq) {
2 | utterance ([g:StartQuiz] Take another quiz)
3 | last-modified (1598465149954)
4 | }
--------------------------------------------------------------------------------
/resources/en/training/t-r.training.bxb:
--------------------------------------------------------------------------------
1 | train (t-r3icl2t4r8bxhcrhfesszye75) {
2 | utterance ([g:StartQuiz] start)
3 | plan (Y2BmEEytSMwtyEnVKyzNrNILBBIMYihCwSWJRSVgcSxKmRgYgZgBSDIyMMBoAA==)
4 | last-modified (1569982430029)
5 | }
6 | train (t-rhqrvtq43qwp4v4gmvqt6avk6) {
7 | utterance ("[g:Answer:prompt] (foo bar foo bar)[v:Answer]")
8 | plan (Y2BiEE6tSMwtyEnVKyzNrNJzzCsuTy3CLsgIhAwMPEDMBMRANgA=)
9 | last-modified (1569982430081)
10 | }
--------------------------------------------------------------------------------
/resources/en/training/t-x.training.bxb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bixbydevelopers/capsule-sample-quiz/51eb3d267edd3f9b866b55131f01f27a5bf74286/resources/en/training/t-x.training.bxb
--------------------------------------------------------------------------------