cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
barrybrown
Community Participant

Uploading answers to formula question using API

I am trying to update an existing formula question to replace the list of possible answers with a new list. The idea here is to be able to generate a list of possible variable values and answers externally. For example, I may want the variables to have co-related dependencies, such as "x should be a random value between 1 and 10, and y should be between 0.75x and 1.5x".

I've tried this curl command, to simply upload a single answer to the question. I tried to match the syntax of the JSON array returned by the corresponding GET operation.

curl -s -H 'Authorization: Bearer MY_TOKEN' \
https://xyz.instructure.com/api/v1/courses/COURSEID/quizzes/QUIZID/questions/QUESTIONID \
-X PUT \
--data-urlencode 'question[answers]=[{"weight":100,"variables":[{"name":"d","value":10}],"answer":1000,"id":1234}]'

However, I get back a "internal server error" message.

To eliminate possible sources of error, I tried changing the name of the question using the question[question_name] attribute and that worked perfectly.

Please tell me this is possible, as it would greatly simplify my quizzes.

Labels (1)
9 Replies
James
Community Champion

Do not attempt to match the syntax returned by the GET statement, it's different. It's part of why I gave up on an attempt several years ago to duplicate a quiz -- it's not as straightforward as it looks.


Instead of mimicking the GET, edit a quiz, open up the developer tools (F12) in the browser, and watch what is sent when you edit or create a new formula question.

As long as the call is to an API and not to an internal endpoint, it's definitely possible.

Although it's not exactly what you're wanting to do, I've wrote a blog post last month about how the formula questions work and how people can customize the choices to obtain things that aren't strictly random numbers: Using Calculated Values in the Text of Formula Questions . You may [or may not] find some useful information there.

Here's the API call for my testing on that:

question[question_name]: Question
question[assessment_question_id]: 155022344
question[question_type]: calculated_question
question[points_possible]: 1
question[correct_comments_html]:
question[incorrect_comments_html]:
question[neutral_comments_html]:
question[question_text]: <p>If the square of an integer is [a], then what is the integer?</p>
question[regrade_option]:
question[position]: 0
question[text_after_answers]:
question[matching_answer_incorrect_matches]:
question[formulas][]: a
question[variables][0][name]: a
question[variables][0][min]: 1
question[variables][0][max]: 15
question[variables][0][scale]: 0
question[answer_tolerance]: 0
question[formula_decimal_places]: 0
question[answers][0][variables][0][name]: a
question[answers][0][variables][0][value]: 16
question[answers][0][answer_text]: 4
question[answers][1][variables][0][name]: a
question[answers][1][variables][0][value]: 144
question[answers][1][answer_text]: 12
question[answers][2][variables][0][name]: a
question[answers][2][variables][0][value]: 9
question[answers][2][answer_text]: 3
question[answers][3][variables][0][name]: a
question[answers][3][variables][0][value]: 81
question[answers][3][answer_text]: 9
question[answers][4][variables][0][name]: a
question[answers][4][variables][0][value]: 1
question[answers][4][answer_text]: 1
question[answers][5][variables][0][name]: a
question[answers][5][variables][0][value]: 49
question[answers][5][answer_text]: 7
question[answers][6][variables][0][name]: a
question[answers][6][variables][0][value]: 16
question[answers][6][answer_text]: 4
question[answers][7][variables][0][name]: a
question[answers][7][variables][0][value]: 4
question[answers][7][answer_text]: 2
question[answers][8][variables][0][name]: a
question[answers][8][variables][0][value]: 25
question[answers][8][answer_text]: 5
question[answers][9][variables][0][name]: a
question[answers][9][variables][0][value]: 81
question[answers][9][answer_text]: 9
_method: PUT

Unfortunately, the call was made to a non-API endpoint, which means that it may not work with the API. I don't have time to dig into it right now as class is about to start.

barrybrown
Community Participant

I've made a little bit of progress on this.

So far I've been unable to upload a set of answers. But I've discovered a couple of gotchas.

First, I noticed that when I change just the assignment name using the question[question_name] attribute, I lose the formulas in the question. They just get deleted. Also, all of the calculated answers get reset to 0. I've filed a bug report for this.

Second, I tried downloading the entire question as a JSON file and then re-uploading the entire question using the PUT operation. It didn't work until I discovered that I need to wrap the entire JSON object in another object with a "question" attribute. To make working with the JSON easier, I put the data into a file.

