Skip to content

Creating Rules

U8N WXD edited this page Aug 17, 2021 · 5 revisions

Table of contents

This page is an introduction to creating new Oppia rules. An Oppia rule takes a reader's response, normalizes it, and evaluates it against a predicate. A card's interaction will correspond to a set of rules, and the first rule that is triggered by the response determines what Oppia will display to the reader next.

How rules work

Rule templates

Rules are listed in extensions/interactions/rule_templates.json, where they are grouped by interaction. For example, here are the rules for the MathEquationInput interaction:

{
  ...
  "MathEquationInput": {
    "MatchesExactlyWith": {
      "description": "matches exactly with {{x|MathEquation}} {{y|PositionOfTerms}}"
    },
    "IsEquivalentTo": {
      "description": "is equivalent to {{x|MathEquation}}"
    },
    "ContainsSomeOf": {
      "description": "contains at least one of the terms present in {{x|MathEquation}} {{y|PositionOfTerms}}"
    },
    "OmitsSomeOf": {
      "description": "omits at least one of the terms present in {{x|MathEquation}} {{y|PositionOfTerms}}"
    },
    "MatchesWithGeneralForm": {
      "description": "matches the form of {{x|MathEquation}} with placeholders {{y|SetOfAlgebraicIdentifier}}"
    }
  },
  ...
}

Rule descriptions

Rule descriptions can specify rule inputs with curly braces. For an input p of type Type, the description would include {{p|Type}}. The types are defined as classes in extensions/objects/models/objects.py, and each class provides a normalize method that transforms the input into a canonical form.

Evaluation functions

Each rule is implemented in the interaction's rules service as a method (evaluation function). In extensions/interactions/MathEquationInput/directives/math-equation-input-rules.service.ts, the MathEquationInputRulesService class defines a MatchesExactlyWith method, an IsEquivalentTo method, and so on. Each method takes exactly two arguments: answer, which is the correct answer, and inputs, an object with the rule inputs as attributes. For example the inputs argument to the MatchesWithGeneralForm method of MathEquationInputRulesService would have two attributes: x and y. The types for these inputs are defined in extensions/interactions/rule-input-defs.ts and extensions/interactions/answer-defs.ts.

The return value from a rule is always a boolean that describes whether answer and inputs obey the rule. For example, the IsEquivalentTo rule returns whether the user's inputs are equivalent to the creator-configured answer.

For example, suppose we have a rule IsBetweenInclusive with the description is at least {{x|Real}} and at most {{y|Real}}. Then our evaluation function could be:

export class MyInteractionRulesService {
  IsBetweenInclusive(
      answer: MyInteractionAnswer,
      inputs: MyInteractionInputs): boolean {
    return inputs.x <= answer <= inputs.y
  }
}

Note that the evaluation function must not use any external information, for example the reader's current card. The inputs and answer must be sufficient to determine whether the rule is satisfied.

Adding a New Rule

Suppose you have just created a new interaction, say MyInteraction, and now you want to define the rules associated with it. To do so, follow these steps:

  1. In extensions/interactions/rule_templates.json, add a dictionary under the key MyInteraction. You can skip this step if you are adding a rule to an existing interaction.
  2. For each rule you want to create add an appropriate key that names the rule. The corresponding value should be a dictionary with a single key, description, whose value is a string describing the rule. The string should contain rule inputs as substrings of the form {{inputName|InputType}}. See the descriptions section above for details.
  3. Add a corresponding evaluation function to the interaction's rules service, as described in the evaluation functions section above.
  4. Add tests for the evaluation function by creating a *.spec.ts file for the rules service. Create a separate test for each rule and check the various corner cases to make sure that rules are defined correctly. You must do this for any new rule that you create so that you can ensure that the rule works properly and returns the correct results on a variety of inputs.

If you would like to contribute a rule to Oppia, please feel free to do so, but we recommend that you talk to us first! This helps ensure that work is not duplicated, and that the added rules are useful for creators and learners. We also welcome suggestions for new rules.

Core documentation


Developing Oppia


Developer Reference

Clone this wiki locally