Open protocol for accessing question engines
History
Previously (2004 to early 2005) there was an attempt (by the Serving Maths Project, Gustav Delius and others) to create something called Remote Question Protocol. This was a web service interface designed to allow question from different question engines (for example AIM and STACK) to be included in Moodle quizzes. The effort ran out of resources at a time when the code was not in a working state. Several groups have an interest in getting RQP working again, although no one is currently working on it.
During 2005, The Open University (mainly Sam Marshall) developed in-house an online assessment system called OpenMark. Its architecture was of separate question engines and a test navigator, communicating via a web service link which we call Opaque (Open Protocol for Accessing QUestion Engines).
Then in May 2006, I (Tim Hunt) took over maintenance of the quiz module from Gustav. At which point I learned about RQP. I was immediately struck by how similar Opaque and RQP were conceptually, although they differed in details.
In autumn 2006, the OU wanted to be able to include OM question in Moodle quizzes. Our choices were either
- change OM to use RQP; or
- change (add to) Moodle to support Opaque.
We decided to do 2.
OpenMark was released under an open source license (hosted at dev.java.net) in Spring 2007; and STACK 2.0 (http://stack.bham.ac.uk/) was released just before Christmas 2007, so there are now at least two freely available Opaque compatible systems.
As of 2011 there is a third project using Opaque: ounit - Unit testing and grading environment for e-learning systems http://code.google.com/p/ounit/.
Rationale
Why are people interested in this sort of protocol? The answer is separation of concerns. The things an online assessment system has to do are:
- Identify students, tests, which students can do which tests at what times, and which questions make up each test.
- Record the scores and other information that results from students attempting test, and provide reporting facilities for this information.
- Display questions to students and process the responses the students make to generate scores, feedback, and so on.
- Allow teachers to create and configure the questions.
Parts 1 and 2 are boring book-keeping, and Moodle already knows how to do it.
For the developers interested in Opaque/RQP, the really interesting part of an assessment system is 3. We want to pose really innovative type of questions to students, and use sophisticated algorithms to analyse the student's response and give targeted feedback that helps the students learn from their mistakes.
This may require specialist tools. For example, STACK and AIM both use Computer algebra systems to set and mark interesting mathematical questions. OpenMark lets question authors write answer marking and feedback generation algorithms in Java code. It is simply not feasible to rewrite these question engines in PHP and turn them into Moodle question types. Hence the search for another way to get these questions into Moodle.
Of course, each of STACK, AIM, OM, etc. could programme their own solutions to the administration parts 1 and 2. However, this is a needless duplication of effort. It does not allow questions from different question engines to be combined in the same test. People who are interested in writing advanced question engines are normally not very interested in writing student administration systems.
Finally, there is point 4, question authoring. Authoring advanced question types is a complicated task that will probably need a specialised interface. For example STACK provides its own editing interface. Om questions are authored in a Java IDE, then complied to a JAR file and uploaded to the server. This is all beyond the scope of Opaque. RQP started trying to address authoring at about the point that it was abandoned.
Our approach is that Opaque should be optimised for part 3 above - presenting questions to students and processing their responses. This is the point where interoperability is required. Interoperability is hard, so let us keep the scope of the protocol as small as possible. Interoperability of student data and results is a separate problem. Authoring of advanced question types is specialised to the particular question engine, and interoperability is not possible.
All this is in the context of an online assessment system. Therefore, we are interested in questions that get presented to students in their web browser as HTML, with accompanying CSS, JavaScript, Images and other media files. Student responses get sent back as form submissions, that is HTTP POST requests. Conceivably the trendy XMLHttpRequest could be used instead, but it is not really necessary. Opaque only works with form submissions, which is simple and sufficient.
Overview of Opaque
Opaque is a web service protocol based on SOAP.
The parts of the system
Opaque is only used at the point when questions are being presented to students, and possibly later when a student's attempt is being reviewed by a teacher.
It allows the test management system (TMS) to delegate the rendering of questions, the scoring of responses and the generation of feedback to a remote question engine.
There may be a separate question bank where question definitions are stored, or question definitions may be stored within the question engine (e.g. Stack) or the test management system may also take the role of question bank (e.g. OM).
The TMS takes full responsibility for authenticating students and controlling which questions they see. It asks an appropriate question engine to render each question. The question just had to process the questions and responses it is told to process.
Although Opaque is designed to allow interoperability between arbitrary different types of question engines and TMSs, the expectation is that any online assessment system will only use particular instances of each component.
Identifying questions
When the TMS asks the question engine to render a particular qustion, it needs to identify that question. This is done using three pieces of information:
- questionbank base URL
- If the question engine is designed to fetch question definitions from a remote question bank, then this is base URL for fetching questions from. For example http://example.com/questionbank.
- question id
- This is a string conatining only the characters [_a-z0-9.], and also matching the syntactic rules for Java package names. That is, it is one or more 'words' separated by '.'s, each word matching the regexp [_a-z][_a-z0-9]*. This identifies the question withing the question bank. For example calculus.diff01a or _12345.
- question version
- This is a string of the fomat major.minor, where major and minor are both decimal integers with no leading zero. Version with the same major version number should all behave roughly the same way. minor version changes are things like bug fixes. Therefore, a test definition would probably only specify which major version of the question to use, and the most recent minor version would be picked by the TMS. The full version number is needed for resuming interrupted sessions and reviewing student responsees. See reproducability guarantee below. For example 1.1, 1.2.
If the question engine fetches question definitions from a remote question bank, then it will fetch a particular question by requesting the URL questionbankBaseURL/questionId.questionVersion (e.g. http://example.com/questionbank/calculus.diff01a.1.1). This returns a single block of data (e.g. OM questions are jar files conaining XML, media files and java code). The format of this data is up to the question engine. It is the responsibility of the TMS to only ask question engines to render questions that they can understand. In the question engine supports different question formats, then it may use the mime type of the response from the question bank to decide how to process this question.
If the question engine stores its own questions, then it can ignore the questionbank base URL, and use the question id and question version to uniquely identify the requested question within its datastore.
Data passed when a question session is started
When a new question session is stared, certain information is passed from the test navigator to the question engine:
- randomseed
- Any randomisation done be the question engine should be seeded with this number. See below for details.
- userid
- An Opaque question engine should not really be concerned with the identity of the user it is processing questions for. However, this information is provided for information, for example for use in logging messages.
- language
- Information about the current user's preferred language. This allows the question engine to implement internationalization functionality.
- passKey
- This may be used as a basic security measure. The value passed is md5($secret . $userid), where $secret is a shared secret string that needs to be known to the question engine and TMS.
- preferredbehaviour
- Some TMSs (for example Moodle) allow the test author to express a preference about how questions in the quiz should work. This string is used to pass this preference on to the question engine. The question engine should decide what use, if any, it should make of this values. We do not define the permissible values here.
The question engine may ignore any of these pieces of data if it does not have a use for it. The TMS is only required to send randomseed. If other elements are missing, then the question engine handle the situation appropriately.
These values are passed in the initialParams parameter of the start() method.
What can be included in rendered questions
When a question engine is asked to render a question, it returns a response which includes the following parts:
- String XHTML
- Well-formed XML which, when inserted into the body or div of an HTML page, will result in valid HTML.
- String CSS
- any CSS needed to style the content. The TMS will cache this, and include a link to it in the head of the HTML page.
- Resource[] resources
- an array of resources which the TMS must cache and make available.
A Resource has the following fields:
- byte[] content
- String encoding
- String filename
- String mimeType
The content is what is served when the resource is requested. It is served with the specified encoding and mimeType. The filename is used in the URL the resource will be made available at. If the question requires any javascript, this should be returned as a resource.
Both the XHTML and CSS may contain placeholders of the form %%identifier%%. These are substituted by the TMS. Recognised placeholders include:
- %%RESOURCE%%
- Path [relative or absolute] at which resources will become available. This should not include the terminating /. Example: If a resource has the name myfile.png, then <img src="%%RESOURCES%%/myfile.png"/> should work to include that image.
- %%IDPREFIX%%
- This should be prepended to any id or class attribute in the XHTML and references to them in the CSS or Javascript. This allows multiple questions to be diplayed on one page, without conflict. [Note, this is not currently implemented since OM only ever displays one question per page. There is a problem here with javascript, since placeholders are not substituted in resources.
- %%%%
- Replaced with %%.
For perfomance reasons, whenever the TMS asks the question engine to render a question, it may send a list of resources that it already has cached for that question. The question engine may choose not to resend any of these resources.
How the student response is represented
The TMS receives the student's response as a HTML form submission, that is, as a HTTP POST request.
Because there might be more than one question per page, the post response to the TMS may contain many post variables. The TMS separates out the ones with the appropriate %%IDPREFIX%%, and forwards just those key->value pairs to the question engine (with idprefix removed from the key name), when it asks it to process that response.
What sort of results can be returned
Any request to the question engine to process some results will return HTML and resources etc. to be displayed to the student. In addition, it may (if the student has finished answering the question) include some stuctured results data to be stored in the results database. The Results structure includes:
- String actionSummary
- A log of the actions the student took in getting to their final answer. Intended for reports seen by test administrators.
- String answerLine
- A one-line summary of the student's final answer. For general reporting.
- int attempts
- Number of attempts it took the student to get their final answer right, if their final answer was right. -1 if the final answer was wrong; -2 if the final answer was partially correct; 0 of the student gave up on the question.
- String questionLine
- A one line summary of the question that was asked. For reporting. Most useful when a question has different random variants.
- Map<String, int> scores
- The scores for this question. Normally the score will be a single integer, and the corresponding key will by the empty string (""). However, in some situations (e.g. psychological tests) the question may want to report scores against different 'axes'.
To repeat, these are stored in a database by the TMS, and used for reporting and computing overall test scores without needing to contact the question engine. If it is necessary to reconstruct exactly what the student saw on a particular question, and exactly what they entered, then this can be done using the question engine. See the next section.
Question sessions and the reproducibility guarantee
Opaque is a stateful protocol, but in a weak sense.
In the protocol, a student's attempt on a question will require a call to the start() method, then one or more calls to the process() method. These calls may be separated by several minutes, but it could be any length of time.
Normally, the start() call will return a question session id, which is then passed to each call of the process() method. This allows the question engine to maintain complex state if it wishes. However, we want the system to be robust. For example a student should be able to stop working on a question one day and expected to come back the next day and resume where they left off? We also wish to be able to recover if the question engine crashes?
Therefore, we require the question engine to guarantee a sequence of calls to start() and process() with exactly the same arguments will always return the same results. This, we can always re-create a question session simply by playing back the sequence of web service calls. The TMS just needs to store the information needed to recreate the sequence of calls. That is, the question identity, the random seed, and the values submitted by students in each POST request. This ensures that if a question session is interrupted or lost, it can just be recreated.
Question sessions are designed to improve performance since the question definition only has to be read and processed once per session. It should also make implementing question engines easier, since state does not have to be saved in persistent storage, and most languages designed for writing server-side applications have session handling mechanisms.
If the TMS knows that a student has suspended their attempt on a question (for example by navigating to a different question) it should call the stop() method to inform the question engine that that question session is no longer needed. This helps the question engine reduce the number of active question sessions.
The question engine may also discard any question session that has been inactive for a period of time.
What happens in a typical attempt on a question
This diagram indicates a typical sequence of calls that happen when a student attempts a question.
When a test attempt is finished early
(This feature was added in Moodle 2.1. It is backwards compatible.)
When Opaque was first conceived, it was assumed that when working through a quiz, the student. would attempt and complete each question before finishing the test.
However, this may not be the case. (For example, consider Moodle's deferred feedback mode.) Therefore, at the point where the student finishes their test attempt, there may be some questions that have not yet been completed.
In this situation, the TMS may make an additional process() call to the question engine, passing the conventional response data ('-finish' => 1).
The question engine may choose to detect this conventional process call, and handle it appropriately, for example by grading whatever response the student has entered and awarding whatever credit it is worth, and then sending back results, including questionEnd = true, in the normal way.
Display options
(This is a proposed feature to be added in the Moodle 2.1 version of this protocol. It is backwards compatible.)
In addition to the 'hard' Data passed when a question session is started, some additional options may also be passed from the TMS to the question engine in the initialParams.
All of these params have names that start display_. They may be used to alter the HTML (and CSS) returned by the start() and process() methods, but they may not change anything that would break the Question sessions and the reproducibility guarantee. That is, it must always be possible to start a new question session with different display options (but all other initialParams the same), and then play back the same sequence of process calls, and everything must work the same, apart from the differences in the HTML output.
If any of these options are omitted the default indicated should be assumed. (The question engine is not obliged to make any use of these params anyway.)
- display_readonly
- Default 0. If 1, then then question should be displayed with all form controls disabled or read-only. This is typically used when a third-part looks at a question belonging to someone else, so they should be able to see the current state, but should not be able to modify it.
- display_marks
- 0 = do not give any information about numerical marks. 1 = give information about the maximum mark possible, perhaps for each part of the question if applicable, but do not give any information about the students mark. 2 = Give information about the students mark (and the maximum possible) if appropriate. Default 2.
- display_markdp
- integer, suggested number of decimal places to display when displaying marks. No default defined by Opaque.
- display_correctness
- 0 = hide, 1 = show (the default). Whether to reveal whether (parts of) the student's response was right or wrong (as opposed to the numerical mark earned by the response). This might be use, for example, to control whether red/green colour highlighting is displayed.
- display_feedback
- 0 = hide, 1 = show (the default). Whether to display feedback that depends on the response the student gave.
- display_generalfeedback
- 0 = hide, 1 = show (the default). Whether to display an overall summary of the question, for example a worked solution. Typically this is displayed to all students after they have finished the question.
Note that all these display options should only be used to adjust what is normally displayed given the current state of the question. For example, there will not normally be any feedback to display until after the student as submitted an answer. Therefore, before the student has submitted anything, this option is irrelevant.
The Opaque data structures
These are documented in IDL (which I don't really understand, but I am familiar with it from other docs (e.g. http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/) and it is nicely language independent.)
StartReturn
interface StartReturn { readonly attribute string questionSession; readonly attribute string XHTML; readonly attribute string CSS; readonly attribute string progressInfo; readonly attribute Resource[] resources; }
Resource
interface Resource { readonly attribute BLOB content; readonly attribute string encoding; readonly attribute string filename; readonly attribute string mimeType; }
ProcessReturn
interface ProcessReturn { readonly attribute string XHTML; readonly attribute string CSS; readonly attribute string progressInfo; readonly attribute boolean questionEnd; readonly attribute Resource[] resources; readonly attribute Results results; }
Results
interface Results { readonly attribute string questionLine; readonly attribute string answerLine; readonly attribute string actionSummary; readonly attribute integer attempts; readonly attribute Map<string, float> scores; readonly attribute Map<string, string> customResults; }
The Opaque API methods
String getEngineInfo()
This returns some XML data as a string (byte array). The format of the response is
<engineinfo> <name>[question engine]</name> ''<!-- Required -->'' <usedmemory>['123 bytes' or '45 KB' or '67 MB']</usedmemory> ''<!-- Optional-->'' <activesessions>[Number of question sessions active]</activesessions> ''<!-- Optional-->'' </engineinfo>
This method is only ever used for system monitoring - that is to ask a question engine "are you still there?". It is not essential to the functioning of the system.
The tags inside the outer <engineinfo> tag may be in any order. Other tags may also be included.
String getQuestionMetadata(String questionID, String questionVersion, String questionBaseURL)
This returns a snipped of XML like this:
<questionmetadata> <scoring> <marks>3</marks> </scoring> <plainmode>yes</plainmode> </questionmetadata>
The scoring section is basically giving the maximum score for the question, but because a question can report scores against different 'axes', this may be more complicated.
plain mode is a feature of the OpenMark system designed to enhance accessibility. Since OpenMark use a lot of JavaScript and CSS which may not be accessible to some people, then every question should provide an alternative rendering using just plain HTML and standard from controls. When a student chooses to take a quiz in plain mode, they see the plain mode version of each question. However, not all questions provide a plain-mode version, in which case they should return ,plainmode>no</plainmode>.
StartReturn start(String questionID, String questionVersion, String questionBaseURL, Map<String, String> initialParams)
This is one of the key methods. It tells the question engine to start a new attempt at a question, see the sections above about Identifying questions and Data passed when a question session is started for an explanation of the arguments.
The return value is a StartReturn object, as documented above, which includes the rendered form of the question in its initial state, and the question session id.
ProcessReturn process(String questionSession, Map<String, String> response)
This is the other key method used when attempting a question. It continues an attempt started by start().
The questionSession is the session identifier returned by the start() call for the question attempt we are continuing.
The response array contains the data that was sent back from the POST request, with %%IDPREFIX%% stripped off of the variable names.
stop(String questionSession)
This method is not essential, but TMSs should implement it.
It tells the question engine that this question session is unlikely to be continued in the near future, and so it does not need to be kept.
If, later, this question attempt is continued, then the TMS will re-create the state by playing back the sequence of calls to start() and process() which will use a new question session id.
Opaque WSDL
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="http://om.open.ac.uk/" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://om.open.ac.uk/" xmlns:intf="http://om.open.ac.uk/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:types> <schema targetNamespace="http://om.open.ac.uk/" xmlns="http://www.w3.org/2001/XMLSchema"> <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/> <complexType name="ArrayOf_soapenc_string"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="soapenc:string[]"/> </restriction> </complexContent> </complexType> <complexType name="Resource"> <sequence> <element name="content" nillable="true" type="soapenc:base64Binary"/> <element name="encoding" nillable="true" type="soapenc:string"/> <element name="filename" nillable="true" type="soapenc:string"/> <element name="mimeType" nillable="true" type="soapenc:string"/> </sequence> </complexType> <complexType name="ArrayOfResource"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="impl:Resource[]"/> </restriction> </complexContent> </complexType> <complexType name="StartReturn"> <sequence> <element name="CSS" nillable="true" type="soapenc:string"/> <element name="XHTML" nillable="true" type="soapenc:string"/> <element name="progressInfo" nillable="true" type="soapenc:string"/> <element name="questionSession" nillable="true" type="soapenc:string"/> <element name="resources" nillable="true" type="impl:ArrayOfResource"/> </sequence> </complexType> <complexType name="OmException"> <sequence/> </complexType> <complexType name="CustomResult"> <sequence> <element name="name" nillable="true" type="soapenc:string"/> <element name="value" nillable="true" type="soapenc:string"/> </sequence> </complexType> <complexType name="ArrayOfCustomResult"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="impl:CustomResult[]"/> </restriction> </complexContent> </complexType> <complexType name="Score"> <sequence> <element name="axis" nillable="true" type="soapenc:string"/> <element name="marks" type="xsd:int"/> </sequence> </complexType> <complexType name="ArrayOfScore"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="impl:Score[]"/> </restriction> </complexContent> </complexType> <complexType name="Results"> <sequence> <element name="actionSummary" nillable="true" type="soapenc:string"/> <element name="answerLine" nillable="true" type="soapenc:string"/> <element name="attempts" type="xsd:int"/> <element name="customResults" nillable="true" type="impl:ArrayOfCustomResult"/> <element name="questionLine" nillable="true" type="soapenc:string"/> <element name="scores" nillable="true" type="impl:ArrayOfScore"/> </sequence> </complexType> <complexType name="ProcessReturn"> <sequence> <element name="CSS" nillable="true" type="soapenc:string"/> <element name="XHTML" nillable="true" type="soapenc:string"/> <element name="progressInfo" nillable="true" type="soapenc:string"/> <element name="questionEnd" type="xsd:boolean"/> <element name="resources" nillable="true" type="impl:ArrayOfResource"/> <element name="results" nillable="true" type="impl:Results"/> </sequence> </complexType> </schema> </wsdl:types> <wsdl:message name="getEngineInfoResponse"> <wsdl:part name="getEngineInfoReturn" type="soapenc:string"/> </wsdl:message> <wsdl:message name="processRequest"> <wsdl:part name="questionSession" type="soapenc:string"/> <wsdl:part name="names" type="impl:ArrayOf_soapenc_string"/> <wsdl:part name="values" type="impl:ArrayOf_soapenc_string"/> </wsdl:message> <wsdl:message name="getEngineInfoRequest"> </wsdl:message> <wsdl:message name="getQuestionMetadataResponse"> <wsdl:part name="getQuestionMetadataReturn" type="soapenc:string"/> </wsdl:message> <wsdl:message name="processResponse"> <wsdl:part name="processReturn" type="impl:ProcessReturn"/> </wsdl:message> <wsdl:message name="stopResponse"> </wsdl:message> <wsdl:message name="OmException"> <wsdl:part name="fault" type="impl:OmException"/> </wsdl:message> <wsdl:message name="startResponse"> <wsdl:part name="startReturn" type="impl:StartReturn"/> </wsdl:message> <wsdl:message name="stopRequest"> <wsdl:part name="questionSession" type="soapenc:string"/> </wsdl:message> <wsdl:message name="getQuestionMetadataRequest"> <wsdl:part name="questionID" type="soapenc:string"/> <wsdl:part name="questionVersion" type="soapenc:string"/> <wsdl:part name="questionBaseURL" type="soapenc:string"/> </wsdl:message> <wsdl:message name="startRequest"> <wsdl:part name="questionID" type="soapenc:string"/> <wsdl:part name="questionVersion" type="soapenc:string"/> <wsdl:part name="questionBaseURL" type="soapenc:string"/> <wsdl:part name="initialParamNames" type="impl:ArrayOf_soapenc_string"/> <wsdl:part name="initialParamValues" type="impl:ArrayOf_soapenc_string"/> <wsdl:part name="cachedResources" type="impl:ArrayOf_soapenc_string"/> </wsdl:message> <wsdl:portType name="OmService"> <wsdl:operation name="start" parameterOrder="questionID questionVersion questionBaseURL initialParamNames initialParamValues cachedResources"> <wsdl:input message="impl:startRequest" name="startRequest"/> <wsdl:output message="impl:startResponse" name="startResponse"/> <wsdl:fault message="impl:OmException" name="OmException"/> </wsdl:operation> <wsdl:operation name="stop" parameterOrder="questionSession"> <wsdl:input message="impl:stopRequest" name="stopRequest"/> <wsdl:output message="impl:stopResponse" name="stopResponse"/> <wsdl:fault message="impl:OmException" name="OmException"/> </wsdl:operation> <wsdl:operation name="process" parameterOrder="questionSession names values"> <wsdl:input message="impl:processRequest" name="processRequest"/> <wsdl:output message="impl:processResponse" name="processResponse"/> <wsdl:fault message="impl:OmException" name="OmException"/> </wsdl:operation> <wsdl:operation name="getEngineInfo"> <wsdl:input message="impl:getEngineInfoRequest" name="getEngineInfoRequest"/> <wsdl:output message="impl:getEngineInfoResponse" name="getEngineInfoResponse"/> </wsdl:operation> <wsdl:operation name="getQuestionMetadata" parameterOrder="questionID questionVersion questionBaseURL"> <wsdl:input message="impl:getQuestionMetadataRequest" name="getQuestionMetadataRequest"/> <wsdl:output message="impl:getQuestionMetadataResponse" name="getQuestionMetadataResponse"/> <wsdl:fault message="impl:OmException" name="OmException"/> </wsdl:operation> </wsdl:portType> <wsdl:binding name="OmSoapBinding" type="impl:OmService"> <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="start"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="startRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:input> <wsdl:output name="startResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:output> <wsdl:fault name="OmException"> <wsdlsoap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" name="OmException" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="stop"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="stopRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:input> <wsdl:output name="stopResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:output> <wsdl:fault name="OmException"> <wsdlsoap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" name="OmException" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="process"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="processRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:input> <wsdl:output name="processResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:output> <wsdl:fault name="OmException"> <wsdlsoap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" name="OmException" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getEngineInfo"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="getEngineInfoRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:input> <wsdl:output name="getEngineInfoResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="getQuestionMetadata"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="getQuestionMetadataRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:input> <wsdl:output name="getQuestionMetadataResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:output> <wsdl:fault name="OmException"> <wsdlsoap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" name="OmException" namespace="http://om.open.ac.uk/" use="encoded"/> </wsdl:fault> </wsdl:operation> </wsdl:binding> <wsdl:service name="OmServiceService"> <wsdl:port binding="impl:OmSoapBinding" name="Om"> <wsdlsoap:address location="http://kestrel.open.ac.uk/om-qe/services/Om"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
See also
- https://docs.moodle.org/20/en/Opaque_question_type for instructions on installing and using the Moodle Opaque question type.
- https://github.com/timhunt/moodle-qtype_opaque and https://github.com/timhunt/moodle-qbehaviour_opaque for Moodle's implementation of the TMS side of the Opaque protocol.