{"id":2130,"date":"2019-05-11T14:38:54","date_gmt":"2019-05-11T12:38:54","guid":{"rendered":"http:\/\/www.sciencetronics.com\/greenphotons\/?p=2130"},"modified":"2025-06-16T18:37:46","modified_gmt":"2025-06-16T16:37:46","slug":"canvas-using-python-to-create-calculated-questions","status":"publish","type":"post","link":"https:\/\/www.sciencetronics.com\/greenphotons\/?p=2130","title":{"rendered":"Canvas: Using Python to create Calculated Questions"},"content":{"rendered":"<h2>Background<\/h2>\n<p>I just recently started with Canvas because Uppsala University has decided to use it as its upcoming LMS platform after a failed attempt with another product. Therefore I had already spent some time with Blackboard and was quite fond of the <em>calculated questions<\/em> type in quizzes. I quickly found out that Canvas offers essentially the same functionality but a bit less comfortable.<\/p>\n<h2>Problem<\/h2>\n<p>A <em>calculated question<\/em> or <em>Formula Question <\/em>as it is called in the interface of Canvas is based on a table of pre-generated variable values and corresponding results. In the general case the variables are defined and the target function is entered using the web interface, then Canvas calculates random number values for the variables and the resulting answer value. However, as the designer you have no possibility to influence the variable values afterwards (unlike in Blackboard where you have a spreadsheet-like interface). Also, in Canvas, the equation cannot be altered once it has been entered &#8211; and the supported syntax is not very convenient for more complex problems.<br \/>\nI was also missing the ability to give a relative tolerance for the correct answers in a question, however, I found out that entering a percentage-sign exactly gives this behavior even though it does not seem documented anywhere.<\/p>\n<h2>Solution or problems?<\/h2>\n<p>My hope was then for the API, since it seemed to support the creation of questions. But even though there is a Python library for the purpose of controlling Canvas, many of the functions are not very well documented. My first tries failed miserably but finally I was on the right track.<\/p>\n<p>The cause of my problems was that the Canvas API uses different field identifiers and structures when creating a calculated question as when you retrieve the contents of an already existing question, as I of course did in my attempts to reverse-engineer the interface.<\/p>\n<h2>Working solution<\/h2>\n<p>Here is now an example for a working solution to give you full control over the generation of Formula Qeustions using Python and the <a href=\"https:\/\/github.com\/ucfopen\/canvasapi\">canvasapi <\/a>library. The example is in Python 3 and creates a question from the field of electronics &#8211; the voltage in a voltage divider. The Python script generates the variables, fills the variables with random numbers from a set of predefined, commonly used values. I tried to write the script more for readability than any pythonic optimization.<br \/>\n<iframe style=\"border: none; width: 100%;\" src=\"https:\/\/pastebin.com\/embed_iframe\/bCThQ8ZJ\"><\/iframe><\/p>\n<p>Since this is mostly the result of successful reverse engineering and not based on the actual source code of Canvas the above example should perhaps be used with care, but for me it is what I needed to create usable questions for my students. Perhaps this could also serve the developers as an example on how the interface for calculated questions could be improved in the future.<\/p>\n<h2>How does it work?<\/h2>\n<p>The dictionary <em>variables<\/em> (lines 26-49) contains the names and ranges of the variables, as well as formatting instructions. The ranges are given as lists. In lines 61-66 the random values are generated and the results calculated from these values. Lines 70-77 create a rudimentary table to be included in the question text containing the variables and their values as well as physical units for this particular question. Lines 80-93 finally assemble the variable\/answer block and lines 109-128 put everything together into the dictionary to create a new question.<\/p>\n<p>The script then inserts the question into an existing quiz in an existing course in line 140.<\/p>\n<h3>After running the script<\/h3>\n<p>This screenshot shows the inserted question after running the script, obviously this would need some more cosmetics.<\/p>\n<p><a href=\"http:\/\/www.sciencetronics.com\/greenphotons\/?attachment_id=2133\" rel=\"attachment wp-att-2133\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2133 aligncenter\" src=\"http:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_01.png\" alt=\"\" width=\"488\" height=\"233\" srcset=\"https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_01.png 962w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_01-300x143.png 300w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_01-768x366.png 768w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_01-150x72.png 150w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_01-400x191.png 400w\" sizes=\"auto, (max-width: 488px) 100vw, 488px\" \/><\/a>And when editing the question this is what you see:<\/p>\n<p><a href=\"http:\/\/www.sciencetronics.com\/greenphotons\/?attachment_id=2134\" rel=\"attachment wp-att-2134\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2134\" src=\"http:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_02.png\" alt=\"\" width=\"482\" height=\"1007\" srcset=\"https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_02.png 952w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_02-144x300.png 144w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_02-768x1605.png 768w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_02-490x1024.png 490w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_02-72x150.png 72w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_02-400x836.png 400w\" sizes=\"auto, (max-width: 482px) 100vw, 482px\" \/><\/a><\/p>\n<p>Be careful not to touch the variables or the formula section since this will reset the table values.<\/p>\n<h3>Cosmetics<\/h3>\n<p>In order to be presentable to the students the above questions needs some cosmetics. What is to be calculated? Perhaps insert a picture or an equation? More text?<\/p>\n<p><a href=\"http:\/\/www.sciencetronics.com\/greenphotons\/?attachment_id=2135\" rel=\"attachment wp-att-2135\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2135\" src=\"http:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_03.png\" alt=\"\" width=\"519\" height=\"1083\" srcset=\"https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_03.png 956w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_03-144x300.png 144w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_03-768x1603.png 768w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_03-491x1024.png 491w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_03-72x150.png 72w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_03-400x835.png 400w\" sizes=\"auto, (max-width: 519px) 100vw, 519px\" \/><\/a><\/p>\n<p>After updating the question and leaving the editor it now looks like this in the Canvas UI:<\/p>\n<p><a href=\"http:\/\/www.sciencetronics.com\/greenphotons\/?attachment_id=2136\" rel=\"attachment wp-att-2136\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2136\" src=\"http:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_04.png\" alt=\"\" width=\"518\" height=\"330\" srcset=\"https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_04.png 957w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_04-300x191.png 300w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_04-768x490.png 768w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_04-150x96.png 150w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_04-400x255.png 400w\" sizes=\"auto, (max-width: 518px) 100vw, 518px\" \/><\/a><\/p>\n<h3>Seeing and answering the question<\/h3>\n<p>When you now start the quiz, this is how the question looks:<\/p>\n<p><a href=\"http:\/\/www.sciencetronics.com\/greenphotons\/?attachment_id=2137\" rel=\"attachment wp-att-2137\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2137\" src=\"http:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_05.png\" alt=\"\" width=\"535\" height=\"349\" srcset=\"https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_05.png 1016w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_05-300x195.png 300w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_05-768x500.png 768w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_05-150x98.png 150w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_05-400x261.png 400w\" sizes=\"auto, (max-width: 535px) 100vw, 535px\" \/><\/a><\/p>\n<p><a href=\"http:\/\/www.sciencetronics.com\/greenphotons\/?attachment_id=2138\" rel=\"attachment wp-att-2138\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2138\" src=\"http:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_06.png\" alt=\"\" width=\"571\" height=\"376\" srcset=\"https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_06.png 1048w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_06-300x198.png 300w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_06-768x506.png 768w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_06-1024x674.png 1024w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_06-150x99.png 150w, https:\/\/www.sciencetronics.com\/greenphotons\/wp-content\/uploads\/2019\/05\/Screenshot_2019-05-11-test_06-400x263.png 400w\" sizes=\"auto, (max-width: 571px) 100vw, 571px\" \/><\/a><\/p>\n<h2>Summary<\/h2>\n<ul>\n<li><em>calculated_questions<\/em> can be generated using the Python canvasapi library<\/li>\n<li>answer values have to be provided with the key &#8216;answer-text&#8217;\n<pre class=\"language-python line-numbers\" contenteditable=\"false\"><span class=\"token string\">'answers'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">[<\/span>\r\n   <span class=\"token punctuation\">{<\/span>\r\n     <span class=\"token string\">'weight'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'100'<\/span><span class=\"token punctuation\">,<\/span> \r\n     <span class=\"token string\">'variables'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">[<\/span>\r\n     <span class=\"token punctuation\">{<\/span><span class=\"token string\">'name'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'U0'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'value'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'9.0'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span> \r\n     <span class=\"token punctuation\">{<\/span><span class=\"token string\">'name'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'R1'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'value'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'5600.0'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span> \r\n     <span class=\"token punctuation\">{<\/span><span class=\"token string\">'name'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'R2'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'value'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'5600.0'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span> \r\n     <span class=\"token string\">'answer_text'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'4.5'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span> \u200d\u200d\u200d\u200d\u200d\u200d\u200d\u200d<\/pre>\n<\/li>\n<li>when querying an existing <em>calculated_question<\/em> through the API the answer values are found with the key &#8216;answer&#8217;\n<pre class=\"language-python line-numbers\" contenteditable=\"false\">answers<span class=\"token operator\">=<\/span><span class=\"token punctuation\">[<\/span>\r\n    <span class=\"token punctuation\">{<\/span><span class=\"token string\">'weight'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token number\">100<\/span><span class=\"token punctuation\">,<\/span> \r\n     <span class=\"token string\">'variables'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">[<\/span>\r\n      <span class=\"token punctuation\">{<\/span><span class=\"token string\">'name'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'U0'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'value'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'110.0'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span> \r\n      <span class=\"token punctuation\">{<\/span><span class=\"token string\">'name'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'R1'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'value'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'82.0'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span> \r\n      <span class=\"token punctuation\">{<\/span><span class=\"token string\">'name'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'R2'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'value'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'8200.0'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span> \r\n     <span class=\"token string\">'answer'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token number\">108.91<\/span><span class=\"token punctuation\">,<\/span> \r\n     <span class=\"token string\">'id'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token number\">3863<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span> \u200d\u200d\u200d\u200d\u200d\u200d\u200d\u200d<\/pre>\n<\/li>\n<li>when supplying an equation for the &#8216;formular&#8217; field this has to be done in a list, not a dictionary\n<pre class=\"language-python line-numbers\" contenteditable=\"false\"> <span class=\"token string\">'formulas'<\/span><span class=\"token punctuation\">:<\/span>  <span class=\"token punctuation\">[<\/span><span class=\"token string\">'a*b'<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span>\u200d\u200d<\/pre>\n<\/li>\n<li>when querying an existing <em>calculated_question<\/em> through the API the equations are found in a dictionary like this:\n<pre class=\"language-python line-numbers\" contenteditable=\"false\"> formulas<span class=\"token operator\">=<\/span><span class=\"token punctuation\">[<\/span><span class=\"token punctuation\">{<\/span><span class=\"token string\">'formula'<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">'a*b'<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span>\u200d\u200d<\/pre>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Background I just recently started with Canvas because Uppsala University has decided to use it as its upcoming LMS platform after a failed attempt with another product. Therefore I had already spent some time with Blackboard and was quite fond of the calculated questions type in quizzes. I quickly found out that Canvas offers essentially [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"enabled":false},"version":2}},"categories":[8],"tags":[],"class_list":["post-2130","post","type-post","status-publish","format-standard","hentry","category-everything_en","entry"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p48grL-ym","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=\/wp\/v2\/posts\/2130","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2130"}],"version-history":[{"count":7,"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=\/wp\/v2\/posts\/2130\/revisions"}],"predecessor-version":[{"id":2322,"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=\/wp\/v2\/posts\/2130\/revisions\/2322"}],"wp:attachment":[{"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2130"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2130"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sciencetronics.com\/greenphotons\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2130"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}