Directory structure =================== The file hierarchy for problem data is below. Comments indicate the global variables and functions that can be defined in each file. Only the ID is required. ├── common.py # id, hint_type, hint() [language-specific] ├── en.py # name, description, hint [language-specific] └── problems    └── ├── common.py # id, number ├── en.py # name, description    └──       ├── common.py # id, number, test(), hint_type, hint() [problem-specific]       └── en.py # name, plan, hint [problem-specific] To add a problem, create the necessary files. Pick an unused problem ID from the relevant range below: Prolog: 10000-19999 Python: 20000-29999 Robot: 30000-39999 Problem group IDs should be chosen from the range: Prolog: 1000-1999 Python: 2000-2999 Robot: 3000-3999 Running `scripts/build\_web\_resources.py` in the codeq-server repo will generate JSON files for all problems, and insert new problems, groups and languages in the database. Hints ===== There are two distinct hint types: normal hints and popup hints. Normal hint appears in the hints section of the application, and contains a single or multipart message. A popup hint highlights a range of user’s code and displays a message when the user clicks on or hovers over the highlight. The type of each hint is defined in the appropriate `common.py` file, like this: # common.py from server.hints import Hint, HintPopup hint_type = { 'normal_1': Hint('normal_1'), 'normal_2': Hint('normal_2'), 'popup': Hint('popup') } Message for each hint is defined in the `hint` dictionary in the corresponding language file (e.g. `en.py`). For the example above: # en.py hint = { 'normal_1': '

Message for hint "normal_1".

', 'normal_2': [ '

First part.

', {'message': '

Second part.

', 'linkText': 'Details…'}, '

Final part.

' ], 'popup': '

There is an error here.

' } For a popup hint, a single string is required and will be displayed when the user hovers over (or clicks on) the highlighted range. For a normal hint, a single string can be specified which will be displayed in the hints section (e.g. `normal\_1`). Multipart hints --------------- Instead of specifying a single string for a normal hint, a list of strings can be given; initially, only the first string will be displayed, with a “More…” link to show the next string and so on. List elements can also be dictionaries with `message` and `linkText` attributes - in this case, “More…” will be replaced by the value of `linkText`. In the above example, on receiving the `normal\_2` hint, application will first show First part. More… After clicking “More…”, the message is extended: First part. Second part. Details… After clicking “Details…”, the message is extended again: First part. Second part. Final part. Template arguments ------------------ A template in the form `[%=arg%]` may appear in any hint string. It will be replaced with an actual value returned by the `hint` or `test` functions (described in the next section). This is used for example when displaying test results, with the hint defined as 'test_results': '

Your code passed [%=passed%] / [%=total%] tests.

' The actual values for the `passed` and `total` arguments are returned by the `test` function. Problem functions ================= For each problem, a `test(program)` and `hint(program)` functions must be specified in the corresponding `common.py` file. They are called with the user’s current program when the Test or Hint button is pressed. The `hint(program)` function should return a list of hint objects. Each hint object is a dictionary containing the hint ID, any arguments for the hint message, and (for popup hints) `start` and `end` attributes indicating the range of code to highlight. The `test(program)` function should return a pair of values `(correct, hints)`, where `correct` is a Boolean value indicating whether the program is correct, and `hints` a list of hint objects as described above. For example, given an incorrect program the `test` function might return (False, [{'id': 'test_results', 'args': {'passed': 2, 'total': 5}}]) saying that the program passed two out of five tests, while the `hint` function might return [{'id': 'normal_1'}, {'id': 'popup', 'start': 14, 'end': 20}] displaying the message for `normal\_1` hint in the hints window and highlighting the code between characters 14 and 20. Example ======= In this section we add a new problem “Ballistics” in the “Introduction” section for the “Python” course. First, create the required directory structure (note the required “problems/” path component): mkdir -p "python/problems/introduction/ballistics" Course, group and problem definitions are given in “common.py” files in the respective directories. The “en.py” file in the same directory stores the English versions of user messages. Additional language files can be created to support more languages. Course files ------------ ### python/common.py Minimum content: from server.hints import Hint id = hint_type = { 'no_hint': Hint('no_hint'), 'test_results': Hint('test_results'), } The id attribute stores ID of the corresponding database row and must be different from existing courses. ### python/en.py name = 'Python' description = 'Introductory Python course' hint = { 'no_hint': '

No hint available, sorry!

', 'test_results': '

Your code passed [%=passed%] / [%=total%] tests.

', } Group files ----------- ### python/problems/introduction/common.py Minimum content: id = number = 1 The number attributes are used to determine the order of groups in a course (in this case, “Introduction” is the first problem group in the “Python” course). ### python/problems/introduction/en.py name = 'Introduction' description = 'Expressions, variables, functions, first program.' Problem files ------------- id = number = 3 visible = True solution = '''\ from math import * g = 9.8 kot = float(input("Vnesi kot (v stopinjah): ")) v = float(input("Vnesi hitrost (v m/s): ")) kot_rad = kot * 2 * pi / 360 razdalja = v ** 2 * sin(2 * kot_rad) / g print("Kroglo bo odneslo", razdalja, "metrov.") ''' Style ===== Please observe the following stylistic rules when adding or translating problems. * Wrap lines at 80 columns (not a hard rule). Special characters ------------------ Use appropriate Unicode characters wherever possible. If your setup makes it difficult to enter these characters, copy them from this file. * "foo" → “foo” (english) or „foo“ (slovene) * ' → ’ (typographic apostrophe) * ... → … (ellipsis) * 2x3 → 2×3 (multiplication sign) * <= → ≤ * >= → ≥ * -> → → * => → ⇒ * -- → – (en dash) * :) → ☺ * ;) → 😉 Some other useful characters: * ∪ (set union) * ∩ (set intersection) * ∖ (set difference) * ⊂ * ⊆ * ⊃ * ⊇