{ question:
{ <JSON object returned by GET query> }
}

Once I did that and changed the Content-type to application/json, the system accepted my data. Here's the command I used:

curl -s -H 'Authorization: Bearer TOKEN' -X PUT \
https://xyz.instructure.com/api/v1/courses/COURSEID/quizzes/QUIZID/questions/QUESTIONID \
-H 'Content-Type: application/json' \
--data @question.json

However, the same bug as before happened: the formulas were deleted and the answers were reset to 0. At this point I'm stuck: I don't even have a way to download a quiz question and re-upload it unchanged without the system resetting the formulas and answers.

barrybrown
Community Participant

This is great!

I recall that someone (perhaps it was you) wrote up a similar article a year or two ago. I was looking for it but couldn't find it. Thanks for pointing me to it!

James
Community Champion

I'll see if I can take a look at it tonight. I think Gerald (Chip) Maguire might have done something with it. I know he wrote a lot of code to migrate question types and create them. I've got some PHP code I use to create questions, but I've never tried it with formula questions.

James
Community Champion

barrybrown

I would cancel your "it's broken" support ticket. It's more of a "I didn't understand the proper format because your documentation is lacking."

I created a formula question and obtained it with a GET statement. I wrapped the whole thing in a { "question" : ... } wrapper, removed the quiz_id and id (and assessment_id for good measure, although that may not have been necessary).

When I PUT it back, it lost the formulas.

When I looked at what Canvas sends (with a GET), it has this for the formulas property.

"formulas":[
{
"formula":"w=x-y"
},
{
"formula":"z=2*x+y"
},
{
"formula":"x+y"
}
]

When I look at the information sent to Canvas from the GUI, it has

question[formulas][]: w=x-y
question[formulas][]: z=2*x+y
question[formulas][]: x+y

Notice the difference? The POST/PUT is looking for an array of strings, not an array of objects, which is what the GET returns. With this in mind, I changed my formulas property to be this and it accepted the formulas.

"formulas":[
"w=x-y",
"z=2*x+y",
"x+y"
]

All of that is still wrapped inside the { "question" : ... }

Also, I think you need to send back all of the information for formulas and answers, not just the stuff that needs updating. There is no way for Canvas to know that you just want to update one of the formulas, so it replaces the formulas with the complete contents of the array. The same thing for answers.

barrybrown
Community Participant

Thanks for taking the time to look into this. Isn't it goofy that the output of the GET isn't compatible with the corresponding PUT?

My bug report was about changing the name of the question, which the system accepts but drops the formulas. Oddly, it keeps just about everything else: the text of the question, points, etc., even though I don't explicitly include them in the PUT data.

James
Community Champion

Goofy, yes, but not unheard of. That's what I warned about when looking at the output of the GET and trying to use it for the PUT. This happens in other places as well. The format described in the API documentation at the top of each section is normally for a GET, you have to follow the instructions for the POST / PUT part. Not every item in the GET is changeable and the format is sometimes different. Sometimes there are things that you can change but they're not listed in the documentation and you find out by either 1) trying it and being amazed when it works, 2) looking at the source code to see what it does, or 3) looking at the call that Canvas makes through the network tab on the browser tools.

Good luck on the bug issue. My experience has been that Canvas won't consider it one, rather calling it "expected behavior." Either that or they'll come back with "We're not developing any more on quizzes, all our effort is being put into Quizzes.Next."

jsavage2
Community Contributor

The issue is the ambiguity. We all fall into the trap from time to time (or at least I do...) of focusing on the Canvas-specific effects and forgetting it’s an HTTP interface that aims to be standards-compliant.

The defining feature of PUT is that it’s supposed to be idempotence. Leaving the optional data could yield different results for two identical calls and potentially duplicated items, since you don’t know the object ID for the new formula, yet.

The things that don’t change are unambiguous and/or can’t be empty: you have to explicitly say “0” points; you can’t just leave it blank. A formula, though isn’t required for every question. It can be blank. So if it’s not explicitly specified, it should be blank.

What you’re really looking for is a PATCH method for the endpoint, in preference to PUT.

I’d definitely vote for that if it came up.

Get Outlook for iOS<https://aka.ms/o0ukef>

Was a simple solution found for this?

I also need to have some control over the formula quiz. Fir example jumps of 10 or 2 in the value of a variable- or using more complex formulas in excel.

Please let me know if the is a way to prepare the table of problem, variables, solution externally and upload it to canvas formula quiz