1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
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.
<language>
├── common.py # id, hint_type, hint() [language-specific]
├── en.py # name, description, hint [language-specific]
└── problems
└── <group>
├── common.py # id, number
├── en.py # name, description
└── <problem>
├── 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': '<p>Message for hint "normal_1".</p>',
'normal_2': [
'<p>First part.</p>',
{'message': '<p>Second part.</p>', 'linkText': 'Details…'},
'<p>Final part.</p>'
],
'popup': '<p>There is an error here.</p>'
}
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': '<p>Your code passed [%=passed%] / [%=total%] tests.</p>'
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 = <course 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': '<p>No hint available, sorry!</p>',
'test_results': '<p>Your code passed [%=passed%] / [%=total%] tests.</p>',
}
Group files
-----------
### python/problems/introduction/common.py
Minimum content:
id = <group 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 = <problem 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)
* ⊂
* ⊆
* ⊃
* ⊇
|