From 4918cebf5f135993cd1d2d61bc906559e627f526 Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Mon, 14 Dec 2015 11:39:15 +0100 Subject: Start documenting how to define new problems --- readme.md | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 203 insertions(+), 18 deletions(-) diff --git a/readme.md b/readme.md index ec26acd..420120d 100644 --- a/readme.md +++ b/readme.md @@ -1,20 +1,20 @@ +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 -necessary. See existing problems for examples. - - -├── common.py # id, hint_type, hint() [language-specific] -├── en.py # name, description, hint [language-specific] -├── sl.py # name, description, hint [language-specific] -└── problems -    └── - ├── common.py # id, number - ├── en.py # name, description - ├── sl.py # name, description -    └── -       ├── common.py # id, number, test(), hint_type, hint() [problem-specific] -       ├── en.py # name, plan, hint [problem-specific] -       └── sl.py # name, plan, hint [problem-specific] +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: @@ -23,6 +23,191 @@ the relevant range below: Python: 20000-29999 Robot: 30000-39999 -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. +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.") + ''' -- cgit v1.2.1